From c2fd21f2cc5e3681ecab0ccc089ef4d2ef5bd7a6 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 30 Jun 2021 02:07:12 +0530 Subject: [PATCH 01/78] fix security issue --- .../collection/DefaultNitriteCollection.java | 111 +++++++++++++++--- 1 file changed, 95 insertions(+), 16 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 9ffa2944f..f5e2fbb44 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -75,12 +75,12 @@ class DefaultNitriteCollection implements NitriteCollection { } public WriteResult insert(Document[] documents) { - checkOpened(); notNull(documents, "a null document cannot be inserted"); containsNull(documents, "a null document cannot be inserted"); try { writeLock.lock(); + checkOpened(); return collectionOperations.insert(documents); } finally { writeLock.unlock(); @@ -88,7 +88,6 @@ public WriteResult insert(Document[] documents) { } public WriteResult update(Document document, boolean insertIfAbsent) { - checkOpened(); notNull(document, "a null document cannot be used for update"); if (insertIfAbsent) { @@ -103,12 +102,12 @@ public WriteResult update(Document document, boolean insertIfAbsent) { } public WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) { - checkOpened(); notNull(update, "a null document cannot be used for update"); notNull(updateOptions, "updateOptions cannot be null"); try { writeLock.lock(); + checkOpened(); return collectionOperations.update(filter, update, updateOptions); } finally { writeLock.unlock(); @@ -116,12 +115,12 @@ public WriteResult update(Filter filter, Document update, UpdateOptions updateOp } public WriteResult remove(Document document) { - checkOpened(); notNull(document, "a null document cannot be removed"); if (document.hasId()) { try { writeLock.lock(); + checkOpened(); return collectionOperations.remove(document); } finally { writeLock.unlock(); @@ -132,13 +131,13 @@ public WriteResult remove(Document document) { } public WriteResult remove(Filter filter, boolean justOne) { - checkOpened(); if ((filter == null || filter == Filter.ALL) && justOne) { throw new InvalidOperationException("remove all cannot be combined with just once"); } try { writeLock.lock(); + checkOpened(); return collectionOperations.remove(filter, justOne); } finally { writeLock.unlock(); @@ -146,27 +145,40 @@ public WriteResult remove(Filter filter, boolean justOne) { } public void clear() { - checkOpened(); try { writeLock.lock(); + checkOpened(); nitriteMap.clear(); } finally { writeLock.unlock(); } } +<<<<<<< Updated upstream public DocumentCursor find() { checkOpened(); try { readLock.lock(); return collectionOperations.find(); +======= + public DocumentCursor find(Filter filter, FindOptions findOptions) { + try { + readLock.lock(); + checkOpened(); + return collectionOperations.find(filter, findOptions); +>>>>>>> Stashed changes } finally { readLock.unlock(); } } +<<<<<<< Updated upstream public DocumentCursor find(Filter filter) { checkOpened(); +======= + public void createIndex(IndexOptions indexOptions, String... fields) { + notNull(fields, "fields cannot be null"); +>>>>>>> Stashed changes try { readLock.lock(); @@ -183,6 +195,7 @@ public void createIndex(String field, IndexOptions indexOptions) { // by default async is false while creating index try { writeLock.lock(); + checkOpened(); if (indexOptions == null) { collectionOperations.createIndex(field, IndexType.Unique, false); } else { @@ -194,14 +207,24 @@ public void createIndex(String field, IndexOptions indexOptions) { } } +<<<<<<< Updated upstream public void rebuildIndex(String field, boolean isAsync) { checkOpened(); notNull(field, "field cannot be null"); +======= + public void rebuildIndex(String... fields) { + notNull(fields, "fields cannot be null"); +>>>>>>> Stashed changes IndexEntry indexEntry; try { readLock.lock(); +<<<<<<< Updated upstream indexEntry = collectionOperations.findIndex(field); +======= + checkOpened(); + indexDescriptor = collectionOperations.findIndex(indexFields); +>>>>>>> Stashed changes } finally { readLock.unlock(); } @@ -211,7 +234,12 @@ public void rebuildIndex(String field, boolean isAsync) { try { writeLock.lock(); +<<<<<<< Updated upstream collectionOperations.rebuildIndex(indexEntry, isAsync); +======= + checkOpened(); + collectionOperations.rebuildIndex(indexDescriptor); +>>>>>>> Stashed changes } finally { writeLock.unlock(); } @@ -220,58 +248,92 @@ public void rebuildIndex(String field, boolean isAsync) { } } +<<<<<<< Updated upstream public Collection listIndices() { checkOpened(); +======= + public Collection listIndices() { +>>>>>>> Stashed changes try { readLock.lock(); + checkOpened(); return collectionOperations.listIndexes(); } finally { readLock.unlock(); } } +<<<<<<< Updated upstream public boolean hasIndex(String field) { checkOpened(); notNull(field, "field cannot be null"); +======= + public boolean hasIndex(String... fields) { + notNull(fields, "fields cannot be null"); +>>>>>>> Stashed changes try { readLock.lock(); +<<<<<<< Updated upstream return collectionOperations.hasIndex(field); +======= + checkOpened(); + return collectionOperations.hasIndex(indexFields); +>>>>>>> Stashed changes } finally { readLock.unlock(); } } +<<<<<<< Updated upstream public boolean isIndexing(String field) { checkOpened(); notNull(field, "field cannot be null"); +======= + public boolean isIndexing(String... fields) { + notNull(fields, "field cannot be null"); +>>>>>>> Stashed changes try { readLock.lock(); +<<<<<<< Updated upstream return collectionOperations.isIndexing(field); +======= + checkOpened(); + return collectionOperations.isIndexing(indexFields); +>>>>>>> Stashed changes } finally { readLock.unlock(); } } +<<<<<<< Updated upstream public void dropIndex(String field) { checkOpened(); notNull(field, "field cannot be null"); +======= + public void dropIndex(String... fields) { + notNull(fields, "fields cannot be null"); +>>>>>>> Stashed changes try { writeLock.lock(); +<<<<<<< Updated upstream collectionOperations.dropIndex(field); +======= + checkOpened(); + collectionOperations.dropIndex(indexFields); +>>>>>>> Stashed changes } finally { writeLock.unlock(); } } public void dropAllIndices() { - checkOpened(); - try { writeLock.lock(); + checkOpened(); collectionOperations.dropAllIndices(); } finally { writeLock.unlock(); @@ -279,11 +341,11 @@ public void dropAllIndices() { } public Document getById(NitriteId nitriteId) { - checkOpened(); notNull(nitriteId, "nitriteId cannot be null"); try { readLock.lock(); + checkOpened(); return collectionOperations.getById(nitriteId); } finally { readLock.unlock(); @@ -291,11 +353,30 @@ public Document getById(NitriteId nitriteId) { } public void drop() { - checkOpened(); - try { writeLock.lock(); +<<<<<<< Updated upstream collectionOperations.dropCollection(); +======= + checkOpened(); + + if (collectionOperations != null) { + // close collection and indexes + collectionOperations.close(); + + // drop collection and indexes + collectionOperations.dropCollection(); + } + + // set all reference to null + this.nitriteMap = null; + this.nitriteConfig = null; + this.collectionOperations = null; + this.nitriteStore = null; + + // close event bus + closeEventBus(); +>>>>>>> Stashed changes } finally { writeLock.unlock(); } @@ -326,10 +407,9 @@ public String getName() { } public long size() { - checkOpened(); - try { readLock.lock(); + checkOpened(); return collectionOperations.getSize(); } finally { readLock.unlock(); @@ -357,10 +437,9 @@ public void unsubscribe(CollectionEventListener listener) { } public Attributes getAttributes() { - checkOpened(); - try { readLock.lock(); + checkOpened(); return collectionOperations.getAttributes(); } finally { readLock.unlock(); @@ -368,11 +447,11 @@ public Attributes getAttributes() { } public void setAttributes(Attributes attributes) { - checkOpened(); notNull(attributes, "attributes cannot be null"); try { writeLock.lock(); + checkOpened(); collectionOperations.setAttributes(attributes); } finally { writeLock.unlock(); From 21a5d5d3d3a68f2bc5ced717bb1ab351e37c5425 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 30 Jun 2021 02:11:03 +0530 Subject: [PATCH 02/78] fix security issue --- .../collection/DefaultNitriteCollection.java | 175 +++++++----------- 1 file changed, 64 insertions(+), 111 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index f5e2fbb44..a8f9fa3ea 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -22,6 +22,7 @@ import org.dizitart.no2.collection.events.CollectionEventListener; import org.dizitart.no2.collection.meta.Attributes; import org.dizitart.no2.collection.operation.CollectionOperations; +import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.concurrent.LockService; import org.dizitart.no2.common.event.EventBus; @@ -31,12 +32,14 @@ import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.NotIdentifiableException; import org.dizitart.no2.filters.Filter; -import org.dizitart.no2.index.IndexEntry; +import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; +import java.util.Arrays; import java.util.Collection; import java.util.concurrent.locks.Lock; @@ -74,13 +77,27 @@ class DefaultNitriteCollection implements NitriteCollection { initialize(); } + @Override + public void addProcessor(Processor processor) { + checkOpened(); + notNull(processor, "a null processor cannot be added"); + collectionOperations.addProcessor(processor); + } + + @Override + public void removeProcessor(Processor processor) { + checkOpened(); + notNull(processor, "a null processor cannot be removed"); + collectionOperations.removeProcessor(processor); + } + public WriteResult insert(Document[] documents) { + checkOpened(); notNull(documents, "a null document cannot be inserted"); containsNull(documents, "a null document cannot be inserted"); try { writeLock.lock(); - checkOpened(); return collectionOperations.insert(documents); } finally { writeLock.unlock(); @@ -88,6 +105,7 @@ public WriteResult insert(Document[] documents) { } public WriteResult update(Document document, boolean insertIfAbsent) { + checkOpened(); notNull(document, "a null document cannot be used for update"); if (insertIfAbsent) { @@ -102,12 +120,12 @@ public WriteResult update(Document document, boolean insertIfAbsent) { } public WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) { + checkOpened(); notNull(update, "a null document cannot be used for update"); notNull(updateOptions, "updateOptions cannot be null"); try { writeLock.lock(); - checkOpened(); return collectionOperations.update(filter, update, updateOptions); } finally { writeLock.unlock(); @@ -115,12 +133,12 @@ public WriteResult update(Filter filter, Document update, UpdateOptions updateOp } public WriteResult remove(Document document) { + checkOpened(); notNull(document, "a null document cannot be removed"); if (document.hasId()) { try { writeLock.lock(); - checkOpened(); return collectionOperations.remove(document); } finally { writeLock.unlock(); @@ -131,13 +149,13 @@ public WriteResult remove(Document document) { } public WriteResult remove(Filter filter, boolean justOne) { + checkOpened(); if ((filter == null || filter == Filter.ALL) && justOne) { throw new InvalidOperationException("remove all cannot be combined with just once"); } try { writeLock.lock(); - checkOpened(); return collectionOperations.remove(filter, justOne); } finally { writeLock.unlock(); @@ -145,195 +163,124 @@ public WriteResult remove(Filter filter, boolean justOne) { } public void clear() { + checkOpened(); try { writeLock.lock(); - checkOpened(); nitriteMap.clear(); } finally { writeLock.unlock(); } } -<<<<<<< Updated upstream - public DocumentCursor find() { - checkOpened(); - try { - readLock.lock(); - return collectionOperations.find(); -======= public DocumentCursor find(Filter filter, FindOptions findOptions) { + checkOpened(); try { readLock.lock(); - checkOpened(); return collectionOperations.find(filter, findOptions); ->>>>>>> Stashed changes } finally { readLock.unlock(); } } -<<<<<<< Updated upstream - public DocumentCursor find(Filter filter) { - checkOpened(); -======= public void createIndex(IndexOptions indexOptions, String... fields) { - notNull(fields, "fields cannot be null"); ->>>>>>> Stashed changes - - try { - readLock.lock(); - return collectionOperations.find(filter); - } finally { - readLock.unlock(); - } - } - - public void createIndex(String field, IndexOptions indexOptions) { checkOpened(); - notNull(field, "field cannot be null"); + notNull(fields, "fields cannot be null"); - // by default async is false while creating index + Fields indexFields = Fields.withNames(fields); try { writeLock.lock(); - checkOpened(); if (indexOptions == null) { - collectionOperations.createIndex(field, IndexType.Unique, false); + collectionOperations.createIndex(indexFields, IndexType.UNIQUE); } else { - collectionOperations.createIndex(field, indexOptions.getIndexType(), - indexOptions.isAsync()); + collectionOperations.createIndex(indexFields, indexOptions.getIndexType()); } } finally { writeLock.unlock(); } } -<<<<<<< Updated upstream - public void rebuildIndex(String field, boolean isAsync) { - checkOpened(); - notNull(field, "field cannot be null"); -======= public void rebuildIndex(String... fields) { + checkOpened(); notNull(fields, "fields cannot be null"); ->>>>>>> Stashed changes - IndexEntry indexEntry; + IndexDescriptor indexDescriptor; + Fields indexFields = Fields.withNames(fields); try { readLock.lock(); -<<<<<<< Updated upstream - indexEntry = collectionOperations.findIndex(field); -======= - checkOpened(); indexDescriptor = collectionOperations.findIndex(indexFields); ->>>>>>> Stashed changes } finally { readLock.unlock(); } - if (indexEntry != null) { - validateRebuildIndex(indexEntry); + if (indexDescriptor != null) { + validateRebuildIndex(indexDescriptor); try { writeLock.lock(); -<<<<<<< Updated upstream - collectionOperations.rebuildIndex(indexEntry, isAsync); -======= - checkOpened(); collectionOperations.rebuildIndex(indexDescriptor); ->>>>>>> Stashed changes } finally { writeLock.unlock(); } } else { - throw new IndexingException(field + " is not indexed"); + throw new IndexingException(Arrays.toString(fields) + " is not indexed"); } } -<<<<<<< Updated upstream - public Collection listIndices() { + public Collection listIndices() { checkOpened(); -======= - public Collection listIndices() { ->>>>>>> Stashed changes try { readLock.lock(); - checkOpened(); return collectionOperations.listIndexes(); } finally { readLock.unlock(); } } -<<<<<<< Updated upstream - public boolean hasIndex(String field) { - checkOpened(); - notNull(field, "field cannot be null"); -======= public boolean hasIndex(String... fields) { + checkOpened(); notNull(fields, "fields cannot be null"); ->>>>>>> Stashed changes + Fields indexFields = Fields.withNames(fields); try { readLock.lock(); -<<<<<<< Updated upstream - return collectionOperations.hasIndex(field); -======= - checkOpened(); return collectionOperations.hasIndex(indexFields); ->>>>>>> Stashed changes } finally { readLock.unlock(); } } -<<<<<<< Updated upstream - public boolean isIndexing(String field) { - checkOpened(); - notNull(field, "field cannot be null"); -======= public boolean isIndexing(String... fields) { + checkOpened(); notNull(fields, "field cannot be null"); ->>>>>>> Stashed changes + Fields indexFields = Fields.withNames(fields); try { readLock.lock(); -<<<<<<< Updated upstream - return collectionOperations.isIndexing(field); -======= - checkOpened(); return collectionOperations.isIndexing(indexFields); ->>>>>>> Stashed changes } finally { readLock.unlock(); } } -<<<<<<< Updated upstream - public void dropIndex(String field) { - checkOpened(); - notNull(field, "field cannot be null"); -======= public void dropIndex(String... fields) { + checkOpened(); notNull(fields, "fields cannot be null"); ->>>>>>> Stashed changes + Fields indexFields = Fields.withNames(fields); try { writeLock.lock(); -<<<<<<< Updated upstream - collectionOperations.dropIndex(field); -======= - checkOpened(); collectionOperations.dropIndex(indexFields); ->>>>>>> Stashed changes } finally { writeLock.unlock(); } } public void dropAllIndices() { + checkOpened(); + try { writeLock.lock(); - checkOpened(); collectionOperations.dropAllIndices(); } finally { writeLock.unlock(); @@ -341,11 +288,11 @@ public void dropAllIndices() { } public Document getById(NitriteId nitriteId) { + checkOpened(); notNull(nitriteId, "nitriteId cannot be null"); try { readLock.lock(); - checkOpened(); return collectionOperations.getById(nitriteId); } finally { readLock.unlock(); @@ -353,12 +300,10 @@ public Document getById(NitriteId nitriteId) { } public void drop() { + checkOpened(); + try { writeLock.lock(); -<<<<<<< Updated upstream - collectionOperations.dropCollection(); -======= - checkOpened(); if (collectionOperations != null) { // close collection and indexes @@ -376,25 +321,30 @@ public void drop() { // close event bus closeEventBus(); ->>>>>>> Stashed changes } finally { writeLock.unlock(); } isDropped = true; - close(); } public boolean isOpen() { if (nitriteStore == null || nitriteStore.isClosed() || isDropped) { + try { close(); + } catch (Exception e) { + throw new NitriteIOException("failed to close the database", e); + } return false; } else return true; } public void close() { if (collectionOperations != null) { + // close collection and indexes collectionOperations.close(); } + + // set all reference to null this.nitriteMap = null; this.nitriteConfig = null; this.collectionOperations = null; @@ -407,9 +357,10 @@ public String getName() { } public long size() { + checkOpened(); + try { readLock.lock(); - checkOpened(); return collectionOperations.getSize(); } finally { readLock.unlock(); @@ -437,9 +388,10 @@ public void unsubscribe(CollectionEventListener listener) { } public Attributes getAttributes() { + checkOpened(); + try { readLock.lock(); - checkOpened(); return collectionOperations.getAttributes(); } finally { readLock.unlock(); @@ -447,11 +399,11 @@ public Attributes getAttributes() { } public void setAttributes(Attributes attributes) { + checkOpened(); notNull(attributes, "attributes cannot be null"); try { writeLock.lock(); - checkOpened(); collectionOperations.setAttributes(attributes); } finally { writeLock.unlock(); @@ -486,11 +438,12 @@ private void checkOpened() { } } - private void validateRebuildIndex(IndexEntry indexEntry) { - notNull(indexEntry, "index cannot be null"); + private void validateRebuildIndex(IndexDescriptor indexDescriptor) { + notNull(indexDescriptor, "index cannot be null"); - if (isIndexing(indexEntry.getField())) { - throw new IndexingException("indexing on value " + indexEntry.getField() + " is currently running"); + String[] indexFields = indexDescriptor.getIndexFields().getFieldNames().toArray(new String[0]); + if (isIndexing(indexFields)) { + throw new IndexingException("indexing on value " + indexDescriptor.getIndexFields() + " is currently running"); } } From 50b9bf1bea57a32612caa02c5390dd81f2f78400 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 30 Jun 2021 02:15:22 +0530 Subject: [PATCH 03/78] fix security issue --- .../collection/DefaultNitriteCollection.java | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 944ba4452..777f5813a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -92,12 +92,12 @@ public void removeProcessor(Processor processor) { } public WriteResult insert(Document[] documents) { - checkOpened(); notNull(documents, "a null document cannot be inserted"); containsNull(documents, "a null document cannot be inserted"); try { writeLock.lock(); + checkOpened(); return collectionOperations.insert(documents); } finally { writeLock.unlock(); @@ -105,7 +105,6 @@ public WriteResult insert(Document[] documents) { } public WriteResult update(Document document, boolean insertIfAbsent) { - checkOpened(); notNull(document, "a null document cannot be used for update"); if (insertIfAbsent) { @@ -120,12 +119,12 @@ public WriteResult update(Document document, boolean insertIfAbsent) { } public WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) { - checkOpened(); notNull(update, "a null document cannot be used for update"); notNull(updateOptions, "updateOptions cannot be null"); try { writeLock.lock(); + checkOpened(); return collectionOperations.update(filter, update, updateOptions); } finally { writeLock.unlock(); @@ -133,12 +132,12 @@ public WriteResult update(Filter filter, Document update, UpdateOptions updateOp } public WriteResult remove(Document document) { - checkOpened(); notNull(document, "a null document cannot be removed"); if (document.hasId()) { try { writeLock.lock(); + checkOpened(); return collectionOperations.remove(document); } finally { writeLock.unlock(); @@ -149,13 +148,13 @@ public WriteResult remove(Document document) { } public WriteResult remove(Filter filter, boolean justOne) { - checkOpened(); if ((filter == null || filter == Filter.ALL) && justOne) { throw new InvalidOperationException("remove all cannot be combined with just once"); } try { writeLock.lock(); + checkOpened(); return collectionOperations.remove(filter, justOne); } finally { writeLock.unlock(); @@ -163,9 +162,9 @@ public WriteResult remove(Filter filter, boolean justOne) { } public void clear() { - checkOpened(); try { writeLock.lock(); + checkOpened(); nitriteMap.clear(); } finally { writeLock.unlock(); @@ -173,9 +172,9 @@ public void clear() { } public DocumentCursor find(Filter filter, FindOptions findOptions) { - checkOpened(); try { readLock.lock(); + checkOpened(); return collectionOperations.find(filter, findOptions); } finally { readLock.unlock(); @@ -183,12 +182,13 @@ public DocumentCursor find(Filter filter, FindOptions findOptions) { } public void createIndex(IndexOptions indexOptions, String... fields) { - checkOpened(); notNull(fields, "fields cannot be null"); Fields indexFields = Fields.withNames(fields); try { writeLock.lock(); + checkOpened(); + if (indexOptions == null) { collectionOperations.createIndex(indexFields, IndexType.UNIQUE); } else { @@ -200,13 +200,13 @@ public void createIndex(IndexOptions indexOptions, String... fields) { } public void rebuildIndex(String... fields) { - checkOpened(); notNull(fields, "fields cannot be null"); IndexDescriptor indexDescriptor; Fields indexFields = Fields.withNames(fields); try { readLock.lock(); + checkOpened(); indexDescriptor = collectionOperations.findIndex(indexFields); } finally { readLock.unlock(); @@ -217,6 +217,7 @@ public void rebuildIndex(String... fields) { try { writeLock.lock(); + checkOpened(); collectionOperations.rebuildIndex(indexDescriptor); } finally { writeLock.unlock(); @@ -227,10 +228,9 @@ public void rebuildIndex(String... fields) { } public Collection listIndices() { - checkOpened(); - try { readLock.lock(); + checkOpened(); return collectionOperations.listIndexes(); } finally { readLock.unlock(); @@ -238,12 +238,12 @@ public Collection listIndices() { } public boolean hasIndex(String... fields) { - checkOpened(); notNull(fields, "fields cannot be null"); Fields indexFields = Fields.withNames(fields); try { readLock.lock(); + checkOpened(); return collectionOperations.hasIndex(indexFields); } finally { readLock.unlock(); @@ -251,12 +251,12 @@ public boolean hasIndex(String... fields) { } public boolean isIndexing(String... fields) { - checkOpened(); notNull(fields, "field cannot be null"); Fields indexFields = Fields.withNames(fields); try { readLock.lock(); + checkOpened(); return collectionOperations.isIndexing(indexFields); } finally { readLock.unlock(); @@ -264,12 +264,12 @@ public boolean isIndexing(String... fields) { } public void dropIndex(String... fields) { - checkOpened(); notNull(fields, "fields cannot be null"); Fields indexFields = Fields.withNames(fields); try { writeLock.lock(); + checkOpened(); collectionOperations.dropIndex(indexFields); } finally { writeLock.unlock(); @@ -277,10 +277,9 @@ public void dropIndex(String... fields) { } public void dropAllIndices() { - checkOpened(); - try { writeLock.lock(); + checkOpened(); collectionOperations.dropAllIndices(); } finally { writeLock.unlock(); @@ -288,11 +287,11 @@ public void dropAllIndices() { } public Document getById(NitriteId nitriteId) { - checkOpened(); notNull(nitriteId, "nitriteId cannot be null"); try { readLock.lock(); + checkOpened(); return collectionOperations.getById(nitriteId); } finally { readLock.unlock(); @@ -300,10 +299,9 @@ public Document getById(NitriteId nitriteId) { } public void drop() { - checkOpened(); - try { writeLock.lock(); + checkOpened(); if (collectionOperations != null) { // close collection and indexes @@ -357,10 +355,9 @@ public String getName() { } public long size() { - checkOpened(); - try { readLock.lock(); + checkOpened(); return collectionOperations.getSize(); } finally { readLock.unlock(); @@ -388,10 +385,9 @@ public void unsubscribe(CollectionEventListener listener) { } public Attributes getAttributes() { - checkOpened(); - try { readLock.lock(); + checkOpened(); return collectionOperations.getAttributes(); } finally { readLock.unlock(); @@ -399,11 +395,11 @@ public Attributes getAttributes() { } public void setAttributes(Attributes attributes) { - checkOpened(); notNull(attributes, "attributes cannot be null"); try { writeLock.lock(); + checkOpened(); collectionOperations.setAttributes(attributes); } finally { writeLock.unlock(); From 633fb54e5581eabc3b546c834caaf7e08487bad0 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 1 Jul 2021 22:35:16 +0530 Subject: [PATCH 04/78] infer issue fixed --- .gitignore | 3 +- .../dizitart/no2/mvstore/MVStoreModule.java | 12 +- .../dizitart/no2/mvstore/NitriteMVStore.java | 2 +- .../org/dizitart/no2/mvstore/Recovery.java | 204 +++++++------- .../no2/mvstore/compat/v3/MigrationUtil.java | 10 +- .../mvstore/compat/v3/NitriteDataType.java | 18 +- .../no2/sync/handlers/JournalAware.java | 1 + .../dizitart/no2/rocksdb/RocksDBModule.java | 10 +- .../dizitart/no2/rocksdb/RocksDBStore.java | 2 +- .../dizitart/no2/spatial/SpatialModule.java | 7 +- .../org/dizitart/no2/support/Exporter.java | 11 +- .../org/dizitart/no2/support/Importer.java | 6 +- .../java/org/dizitart/no2/NitriteConfig.java | 9 +- .../org/dizitart/no2/NitriteDatabase.java | 7 +- .../no2/collection/CollectionFactory.java | 2 + .../collection/DefaultNitriteCollection.java | 89 ++++-- .../org/dizitart/no2/common/Constants.java | 6 +- .../common/concurrent/ThreadPoolManager.java | 2 +- .../no2/common/module/NitritePlugin.java | 7 +- .../no2/common/module/PluginManager.java | 80 ++++-- .../org/dizitart/no2/filters/InFilter.java | 1 - .../java/org/dizitart/no2/index/IndexMap.java | 2 +- .../no2/index/NitriteTextIndexer.java | 5 + .../no2/migration/MigrationManager.java | 1 + .../no2/migration/commands/BaseCommand.java | 7 + .../no2/migration/commands/Command.java | 7 +- .../no2/migration/commands/Rename.java | 27 +- .../no2/migration/commands/RenameField.java | 38 +-- .../org/dizitart/no2/store/NitriteStore.java | 2 +- .../no2/store/memory/InMemoryStore.java | 2 +- .../no2/store/memory/InMemoryStoreModule.java | 12 +- .../DefaultTransactionalCollection.java | 107 ++++--- .../no2/transaction/NitriteTransaction.java | 62 +++-- ...onalConfig.java => TransactionConfig.java} | 4 +- .../no2/transaction/TransactionContext.java | 2 +- ...tionalStore.java => TransactionStore.java} | 6 +- .../DefaultTransactionalCollectionTest.java | 22 +- .../DefaultTransactionalRepositoryTest.java | 4 +- .../transaction/NitriteTransactionTest.java | 2 +- ...igTest.java => TransactionConfigTest.java} | 26 +- .../transaction/TransactionContextTest.java | 4 +- .../no2/transaction/TransactionStoreTest.java | 144 ++++++++++ .../no2/transaction/TransactionalMapTest.java | 262 +++++++++--------- .../transaction/TransactionalStoreTest.java | 144 ---------- 44 files changed, 771 insertions(+), 610 deletions(-) rename nitrite/src/main/java/org/dizitart/no2/transaction/{TransactionalConfig.java => TransactionConfig.java} (93%) rename nitrite/src/main/java/org/dizitart/no2/transaction/{TransactionalStore.java => TransactionStore.java} (95%) rename nitrite/src/test/java/org/dizitart/no2/transaction/{TransactionalConfigTest.java => TransactionConfigTest.java} (63%) create mode 100644 nitrite/src/test/java/org/dizitart/no2/transaction/TransactionStoreTest.java delete mode 100644 nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalStoreTest.java diff --git a/.gitignore b/.gitignore index 2657aa562..196117685 100644 --- a/.gitignore +++ b/.gitignore @@ -451,4 +451,5 @@ captures/ test.log /build !no2-old.db -.diffblue \ No newline at end of file +.diffblue +infer-out \ No newline at end of file diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreModule.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreModule.java index 9270206e7..595d535ce 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreModule.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreModule.java @@ -34,6 +34,7 @@ * @author Anindya Chatterjee */ public class MVStoreModule implements StoreModule { + private NitriteStore nitriteStore; @Setter(AccessLevel.PACKAGE) private MVStoreConfig storeConfig; @@ -45,7 +46,7 @@ public MVStoreModule(String path) { @Override public Set plugins() { - return setOf(getStore()); + return setOf(nitriteStore); } public static MVStoreModuleBuilder withConfig() { @@ -53,8 +54,11 @@ public static MVStoreModuleBuilder withConfig() { } public NitriteStore getStore() { - NitriteMVStore store = new NitriteMVStore(); - store.setStoreConfig(storeConfig); - return store; + if (nitriteStore == null) { + NitriteMVStore store = new NitriteMVStore(); + store.setStoreConfig(storeConfig); + nitriteStore = store; + } + return nitriteStore; } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java index db963d23d..dec0d85e1 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java @@ -76,7 +76,7 @@ public void commit() { } @Override - public void close() throws Exception { + public void close() { if (getStoreConfig().autoCompact()) { compact(); } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java index 56a0519e3..575f7f732 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java @@ -16,6 +16,7 @@ package org.dizitart.no2.mvstore; +import org.dizitart.no2.exceptions.NitriteIOException; import org.h2.mvstore.Chunk; import org.h2.mvstore.DataUtils; import org.h2.mvstore.MVMap; @@ -90,34 +91,38 @@ public static boolean recover(String fileName, PrintWriter writer) { */ private static boolean repair(String fileName, PrintWriter pw) { long version = Long.MAX_VALUE; - OutputStream ignore = new OutputStream() { + boolean repaired = false; + + try(OutputStream ignore = new OutputStream() { @Override public void write(int b) { // ignore } - }; - boolean repaired = false; - while (version >= 0) { - pw.println(version == Long.MAX_VALUE ? "Trying latest version" : ("Trying version " + version)); - pw.flush(); - version = rollback(fileName, version, new PrintWriter(ignore)); - try { - String error = info(fileName + ".temp", new PrintWriter(ignore)); - if (error == null) { - FilePath.get(fileName).moveTo(FilePath.get(fileName + ".back"), true); - FilePath.get(fileName + ".temp").moveTo(FilePath.get(fileName), true); - pw.println("Success"); - repaired = true; - break; - } - pw.println(" ... failed: " + error); - } catch (Exception e) { - pw.println("Fail: " + e.getMessage()); + }) { + while (version >= 0) { + pw.println(version == Long.MAX_VALUE ? "Trying latest version" : ("Trying version " + version)); pw.flush(); + version = rollback(fileName, version, new PrintWriter(ignore)); + try { + String error = info(fileName + ".temp", new PrintWriter(ignore)); + if (error == null) { + FilePath.get(fileName).moveTo(FilePath.get(fileName + ".back"), true); + FilePath.get(fileName + ".temp").moveTo(FilePath.get(fileName), true); + pw.println("Success"); + repaired = true; + break; + } + pw.println(" ... failed: " + error); + } catch (Exception e) { + pw.println("Fail: " + e.getMessage()); + pw.flush(); + } + version--; } - version--; + pw.flush(); + } catch (IOException e) { + throw new NitriteIOException("Failed to repair database", e); } - pw.flush(); return repaired; } @@ -130,92 +135,93 @@ public void write(int b) { */ private static long rollback(String fileName, long targetVersion, Writer writer) { long newestVersion = -1; - PrintWriter pw = new PrintWriter(writer, true); - if (!FilePath.get(fileName).exists()) { - pw.println("File not found: " + fileName); - return newestVersion; - } - FileChannel file = null; - FileChannel target = null; - int blockSize = BLOCK_SIZE; - try { - file = FilePath.get(fileName).open("r"); - FilePath.get(fileName + ".temp").delete(); - target = FilePath.get(fileName + ".temp").open("rw"); - long fileSize = file.size(); - ByteBuffer block = ByteBuffer.allocate(4096); - Chunk newestChunk = null; - for (long pos = 0; pos < fileSize; ) { - block.rewind(); - DataUtils.readFully(file, pos, block); - block.rewind(); - int headerType = block.get(); - if (headerType == 'H') { + try(PrintWriter pw = new PrintWriter(writer, true)) { + if (!FilePath.get(fileName).exists()) { + pw.println("File not found: " + fileName); + return newestVersion; + } + FileChannel file = null; + FileChannel target = null; + int blockSize = BLOCK_SIZE; + try { + file = FilePath.get(fileName).open("r"); + FilePath.get(fileName + ".temp").delete(); + target = FilePath.get(fileName + ".temp").open("rw"); + long fileSize = file.size(); + ByteBuffer block = ByteBuffer.allocate(4096); + Chunk newestChunk = null; + for (long pos = 0; pos < fileSize; ) { block.rewind(); - target.write(block, pos); - pos += blockSize; - continue; - } - if (headerType != 'c') { - pos += blockSize; - continue; - } - Chunk c; - try { - c = readChunkHeader(block, pos); - } catch (IllegalStateException e) { - pos += blockSize; - continue; - } - if (c.len <= 0) { - // not a chunk - pos += blockSize; - continue; - } - int length = c.len * BLOCK_SIZE; - ByteBuffer chunk = ByteBuffer.allocate(length); - DataUtils.readFully(file, pos, chunk); - if (c.version > targetVersion) { - // newer than the requested version + DataUtils.readFully(file, pos, block); + block.rewind(); + int headerType = block.get(); + if (headerType == 'H') { + block.rewind(); + target.write(block, pos); + pos += blockSize; + continue; + } + if (headerType != 'c') { + pos += blockSize; + continue; + } + Chunk c; + try { + c = readChunkHeader(block, pos); + } catch (IllegalStateException e) { + pos += blockSize; + continue; + } + if (c.len <= 0) { + // not a chunk + pos += blockSize; + continue; + } + int length = c.len * BLOCK_SIZE; + ByteBuffer chunk = ByteBuffer.allocate(length); + DataUtils.readFully(file, pos, chunk); + if (c.version > targetVersion) { + // newer than the requested version + pos += length; + continue; + } + chunk.rewind(); + target.write(chunk, pos); + if (newestChunk == null || c.version > newestChunk.version) { + newestChunk = c; + newestVersion = c.version; + } pos += length; - continue; } - chunk.rewind(); - target.write(chunk, pos); - if (newestChunk == null || c.version > newestChunk.version) { - newestChunk = c; - newestVersion = c.version; + if (newestChunk != null) { + int length = newestChunk.len * BLOCK_SIZE; + ByteBuffer chunk = ByteBuffer.allocate(length); + DataUtils.readFully(file, newestChunk.block * BLOCK_SIZE, chunk); + chunk.rewind(); + target.write(chunk, fileSize); } - pos += length; - } - if (newestChunk != null) { - int length = newestChunk.len * BLOCK_SIZE; - ByteBuffer chunk = ByteBuffer.allocate(length); - DataUtils.readFully(file, newestChunk.block * BLOCK_SIZE, chunk); - chunk.rewind(); - target.write(chunk, fileSize); - } - } catch (IOException e) { - pw.println("ERROR: " + e); - e.printStackTrace(pw); - } finally { - if (file != null) { - try { - file.close(); - } catch (IOException e) { - // ignore + } catch (IOException e) { + pw.println("ERROR: " + e); + e.printStackTrace(pw); + } finally { + if (file != null) { + try { + file.close(); + } catch (IOException e) { + // ignore + } } - } - if (target != null) { - try { - target.close(); - } catch (IOException e) { - // ignore + if (target != null) { + try { + target.close(); + } catch (IOException e) { + // ignore + } } } + pw.flush(); + return newestVersion; } - pw.flush(); - return newestVersion; } /** diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java index f6dc0d984..6c7119a21 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java @@ -77,17 +77,19 @@ public static void migrate(MVStore newStore, MVStore oldStore) { @SuppressWarnings({"unchecked", "rawtypes"}) private static void copyData(MVMap oldMap, MVMap newMap) { if (oldMap != null) { - for (Object key : oldMap.keySet()) { - Object newKey = key; + Set entrySet = oldMap.entrySet(); + for (Map.Entry entry : entrySet) { + Object key = entry.getKey(); + Object newKey = entry.getKey(); + if (key instanceof Compat.NitriteId) { newKey = nitriteId((Compat.NitriteId) key); } else if (oldMap.getName().contains(INDEX_PREFIX)) { // index map, wrap with DBValue newKey = newKey == null ? DBNull.getInstance() : new DBValue((Comparable) newKey); } - Object value = oldMap.get(key); - Object newValue = migrateValue(value); + Object newValue = migrateValue(entry.getValue()); newMap.put(newKey, newValue); } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataType.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataType.java index c4da9cbf9..4ec5b385c 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataType.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataType.java @@ -336,11 +336,11 @@ static Integer getCommonClassId(Class clazz) { * @return the byte array */ public static byte[] serialize(Object obj) { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ObjectOutputStream os = new ObjectOutputStream(out); - os.writeObject(obj); - return out.toByteArray(); + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + try (ObjectOutputStream os = new ObjectOutputStream(out)) { + os.writeObject(obj); + return out.toByteArray(); + } } catch (Throwable e) { throw DataUtils.newIllegalArgumentException( "Could not serialize {0}", obj, e); @@ -354,10 +354,10 @@ public static byte[] serialize(Object obj) { * @return the object */ public static Object deserialize(byte[] data) { - try { - ByteArrayInputStream in = new ByteArrayInputStream(data); - NitriteObjectInputStream is = new NitriteObjectInputStream(in); - return is.readObject(); + try (ByteArrayInputStream in = new ByteArrayInputStream(data)) { + try (NitriteObjectInputStream is = new NitriteObjectInputStream(in)) { + return is.readObject(); + } } catch (Throwable e) { throw DataUtils.newIllegalArgumentException( "Could not deserialize {0}", Arrays.toString(data), e); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/JournalAware.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/JournalAware.java index 010421761..6f97dfadb 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/JournalAware.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/JournalAware.java @@ -87,6 +87,7 @@ default LastWriteWinState createState(Receipt receipt) { default boolean shouldRetry(Receipt receipt) { if (receipt == null) return false; + if (receipt.getAdded() == null) return false; if (receipt.getAdded() == null && receipt.getRemoved() == null) return false; return !receipt.getAdded().isEmpty() || !receipt.getRemoved().isEmpty(); } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBModule.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBModule.java index 6e15fc13b..2f5251ae3 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBModule.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBModule.java @@ -11,6 +11,7 @@ import static org.dizitart.no2.common.util.Iterables.setOf; public class RocksDBModule implements StoreModule { + private NitriteStore nitriteStore; @Setter(AccessLevel.PACKAGE) private RocksDBConfig storeConfig; @@ -30,8 +31,11 @@ public static RocksDBModuleBuilder withConfig() { } public NitriteStore getStore() { - RocksDBStore store = new RocksDBStore(); - store.setStoreConfig(storeConfig); - return store; + if (nitriteStore == null) { + RocksDBStore store = new RocksDBStore(); + store.setStoreConfig(storeConfig); + nitriteStore = store; + } + return nitriteStore; } } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java index 11dd021ca..26fedd17d 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java @@ -67,7 +67,7 @@ public void commit() { } @Override - public void close() throws Exception { + public void close() { try { if (!closed.get()) { // close nitrite maps diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialModule.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialModule.java index c0f37bca2..aa07b5f33 100644 --- a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialModule.java +++ b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialModule.java @@ -30,8 +30,13 @@ * @author Anindya Chatterjee */ public class SpatialModule implements NitriteModule { + private SpatialIndexer spatialIndexer; + @Override public Set plugins() { - return setOf(new SpatialIndexer()); + if (spatialIndexer == null) { + spatialIndexer = new SpatialIndexer(); + } + return setOf(spatialIndexer); } } diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java b/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java index c1e4d51b4..d3ee617a2 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java @@ -77,7 +77,6 @@ public static ObjectMapper createObjectMapper() { objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); -// objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return objectMapper; } @@ -121,7 +120,9 @@ public void exportTo(File file) { throw new IOException("Failed to create parent directory " + parent.getPath()); } } - exportTo(new FileOutputStream(file)); + try (FileOutputStream outputStream = new FileOutputStream(file)) { + exportTo(outputStream); + } } catch (IOException ioe) { throw new NitriteIOException("I/O error while writing content to file " + file, ioe); } @@ -132,8 +133,10 @@ public void exportTo(File file) { * * @param stream the stream */ - public void exportTo(OutputStream stream) { - exportTo(new OutputStreamWriter(stream)); + public void exportTo(OutputStream stream) throws IOException { + try(OutputStreamWriter writer = new OutputStreamWriter(stream)) { + exportTo(writer); + } } /** diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/Importer.java b/nitrite-support/src/main/java/org/dizitart/no2/support/Importer.java index 3e7a2c895..cdfe968fe 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/Importer.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/Importer.java @@ -90,8 +90,10 @@ public void importFrom(File file) { * * @param stream the stream */ - public void importFrom(InputStream stream) { - importFrom(new InputStreamReader(stream)); + public void importFrom(InputStream stream) throws IOException { + try(InputStreamReader reader = new InputStreamReader(stream)) { + importFrom(reader); + } } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java b/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java index d97c041bf..a432ae80e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java @@ -43,7 +43,7 @@ */ @Slf4j @ToString -public class NitriteConfig { +public class NitriteConfig implements AutoCloseable { /** * Indicates if this {@link NitriteConfig} is already configured. */ @@ -192,6 +192,13 @@ public NitriteStore getNitriteStore() { return pluginManager.getNitriteStore(); } + @Override + public void close() { + if (pluginManager != null) { + pluginManager.close(); + } + } + /** * Initializes this {@link NitriteConfig} instance. */ diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java index 21a79caf7..f355623ac 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java @@ -163,7 +163,12 @@ public synchronized void close() { repositoryFactory.clear(); collectionFactory.clear(); storeInfo.close(); - store.close(); + + if (nitriteConfig != null) { + // close all plugins + nitriteConfig.close(); + } + log.info("Nitrite database has been closed successfully."); } catch (NitriteIOException e) { throw e; diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java index 0e8ce6d6c..52a7c8edc 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java @@ -90,12 +90,14 @@ private NitriteCollection createCollection(String name, NitriteConfig nitriteCon // ignore repository request if (store.getRepositoryRegistry().contains(name)) { nitriteMap.close(); + collection.close(); throw new ValidationException("a repository with same name already exists"); } for (Set set : store.getKeyedRepositoryRegistry().values()) { if (set.contains(name)) { nitriteMap.close(); + collection.close(); throw new ValidationException("a keyed repository with same name already exists"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 777f5813a..548592ae0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -79,16 +79,28 @@ class DefaultNitriteCollection implements NitriteCollection { @Override public void addProcessor(Processor processor) { - checkOpened(); notNull(processor, "a null processor cannot be added"); - collectionOperations.addProcessor(processor); + + try { + writeLock.lock(); + checkOpened(); + collectionOperations.addProcessor(processor); + } finally { + writeLock.unlock(); + } } @Override public void removeProcessor(Processor processor) { - checkOpened(); notNull(processor, "a null processor cannot be removed"); - collectionOperations.removeProcessor(processor); + + try { + writeLock.lock(); + checkOpened(); + collectionOperations.removeProcessor(processor); + } finally { + writeLock.unlock(); + } } public WriteResult insert(Document[] documents) { @@ -326,28 +338,33 @@ public void drop() { } public boolean isOpen() { - if (nitriteStore == null || nitriteStore.isClosed() || isDropped) { - try { - close(); - } catch (Exception e) { - throw new NitriteIOException("failed to close the database", e); - } - return false; - } else return true; + try { + readLock.lock(); + return nitriteStore != null && !nitriteStore.isClosed() && !isDropped; + } catch (Exception e) { + throw new NitriteIOException("failed to close the database", e); + } finally { + readLock.unlock(); + } } public void close() { - if (collectionOperations != null) { - // close collection and indexes - collectionOperations.close(); - } + try { + writeLock.lock(); + if (collectionOperations != null) { + // close collection and indexes + collectionOperations.close(); + } - // set all reference to null - this.nitriteMap = null; - this.nitriteConfig = null; - this.collectionOperations = null; - this.nitriteStore = null; - closeEventBus(); + // set all reference to null + this.nitriteMap = null; + this.nitriteConfig = null; + this.collectionOperations = null; + this.nitriteStore = null; + closeEventBus(); + } finally { + writeLock.unlock(); + } } public String getName() { @@ -365,22 +382,36 @@ public long size() { } public NitriteStore getStore() { - return nitriteStore; + try { + writeLock.lock(); + return nitriteStore; + } finally { + writeLock.unlock(); + } } public void subscribe(CollectionEventListener listener) { - checkOpened(); notNull(listener, "listener cannot be null"); - - eventBus.register(listener); + try { + writeLock.lock(); + checkOpened(); + eventBus.register(listener); + } finally { + writeLock.unlock(); + } } public void unsubscribe(CollectionEventListener listener) { - checkOpened(); notNull(listener, "listener cannot be null"); + try { + writeLock.lock(); + checkOpened(); - if (eventBus != null) { - eventBus.deregister(listener); + if (eventBus != null) { + eventBus.deregister(listener); + } + } finally { + writeLock.unlock(); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/Constants.java b/nitrite/src/main/java/org/dizitart/no2/common/Constants.java index 1fe40ee42..dbae6f970 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/Constants.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/Constants.java @@ -38,7 +38,11 @@ private Constants() {} String v = "unknown"; try (InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("version")) { if (is != null) { - v = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)).readLine(); + try(InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { + try(BufferedReader bufferedReader = new BufferedReader(reader)) { + v = bufferedReader.readLine(); + } + } } } catch (IOException e) { throw new NitriteIOException("failed to load version information", e); diff --git a/nitrite/src/main/java/org/dizitart/no2/common/concurrent/ThreadPoolManager.java b/nitrite/src/main/java/org/dizitart/no2/common/concurrent/ThreadPoolManager.java index a3f84158b..8ad7226b1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/concurrent/ThreadPoolManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/concurrent/ThreadPoolManager.java @@ -102,7 +102,7 @@ public static Future runAsync(Runnable runnable) { /** * Shuts down all thread pools. */ - public static void shutdownThreadPools() { + public synchronized static void shutdownThreadPools() { for (ExecutorService threadPool : threadPools) { synchronized (lock) { if (threadPool != null) { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/module/NitritePlugin.java b/nitrite/src/main/java/org/dizitart/no2/common/module/NitritePlugin.java index d15f52e1e..b7a16d21d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/module/NitritePlugin.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/module/NitritePlugin.java @@ -24,11 +24,16 @@ * @author Anindya Chatterjee. * @since 4.0 */ -public interface NitritePlugin { +public interface NitritePlugin extends AutoCloseable { /** * Initializes the plugin instance. * * @param nitriteConfig the nitrite config */ void initialize(NitriteConfig nitriteConfig); + + default void close() { + // this is to make NitritePlugin a functional interface + // and make close() not throw checked exception + } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java b/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java index 61a53e8c3..f2253a789 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java @@ -41,11 +41,11 @@ */ @Slf4j @Getter -public class PluginManager { +public class PluginManager implements AutoCloseable { private final Map indexerMap; + private final NitriteConfig nitriteConfig; private NitriteMapper nitriteMapper; private NitriteStore nitriteStore; - private final NitriteConfig nitriteConfig; /** * Instantiates a new {@link PluginManager}. @@ -104,6 +104,21 @@ public void initializePlugins() { } } + @Override + public void close() { + for (NitriteIndexer nitriteIndexer : indexerMap.values()) { + nitriteIndexer.close(); + } + + if (nitriteMapper != null) { + nitriteMapper.close(); + } + + if (nitriteStore != null) { + nitriteStore.close(); + } + } + private void loadPlugin(NitritePlugin plugin) { populatePlugins(plugin); } @@ -114,60 +129,67 @@ private void initializePlugin(NitritePlugin plugin) { private void populatePlugins(NitritePlugin plugin) { if (plugin != null) { - loadIfIndexer(plugin); - loadIfNitriteMapper(plugin); - loadIfNitriteStore(plugin); + if (plugin instanceof NitriteIndexer) { + loadIndexer((NitriteIndexer) plugin); + } else if (plugin instanceof NitriteMapper) { + loadNitriteMapper((NitriteMapper) plugin); + } else if (plugin instanceof NitriteStore) { + loadNitriteStore((NitriteStore) plugin); + } else { + plugin.close(); + throw new PluginException("invalid plugin loaded " + plugin); + } } } - private void loadIfNitriteStore(NitritePlugin plugin) { - if (plugin instanceof NitriteStore) { - if (nitriteStore != null) { - throw new PluginException("multiple NitriteStore found"); - } - this.nitriteStore = (NitriteStore) plugin; + private void loadNitriteStore(NitriteStore nitriteStore) { + if (this.nitriteStore != null) { + nitriteStore.close(); + throw new PluginException("multiple NitriteStore found"); } + this.nitriteStore = nitriteStore; } - private void loadIfNitriteMapper(NitritePlugin plugin) { - if (plugin instanceof NitriteMapper) { - if (nitriteMapper != null) { - throw new PluginException("multiple NitriteMapper found"); - } - this.nitriteMapper = (NitriteMapper) plugin; + private void loadNitriteMapper(NitriteMapper nitriteMapper) { + if (this.nitriteMapper != null) { + nitriteMapper.close(); + throw new PluginException("multiple NitriteMapper found"); } + this.nitriteMapper = nitriteMapper; } - private synchronized void loadIfIndexer(NitritePlugin plugin) { - if (plugin instanceof NitriteIndexer) { - NitriteIndexer nitriteIndexer = (NitriteIndexer) plugin; - if (indexerMap.containsKey(nitriteIndexer.getIndexType())) { - throw new PluginException("multiple Indexer found for type " - + nitriteIndexer.getIndexType()); - } - this.indexerMap.put(nitriteIndexer.getIndexType(), nitriteIndexer); + private synchronized void loadIndexer(NitriteIndexer nitriteIndexer) { + if (indexerMap.containsKey(nitriteIndexer.getIndexType())) { + nitriteIndexer.close(); + throw new PluginException("multiple Indexer found for type " + + nitriteIndexer.getIndexType()); } + this.indexerMap.put(nitriteIndexer.getIndexType(), nitriteIndexer); } protected void loadInternalPlugins() { if (!indexerMap.containsKey(IndexType.UNIQUE)) { log.debug("Loading default unique indexer"); - loadPlugin(new UniqueIndexer()); + NitritePlugin plugin = new UniqueIndexer(); + loadPlugin(plugin); } if (!indexerMap.containsKey(IndexType.NON_UNIQUE)) { log.debug("Loading default non-unique indexer"); - loadPlugin(new NonUniqueIndexer()); + NitritePlugin plugin = new NonUniqueIndexer(); + loadPlugin(plugin); } if (!indexerMap.containsKey(IndexType.FULL_TEXT)) { log.debug("Loading nitrite text indexer"); - loadPlugin(new NitriteTextIndexer()); + NitritePlugin plugin = new NitriteTextIndexer(); + loadPlugin(plugin); } if (nitriteMapper == null) { log.debug("Loading mappable mapper"); - loadPlugin(new MappableMapper()); + NitritePlugin plugin = new MappableMapper(); + loadPlugin(plugin); } if (nitriteStore == null) { diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/InFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/InFilter.java index c4b1b065d..79b4a3872 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/InFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/InFilter.java @@ -37,7 +37,6 @@ class InFilter extends ComparableArrayFilter { Collections.addAll(this.comparableSet, values); } - @Override public boolean apply(Pair element) { Document document = element.getSecond(); diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexMap.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexMap.java index 458c4b337..8eecb4f4f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexMap.java @@ -239,7 +239,7 @@ public boolean hasNext() { } }; } - return null; + return Collections.EMPTY_SET; } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/index/NitriteTextIndexer.java b/nitrite/src/main/java/org/dizitart/no2/index/NitriteTextIndexer.java index 47cd4cf96..683cc0f73 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/NitriteTextIndexer.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/NitriteTextIndexer.java @@ -97,6 +97,11 @@ public LinkedHashSet findByFilter(FindPlan findPlan, NitriteConfig ni return textIndex.findNitriteIds(findPlan); } + @Override + public void close() { + indexRegistry.clear(); + } + private TextIndex findTextIndex(IndexDescriptor indexDescriptor, NitriteConfig nitriteConfig) { if (indexRegistry.containsKey(indexDescriptor)) { return indexRegistry.get(indexDescriptor); diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java b/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java index 7da5cea89..61aef06d2 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java @@ -288,6 +288,7 @@ private void executeStep(MigrationStep step) { } command.execute(database); + command.close(); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/commands/BaseCommand.java b/nitrite/src/main/java/org/dizitart/no2/migration/commands/BaseCommand.java index db6cade49..2d1366488 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/commands/BaseCommand.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/commands/BaseCommand.java @@ -30,6 +30,13 @@ abstract class BaseCommand implements Command { */ protected CollectionOperations operations; + @Override + public void close() { + if (operations != null) { + operations.close(); + } + } + /** * Initializes the database for migration. * diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/commands/Command.java b/nitrite/src/main/java/org/dizitart/no2/migration/commands/Command.java index 7d3c7b0db..9019fde80 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/commands/Command.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/commands/Command.java @@ -8,11 +8,16 @@ * @author Anindya Chatterjee * @since 4.0 */ -public interface Command { +public interface Command extends AutoCloseable { /** * Executes a migration step on the database. * * @param nitrite the nitrite database instance */ void execute(Nitrite nitrite); + + default void close() { + // this is just to make Command a functional interface + // and make close() not throw checked exception + } } diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/commands/Rename.java b/nitrite/src/main/java/org/dizitart/no2/migration/commands/Rename.java index 5839867c0..923aa4205 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/commands/Rename.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/commands/Rename.java @@ -29,18 +29,21 @@ public void execute(Nitrite nitrite) { initialize(nitrite, oldName); NitriteMap newMap = nitriteStore.openMap(newName, NitriteId.class, Document.class); - CollectionOperations newOperations = new CollectionOperations(newName, newMap, nitrite.getConfig(), null); - - for (Pair entry : nitriteMap.entries()) { - newMap.put(entry.getFirst(), entry.getSecond()); - } - - IndexManager indexManager = new IndexManager(oldName, nitrite.getConfig()); - Collection indexEntries = indexManager.getIndexDescriptors(); - for (IndexDescriptor indexDescriptor : indexEntries) { - Fields field = indexDescriptor.getIndexFields(); - String indexType = indexDescriptor.getIndexType(); - newOperations.createIndex(field, indexType); + try(CollectionOperations newOperations + = new CollectionOperations(newName, newMap, nitrite.getConfig(), null)) { + + for (Pair entry : nitriteMap.entries()) { + newMap.put(entry.getFirst(), entry.getSecond()); + } + + try (IndexManager indexManager = new IndexManager(oldName, nitrite.getConfig())) { + Collection indexEntries = indexManager.getIndexDescriptors(); + for (IndexDescriptor indexDescriptor : indexEntries) { + Fields field = indexDescriptor.getIndexFields(); + String indexType = indexDescriptor.getIndexType(); + newOperations.createIndex(field, indexType); + } + } } operations.dropCollection(); diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/commands/RenameField.java b/nitrite/src/main/java/org/dizitart/no2/migration/commands/RenameField.java index 7475c5599..6f9f92ec8 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/commands/RenameField.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/commands/RenameField.java @@ -27,29 +27,31 @@ public class RenameField extends BaseCommand implements Command { public void execute(Nitrite nitrite) { initialize(nitrite, collectionName); - IndexManager indexManager = new IndexManager(oldName, nitrite.getConfig()); - Fields oldField = Fields.withNames(oldName); - Collection matchingIndexDescriptors = indexManager.findMatchingIndexDescriptors(oldField); + try(IndexManager indexManager = new IndexManager(oldName, nitrite.getConfig())) { + Fields oldField = Fields.withNames(oldName); + Collection matchingIndexDescriptors + = indexManager.findMatchingIndexDescriptors(oldField); - for (Pair entry : nitriteMap.entries()) { - Document document = entry.getSecond(); - if (document.containsKey(oldName)) { - Object value = document.get(oldName); - document.put(newName, value); - document.remove(oldName); + for (Pair entry : nitriteMap.entries()) { + Document document = entry.getSecond(); + if (document.containsKey(oldName)) { + Object value = document.get(oldName); + document.put(newName, value); + document.remove(oldName); - nitriteMap.put(entry.getFirst(), document); + nitriteMap.put(entry.getFirst(), document); + } } - } - if (!matchingIndexDescriptors.isEmpty()) { - for (IndexDescriptor matchingIndexDescriptor : matchingIndexDescriptors) { - String indexType = matchingIndexDescriptor.getIndexType(); + if (!matchingIndexDescriptors.isEmpty()) { + for (IndexDescriptor matchingIndexDescriptor : matchingIndexDescriptors) { + String indexType = matchingIndexDescriptor.getIndexType(); - Fields oldIndexFields = matchingIndexDescriptor.getIndexFields(); - Fields newIndexFields = getNewIndexFields(oldIndexFields, oldName, newName); - operations.dropIndex(matchingIndexDescriptor.getIndexFields()); - operations.createIndex(newIndexFields, indexType); + Fields oldIndexFields = matchingIndexDescriptor.getIndexFields(); + Fields newIndexFields = getNewIndexFields(oldIndexFields, oldName, newName); + operations.dropIndex(matchingIndexDescriptor.getIndexFields()); + operations.createIndex(newIndexFields, indexType); + } } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/NitriteStore.java b/nitrite/src/main/java/org/dizitart/no2/store/NitriteStore.java index 960b93b8f..1505c400e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/NitriteStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/NitriteStore.java @@ -32,7 +32,7 @@ * @author Anindya Chatterjee * @since 1.0 */ -public interface NitriteStore extends NitritePlugin, AutoCloseable { +public interface NitriteStore extends NitritePlugin { /** * Opens or creates this nitrite store. diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java index e801a0e14..7a50adb94 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java @@ -59,7 +59,7 @@ public void commit() { } @Override - public void close() throws Exception { + public void close() { closed = true; for (NitriteMap map : nitriteMapRegistry.values()) { diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStoreModule.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStoreModule.java index e32ac6c96..867d84cfd 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStoreModule.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStoreModule.java @@ -17,6 +17,7 @@ * @since 4.0 */ public class InMemoryStoreModule implements StoreModule { + private NitriteStore nitriteStore; @Setter(AccessLevel.PACKAGE) private InMemoryConfig storeConfig; @@ -39,13 +40,16 @@ public static InMemoryModuleBuilder withConfig() { @Override public NitriteStore getStore() { - InMemoryStore store = new InMemoryStore(); - store.setStoreConfig(storeConfig); - return store; + if (nitriteStore == null) { + InMemoryStore store = new InMemoryStore(); + store.setStoreConfig(storeConfig); + nitriteStore = store; + } + return nitriteStore; } @Override public Set plugins() { - return setOf(getStore()); + return setOf(nitriteStore); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index d64e7ec91..30b01c4ac 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -1,6 +1,8 @@ package org.dizitart.no2.transaction; -import lombok.Data; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.*; @@ -12,12 +14,12 @@ import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.event.EventBus; import org.dizitart.no2.common.event.NitriteEventBus; +import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.exceptions.*; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; @@ -39,7 +41,7 @@ * @author Anindya Chatterjee * @since 4.0 */ -@Data +@Getter @Setter class DefaultTransactionalCollection implements NitriteCollection { private final NitriteCollection primary; private final TransactionContext transactionContext; @@ -48,13 +50,22 @@ class DefaultTransactionalCollection implements NitriteCollection { private String collectionName; private NitriteMap nitriteMap; private NitriteStore nitriteStore; - private Lock writeLock; - private Lock readLock; private CollectionOperations collectionOperations; - private EventBus, CollectionEventListener> eventBus; private volatile boolean isDropped; private volatile boolean isClosed; + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Lock writeLock; + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private Lock readLock; + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private EventBus, CollectionEventListener> eventBus; + public DefaultTransactionalCollection(NitriteCollection primary, TransactionContext transactionContext, Nitrite nitrite) { @@ -67,7 +78,6 @@ public DefaultTransactionalCollection(NitriteCollection primary, @Override public WriteResult insert(Document[] documents) { - checkOpened(); notNull(documents, "a null document cannot be inserted"); containsNull(documents, "a null document cannot be inserted"); @@ -79,6 +89,7 @@ public WriteResult insert(Document[] documents) { WriteResult result; try { writeLock.lock(); + checkOpened(); result = collectionOperations.insert(documents); } finally { writeLock.unlock(); @@ -99,13 +110,13 @@ public WriteResult insert(Document[] documents) { @Override public WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) { - checkOpened(); notNull(update, "a null document cannot be used for update"); notNull(updateOptions, "updateOptions cannot be null"); WriteResult result; try { writeLock.lock(); + checkOpened(); result = collectionOperations.update(filter, update, updateOptions); } finally { writeLock.unlock(); @@ -140,8 +151,6 @@ public WriteResult update(Filter filter, Document update, UpdateOptions updateOp @Override public WriteResult update(Document document, boolean insertIfAbsent) { - checkOpened(); - notNull(document, "a null document cannot be used for update"); if (insertIfAbsent) { @@ -157,13 +166,13 @@ public WriteResult update(Document document, boolean insertIfAbsent) { @Override public WriteResult remove(Document document) { - checkOpened(); notNull(document, "a null document cannot be removed"); WriteResult result; if (document.hasId()) { try { writeLock.lock(); + checkOpened(); result = collectionOperations.remove(document); } finally { writeLock.unlock(); @@ -192,7 +201,6 @@ public WriteResult remove(Document document) { @Override public WriteResult remove(Filter filter, boolean justOne) { - checkOpened(); if ((filter == null || filter == Filter.ALL) && justOne) { throw new InvalidOperationException("remove all cannot be combined with just once"); } @@ -200,6 +208,7 @@ public WriteResult remove(Filter filter, boolean justOne) { WriteResult result; try { writeLock.lock(); + checkOpened(); result = collectionOperations.remove(filter, justOne); } finally { writeLock.unlock(); @@ -233,10 +242,9 @@ public WriteResult remove(Filter filter, boolean justOne) { @Override public DocumentCursor find(Filter filter, FindOptions findOptions) { - checkOpened(); - try { readLock.lock(); + checkOpened(); return collectionOperations.find(filter, findOptions); } finally { readLock.unlock(); @@ -245,11 +253,11 @@ public DocumentCursor find(Filter filter, FindOptions findOptions) { @Override public Document getById(NitriteId nitriteId) { - checkOpened(); notNull(nitriteId, "nitriteId cannot be null"); try { readLock.lock(); + checkOpened(); return collectionOperations.getById(nitriteId); } finally { readLock.unlock(); @@ -263,9 +271,14 @@ public String getName() { @Override public void addProcessor(Processor processor) { - checkOpened(); notNull(processor, "a null processor cannot be added"); - collectionOperations.addProcessor(processor); + try { + writeLock.lock(); + checkOpened(); + collectionOperations.addProcessor(processor); + } finally { + writeLock.unlock(); + } JournalEntry journalEntry = new JournalEntry(); journalEntry.setChangeType(ChangeType.AddProcessor); @@ -276,9 +289,14 @@ public void addProcessor(Processor processor) { @Override public void removeProcessor(Processor processor) { - checkOpened(); notNull(processor, "a null processor cannot be removed"); - collectionOperations.addProcessor(processor); + try { + writeLock.lock(); + checkOpened(); + collectionOperations.addProcessor(processor); + } finally { + writeLock.unlock(); + } JournalEntry journalEntry = new JournalEntry(); journalEntry.setChangeType(ChangeType.RemoveProcessor); @@ -289,13 +307,13 @@ public void removeProcessor(Processor processor) { @Override public void createIndex(IndexOptions indexOptions, String... fieldNames) { - checkOpened(); notNull(fieldNames, "fieldNames cannot be null"); // by default async is false while creating index try { Fields fields = Fields.withNames(fieldNames); writeLock.lock(); + checkOpened(); if (indexOptions == null) { collectionOperations.createIndex(fields, IndexType.UNIQUE); } else { @@ -314,13 +332,13 @@ public void createIndex(IndexOptions indexOptions, String... fieldNames) { @Override public void rebuildIndex(String... fieldNames) { - checkOpened(); notNull(fieldNames, "fieldNames cannot be null"); IndexDescriptor indexDescriptor; try { Fields fields = Fields.withNames(fieldNames); readLock.lock(); + checkOpened(); indexDescriptor = collectionOperations.findIndex(fields); } finally { readLock.unlock(); @@ -331,6 +349,7 @@ public void rebuildIndex(String... fieldNames) { try { writeLock.lock(); + checkOpened(); collectionOperations.rebuildIndex(indexDescriptor); } finally { writeLock.unlock(); @@ -348,10 +367,9 @@ public void rebuildIndex(String... fieldNames) { @Override public Collection listIndices() { - checkOpened(); - try { readLock.lock(); + checkOpened(); return collectionOperations.listIndexes(); } finally { readLock.unlock(); @@ -360,12 +378,12 @@ public Collection listIndices() { @Override public boolean hasIndex(String... fieldNames) { - checkOpened(); notNull(fieldNames, "fieldNames cannot be null"); try { Fields fields = Fields.withNames(fieldNames); readLock.lock(); + checkOpened(); return collectionOperations.hasIndex(fields); } finally { readLock.unlock(); @@ -374,12 +392,12 @@ public boolean hasIndex(String... fieldNames) { @Override public boolean isIndexing(String... fieldNames) { - checkOpened(); notNull(fieldNames, "fieldNames cannot be null"); try { Fields fields = Fields.withNames(fieldNames); readLock.lock(); + checkOpened(); return collectionOperations.isIndexing(fields); } finally { readLock.unlock(); @@ -388,12 +406,12 @@ public boolean isIndexing(String... fieldNames) { @Override public void dropIndex(String... fieldNames) { - checkOpened(); notNull(fieldNames, "fieldNames cannot be null"); Fields fields = Fields.withNames(fieldNames); try { writeLock.lock(); + checkOpened(); collectionOperations.dropIndex(fields); } finally { writeLock.unlock(); @@ -422,10 +440,9 @@ public void dropIndex(String... fieldNames) { @Override public void dropAllIndices() { - checkOpened(); - try { writeLock.lock(); + checkOpened(); collectionOperations.dropAllIndices(); } finally { writeLock.unlock(); @@ -450,9 +467,9 @@ public void dropAllIndices() { @Override public void clear() { - checkOpened(); try { writeLock.lock(); + checkOpened(); nitriteMap.clear(); } finally { writeLock.unlock(); @@ -476,10 +493,9 @@ public void clear() { @Override public void drop() { - checkOpened(); - try { writeLock.lock(); + checkOpened(); collectionOperations.dropCollection(); } finally { writeLock.unlock(); @@ -529,7 +545,7 @@ public boolean isOpen() { } @Override - public void close() { + public synchronized void close() { if (collectionOperations != null) { collectionOperations.close(); } @@ -549,28 +565,35 @@ public NitriteStore getStore() { @Override public void subscribe(CollectionEventListener listener) { - checkOpened(); notNull(listener, "listener cannot be null"); - - eventBus.register(listener); + try { + writeLock.lock(); + checkOpened(); + eventBus.register(listener); + } finally { + writeLock.unlock(); + } } @Override public void unsubscribe(CollectionEventListener listener) { - checkOpened(); notNull(listener, "listener cannot be null"); - - if (eventBus != null) { - eventBus.deregister(listener); + try { + writeLock.lock(); + checkOpened(); + if (eventBus != null) { + eventBus.deregister(listener); + } + } finally { + writeLock.unlock(); } } @Override public Attributes getAttributes() { - checkOpened(); - try { readLock.lock(); + checkOpened(); return collectionOperations.getAttributes(); } finally { readLock.unlock(); @@ -579,11 +602,11 @@ public Attributes getAttributes() { @Override public void setAttributes(Attributes attributes) { - checkOpened(); notNull(attributes, "attributes cannot be null"); try { writeLock.lock(); + checkOpened(); collectionOperations.setAttributes(attributes); } finally { writeLock.unlock(); diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java index 63fa00a0b..26af4fbc3 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java @@ -15,6 +15,7 @@ import org.dizitart.no2.store.NitriteStore; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryName; @@ -28,8 +29,8 @@ class NitriteTransaction implements Transaction { private final Nitrite nitrite; private final LockService lockService; - private TransactionalStore transactionalStore; - private TransactionalConfig transactionalConfig; + private TransactionStore transactionStore; + private TransactionConfig transactionConfig; private Map contextMap; private Map collectionRegistry; private Map> repositoryRegistry; @@ -38,7 +39,6 @@ class NitriteTransaction implements Transaction { @Getter private String id; - @Getter private State state; public NitriteTransaction(Nitrite nitrite, LockService lockService) { @@ -48,7 +48,7 @@ public NitriteTransaction(Nitrite nitrite, LockService lockService) { } @Override - public NitriteCollection getCollection(String name) { + public synchronized NitriteCollection getCollection(String name) { checkState(); if (collectionRegistry.containsKey(name)) { @@ -62,14 +62,14 @@ public NitriteCollection getCollection(String name) { throw new TransactionException("collection " + name + " does not exists"); } - NitriteMap txMap = transactionalStore.openMap(name, + NitriteMap txMap = transactionStore.openMap(name, NitriteId.class, Document.class); TransactionContext context = new TransactionContext(); context.setCollectionName(name); context.setNitriteMap(txMap); context.setJournal(new LinkedList<>()); - context.setConfig(transactionalConfig); + context.setConfig(transactionConfig); NitriteCollection txCollection = new DefaultTransactionalCollection(primary, context, nitrite); collectionRegistry.put(name, txCollection); @@ -79,7 +79,7 @@ public NitriteCollection getCollection(String name) { @Override @SuppressWarnings("unchecked") - public ObjectRepository getRepository(Class type) { + public synchronized ObjectRepository getRepository(Class type) { checkState(); String name = findRepositoryName(type, null); @@ -94,19 +94,19 @@ public ObjectRepository getRepository(Class type) { throw new TransactionException("repository of type " + type.getName() + " does not exists"); } - NitriteMap txMap = transactionalStore.openMap(name, + NitriteMap txMap = transactionStore.openMap(name, NitriteId.class, Document.class); TransactionContext context = new TransactionContext(); context.setCollectionName(name); context.setNitriteMap(txMap); context.setJournal(new LinkedList<>()); - context.setConfig(transactionalConfig); + context.setConfig(transactionConfig); NitriteCollection primaryCollection = primary.getDocumentCollection(); NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context, nitrite); ObjectRepository txRepository = new DefaultTransactionalRepository<>(type, - primary, backingCollection, transactionalConfig); + primary, backingCollection, transactionConfig); repositoryRegistry.put(name, txRepository); contextMap.put(name, context); @@ -115,7 +115,7 @@ public ObjectRepository getRepository(Class type) { @Override @SuppressWarnings("unchecked") - public ObjectRepository getRepository(Class type, String key) { + public synchronized ObjectRepository getRepository(Class type, String key) { checkState(); String name = findRepositoryName(type, key); @@ -131,26 +131,26 @@ public ObjectRepository getRepository(Class type, String key) { + " and key " + key + " does not exists"); } - NitriteMap txMap = transactionalStore.openMap(name, + NitriteMap txMap = transactionStore.openMap(name, NitriteId.class, Document.class); TransactionContext context = new TransactionContext(); context.setCollectionName(name); context.setNitriteMap(txMap); context.setJournal(new LinkedList<>()); - context.setConfig(transactionalConfig); + context.setConfig(transactionConfig); NitriteCollection primaryCollection = primary.getDocumentCollection(); NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context, nitrite); ObjectRepository txRepository = new DefaultTransactionalRepository<>(type, - primary, backingCollection, transactionalConfig); + primary, backingCollection, transactionConfig); repositoryRegistry.put(name, txRepository); contextMap.put(name, context); return txRepository; } @Override - public void commit() { + public synchronized void commit() { checkState(); this.state = State.PartiallyCommitted; @@ -161,6 +161,7 @@ public void commit() { Stack undoLog = undoRegistry.containsKey(collectionName) ? undoRegistry.get(collectionName) : new Stack<>(); + // put collection level lock Lock lock = lockService.getWriteLock(collectionName); try { lock.lock(); @@ -202,13 +203,14 @@ public void commit() { } @Override - public void rollback() { + public synchronized void rollback() { this.state = State.Aborted; for (Map.Entry> entry : undoRegistry.entrySet()) { String collectionName = entry.getKey(); Stack undoLog = entry.getValue(); + // put collection level lock Lock writeLock = lockService.getWriteLock(collectionName); try { writeLock.lock(); @@ -229,7 +231,7 @@ public void rollback() { } @Override - public void close() { + public synchronized void close() { try { state = State.Closed; for (TransactionContext context : contextMap.values()) { @@ -240,28 +242,34 @@ public void close() { this.collectionRegistry.clear(); this.repositoryRegistry.clear(); this.undoRegistry.clear(); - this.transactionalStore.close(); + this.transactionStore.close(); + this.transactionConfig.close(); } catch (Exception e) { throw new TransactionException("transaction failed to close", e); } } + @Override + public synchronized State getState() { + return state; + } + private void prepare() { - this.contextMap = new HashMap<>(); - this.collectionRegistry = new HashMap<>(); - this.repositoryRegistry = new HashMap<>(); - this.undoRegistry = new HashMap<>(); + this.contextMap = new ConcurrentHashMap<>(); + this.collectionRegistry = new ConcurrentHashMap<>(); + this.repositoryRegistry = new ConcurrentHashMap<>(); + this.undoRegistry = new ConcurrentHashMap<>(); this.id = UUID.randomUUID().toString(); NitriteStore nitriteStore = nitrite.getStore(); NitriteConfig nitriteConfig = nitrite.getConfig(); - this.transactionalConfig = new TransactionalConfig(nitriteConfig); - this.transactionalConfig.loadModule(NitriteModule.module(new TransactionalStore<>(nitriteStore))); + this.transactionConfig = new TransactionConfig(nitriteConfig); + this.transactionConfig.loadModule(NitriteModule.module(new TransactionStore<>(nitriteStore))); - this.transactionalConfig.autoConfigure(); - this.transactionalConfig.initialize(); - this.transactionalStore = (TransactionalStore) this.transactionalConfig.getNitriteStore(); + this.transactionConfig.autoConfigure(); + this.transactionConfig.initialize(); + this.transactionStore = (TransactionStore) this.transactionConfig.getNitriteStore(); this.state = State.Active; } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalConfig.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java similarity index 93% rename from nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalConfig.java rename to nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java index ea9f6b391..476033470 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalConfig.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java @@ -13,10 +13,10 @@ * @since 4.0 */ @Slf4j -class TransactionalConfig extends NitriteConfig { +class TransactionConfig extends NitriteConfig { private final NitriteConfig config; - public TransactionalConfig(NitriteConfig config) { + public TransactionConfig(NitriteConfig config) { super(); this.config = config; } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionContext.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionContext.java index 31ce4b7a7..01b0fb0a5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionContext.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionContext.java @@ -17,7 +17,7 @@ class TransactionContext implements AutoCloseable { private String collectionName; private Queue journal; private NitriteMap nitriteMap; - private TransactionalConfig config; + private TransactionConfig config; private AtomicBoolean active; public TransactionContext() { diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalStore.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java similarity index 95% rename from nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalStore.java rename to nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java index d9478add4..73d0e1e0a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java @@ -12,12 +12,12 @@ * @author Anindya Chatterjee * @since 4.0 */ -class TransactionalStore extends AbstractNitriteStore { +class TransactionStore extends AbstractNitriteStore { private final NitriteStore primaryStore; private final Map> mapRegistry; private final Map> rTreeRegistry; - public TransactionalStore(NitriteStore store) { + public TransactionStore(NitriteStore store) { this.primaryStore = store; this.mapRegistry = new ConcurrentHashMap<>(); this.rTreeRegistry = new ConcurrentHashMap<>(); @@ -49,7 +49,7 @@ public void commit() { } @Override - public void close() throws Exception { + public void close() { for (NitriteMap nitriteMap : mapRegistry.values()) { nitriteMap.close(); } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalCollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalCollectionTest.java index e1c5386ff..c09fcc21d 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalCollectionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalCollectionTest.java @@ -27,37 +27,37 @@ public class DefaultTransactionalCollectionTest { @Test public void testConstructor() { - TransactionalConfig transactionalConfig = mock(TransactionalConfig.class); - when(transactionalConfig.getNitriteStore()).thenThrow(new NotIdentifiableException("An error occurred")); + TransactionConfig transactionConfig = mock(TransactionConfig.class); + when(transactionConfig.getNitriteStore()).thenThrow(new NotIdentifiableException("An error occurred")); TransactionContext transactionContext = new TransactionContext(); - transactionContext.setConfig(transactionalConfig); + transactionContext.setConfig(transactionConfig); assertThrows(NotIdentifiableException.class, () -> new DefaultTransactionalCollection(null, transactionContext, null)); - verify(transactionalConfig).getNitriteStore(); + verify(transactionConfig).getNitriteStore(); } @Test public void testConstructor2() { - TransactionalConfig transactionalConfig = mock(TransactionalConfig.class); - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(new InMemoryStore()))); + TransactionConfig transactionConfig = mock(TransactionConfig.class); + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(new InMemoryStore()))); - doReturn(transactionalStore).when(transactionalConfig).getNitriteStore(); + doReturn(transactionStore).when(transactionConfig).getNitriteStore(); TransactionContext transactionContext = new TransactionContext(); - transactionContext.setConfig(transactionalConfig); + transactionContext.setConfig(transactionConfig); DefaultTransactionalCollection actualDefaultTransactionalCollection = new DefaultTransactionalCollection(null, transactionContext, null); assertNull(actualDefaultTransactionalCollection.getCollectionName()); assertFalse(actualDefaultTransactionalCollection.isDropped()); TransactionContext transactionContext1 = actualDefaultTransactionalCollection.getTransactionContext(); assertSame(transactionContext, transactionContext1); - assertSame(transactionalStore, actualDefaultTransactionalCollection.getStore()); + assertSame(transactionStore, actualDefaultTransactionalCollection.getStore()); assertNull(actualDefaultTransactionalCollection.getPrimary()); assertNull(actualDefaultTransactionalCollection.getNitrite()); assertNull(actualDefaultTransactionalCollection.getNitriteMap()); assertNull(actualDefaultTransactionalCollection.getCollectionOperations().getAttributes()); - verify(transactionalConfig, times(2)).getNitriteStore(); + verify(transactionConfig, times(2)).getNitriteStore(); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java index ad9947681..1845b6a87 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java @@ -243,8 +243,8 @@ public void testSize() { public void testGetStore() { DefaultTransactionalRepository defaultTransactionalRepository = (DefaultTransactionalRepository) mock( DefaultTransactionalRepository.class); - doReturn(new TransactionalStore<>(new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null))))).when(defaultTransactionalRepository).getStore(); + doReturn(new TransactionStore<>(new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null))))).when(defaultTransactionalRepository).getStore(); defaultTransactionalRepository.getStore(); verify(defaultTransactionalRepository).getStore(); } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/NitriteTransactionTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/NitriteTransactionTest.java index 79e36d5f9..5e4c4c4be 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/NitriteTransactionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/NitriteTransactionTest.java @@ -31,7 +31,7 @@ public class NitriteTransactionTest { public void testConstructor() { Nitrite nitrite = mock(Nitrite.class); when(nitrite.getConfig()).thenReturn(new NitriteConfig()); - doReturn(new TransactionalStore<>(new InMemoryStore())).when(nitrite).getStore(); + doReturn(new TransactionStore<>(new InMemoryStore())).when(nitrite).getStore(); assertEquals(State.Active, (new NitriteTransaction(nitrite, new LockService())).getState()); verify(nitrite).getConfig(); verify(nitrite).getStore(); diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalConfigTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionConfigTest.java similarity index 63% rename from nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalConfigTest.java rename to nitrite/src/test/java/org/dizitart/no2/transaction/TransactionConfigTest.java index 397a18f91..82c83c549 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalConfigTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionConfigTest.java @@ -29,35 +29,35 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; -public class TransactionalConfigTest { +public class TransactionConfigTest { @Test public void testConstructor() { - TransactionalConfig actualTransactionalConfig = new TransactionalConfig(new NitriteConfig()); - assertTrue(actualTransactionalConfig.getMigrations().isEmpty()); - assertEquals(1, actualTransactionalConfig.getSchemaVersion().intValue()); - assertNull(actualTransactionalConfig.getNitriteStore()); + TransactionConfig actualTransactionConfig = new TransactionConfig(new NitriteConfig()); + assertTrue(actualTransactionConfig.getMigrations().isEmpty()); + assertEquals(1, actualTransactionConfig.getSchemaVersion().intValue()); + assertNull(actualTransactionConfig.getNitriteStore()); } @Test public void testFindIndexer() { assertThrows(IndexingException.class, - () -> (new TransactionalConfig(new NitriteConfig())).findIndexer("Index Type")); + () -> (new TransactionConfig(new NitriteConfig())).findIndexer("Index Type")); } @Test public void testLoadModule() { - TransactionalConfig transactionalConfig = new TransactionalConfig(new NitriteConfig()); + TransactionConfig transactionConfig = new TransactionConfig(new NitriteConfig()); NitriteModule nitriteModule = mock(NitriteModule.class); when(nitriteModule.plugins()).thenReturn(new HashSet<>()); - assertSame(transactionalConfig, transactionalConfig.loadModule(nitriteModule)); + assertSame(transactionConfig, transactionConfig.loadModule(nitriteModule)); verify(nitriteModule, times(2)).plugins(); } @Test public void testAutoConfigure() { - TransactionalConfig transactionalConfig = new TransactionalConfig(new NitriteConfig()); - transactionalConfig.autoConfigure(); - NitriteStore nitriteStore = transactionalConfig.getNitriteStore(); + TransactionConfig transactionConfig = new TransactionConfig(new NitriteConfig()); + transactionConfig.autoConfigure(); + NitriteStore nitriteStore = transactionConfig.getNitriteStore(); assertTrue(nitriteStore instanceof org.dizitart.no2.store.memory.InMemoryStore); assertFalse(nitriteStore.isClosed()); assertTrue(((InMemoryConfig) nitriteStore.getStoreConfig()).eventListeners().isEmpty()); @@ -65,12 +65,12 @@ public void testAutoConfigure() { @Test public void testNitriteMapper() { - assertNull((new TransactionalConfig(new NitriteConfig())).nitriteMapper()); + assertNull((new TransactionConfig(new NitriteConfig())).nitriteMapper()); } @Test public void testGetNitriteStore() { - assertNull((new TransactionalConfig(new NitriteConfig())).getNitriteStore()); + assertNull((new TransactionConfig(new NitriteConfig())).getNitriteStore()); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionContextTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionContextTest.java index d5a12e2b6..d2abfaeb0 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionContextTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionContextTest.java @@ -43,9 +43,9 @@ public void testClose() throws Exception { TransactionContext transactionContext = new TransactionContext(); TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); transactionContext .setNitriteMap(new TransactionalMap<>("Map Name", primary2, new InMemoryStore())); transactionContext.setJournal(journalEntryList); diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionStoreTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionStoreTest.java new file mode 100644 index 000000000..914bba6dd --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionStoreTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.transaction; + +import org.dizitart.no2.NitriteConfig; +import org.dizitart.no2.exceptions.InvalidOperationException; +import org.dizitart.no2.store.NitriteStore; +import org.dizitart.no2.store.StoreConfig; +import org.dizitart.no2.store.events.StoreEventListener; +import org.dizitart.no2.store.memory.InMemoryConfig; +import org.dizitart.no2.store.memory.InMemoryRTree; +import org.dizitart.no2.store.memory.InMemoryStore; +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class TransactionStoreTest { + @Test + public void testCommit() { + assertThrows(InvalidOperationException.class, + () -> (new TransactionStore<>(new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null))))).commit()); + } + + @Test + public void testClose() throws Exception { + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(new InMemoryStore())))); + transactionStore.close(); + assertNotNull(transactionStore.getCatalog()); + } + + @Test + public void testHasMap() { + NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); + when(nitriteStore.hasMap(anyString())).thenReturn(true); + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(nitriteStore))); + assertTrue(transactionStore.hasMap("Map Name")); + verify(nitriteStore).hasMap(anyString()); + assertNull(transactionStore.getStoreVersion()); + } + + @Test + public void testHasMap2() { + NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); + when(nitriteStore.hasMap(anyString())).thenReturn(false); + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(nitriteStore))); + assertFalse(transactionStore.hasMap("Map Name")); + verify(nitriteStore).hasMap(anyString()); + assertNull(transactionStore.getStoreVersion()); + } + + @Test + public void testOpenRTree() { + NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); + when(nitriteStore.openRTree(anyString(), any(), any())) + .thenReturn(new InMemoryRTree<>()); + when(nitriteStore.hasMap(anyString())).thenReturn(true); + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(nitriteStore))); + Class keyType = Object.class; + assertEquals(0L, transactionStore.openRTree("R Tree Name", keyType, Object.class).size()); + verify(nitriteStore, times(3)).hasMap(anyString()); + verify(nitriteStore).openRTree(anyString(), any(), any()); + assertNull(transactionStore.getStoreVersion()); + } + + @Test + public void testOpenRTree2() { + NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); + when(nitriteStore.openRTree(anyString(), any(), any())) + .thenReturn(new InMemoryRTree<>()); + when(nitriteStore.hasMap(anyString())).thenReturn(false); + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(nitriteStore))); + Class keyType = Object.class; + assertEquals(0L, transactionStore.openRTree("R Tree Name", keyType, Object.class).size()); + verify(nitriteStore).hasMap(anyString()); + assertNull(transactionStore.getStoreVersion()); + } + + @Test + public void testGetStoreVersion() { + NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); + when(nitriteStore.getStoreVersion()).thenReturn("foo"); + assertEquals("foo", (new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(nitriteStore)))).getStoreVersion()); + verify(nitriteStore).getStoreVersion(); + } + + @Test + public void testConstructor() { + TransactionStore transactionStore = new TransactionStore<>(new InMemoryStore()); + TransactionStore actualTransactionStore = new TransactionStore<>(transactionStore); + actualTransactionStore.initialize(new NitriteConfig()); + actualTransactionStore.openOrCreate(); + actualTransactionStore.subscribe(mock(StoreEventListener.class)); + actualTransactionStore.unsubscribe(mock(StoreEventListener.class)); + assertTrue(transactionStore.hasUnsavedChanges()); + assertTrue(actualTransactionStore.hasUnsavedChanges()); + assertFalse(transactionStore.isClosed()); + assertFalse(actualTransactionStore.isClosed()); + assertFalse(transactionStore.isReadOnly()); + assertFalse(actualTransactionStore.isReadOnly()); + } + + @Test + public void testConstructor2() { + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(new InMemoryStore())))); + TransactionStore actualTransactionStore = new TransactionStore<>(transactionStore); + assertNotNull(actualTransactionStore.getCatalog()); + assertFalse(actualTransactionStore.isReadOnly()); + assertFalse(actualTransactionStore.isClosed()); + assertTrue(actualTransactionStore.hasUnsavedChanges()); + assertNull(actualTransactionStore.getStoreConfig()); + assertNotNull(transactionStore.getCatalog()); + assertFalse(transactionStore.isReadOnly()); + assertFalse(transactionStore.isClosed()); + assertTrue(transactionStore.hasUnsavedChanges()); + assertNull(transactionStore.getStoreConfig()); + } +} + diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalMapTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalMapTest.java index 88c794f7b..4687b5e6a 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalMapTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalMapTest.java @@ -29,12 +29,12 @@ public class TransactionalMapTest { public void testContainsKey() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertFalse(transactionalMap.containsKey("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -43,13 +43,13 @@ public void testContainsKey() { public void testContainsKey2() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); transactionalMap.put("42", "42"); assertTrue(transactionalMap.containsKey("42")); assertEquals(1, transactionalMap.entries().toList().size()); @@ -59,13 +59,13 @@ public void testContainsKey2() { public void testContainsKey3() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); transactionalMap.put("foo", "42"); assertFalse(transactionalMap.containsKey("42")); assertEquals(1, transactionalMap.entries().toList().size()); @@ -75,12 +75,12 @@ public void testContainsKey3() { public void testGet() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertNull(transactionalMap.get("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -89,12 +89,12 @@ public void testGet() { public void testGet2() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertNull(transactionalMap.get(0)); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -103,9 +103,9 @@ public void testGet2() { public void testClear() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, new InMemoryStore()); transactionalMap.clear(); @@ -118,12 +118,12 @@ public void testClear() { public void testValues() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertTrue(transactionalMap.values().toList().isEmpty()); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -132,12 +132,12 @@ public void testValues() { public void testRemove() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertNull(transactionalMap.remove("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -145,11 +145,11 @@ public void testRemove() { @Test public void testRemove2() { InMemoryMap primary = new InMemoryMap<>("Map Name", - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertNull(transactionalMap.remove("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -158,13 +158,13 @@ public void testRemove2() { public void testRemove3() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); transactionalMap.put("42", "42"); assertEquals("42", transactionalMap.remove("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); @@ -175,13 +175,13 @@ public void testRemove3() { public void testRemove4() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); transactionalMap.put("", "42"); assertNull(transactionalMap.remove("42")); assertEquals(1, transactionalMap.entries().toList().size()); @@ -191,12 +191,12 @@ public void testRemove4() { public void testKeys() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertTrue(transactionalMap.keys().toList().isEmpty()); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -205,9 +205,9 @@ public void testKeys() { public void testPut() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, new InMemoryStore()); transactionalMap.put("42", "42"); @@ -220,12 +220,12 @@ public void testPut() { public void testSize() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertEquals(0L, transactionalMap.size()); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -234,9 +234,9 @@ public void testSize() { public void testPutIfAbsent() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, new InMemoryStore()); assertNull(transactionalMap.putIfAbsent("Key", "Value")); @@ -249,12 +249,12 @@ public void testPutIfAbsent() { public void testEntries() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); assertTrue((new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null))))).entries() + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null))))).entries() .toList() .isEmpty()); } @@ -263,12 +263,12 @@ public void testEntries() { public void testReversedEntries() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertTrue(transactionalMap.reversedEntries().toList().isEmpty()); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -277,12 +277,12 @@ public void testReversedEntries() { public void testHigherKey() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertNull(transactionalMap.higherKey("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -291,14 +291,14 @@ public void testHigherKey() { public void testHigherKey2() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap transactionalMap = new TransactionalMap<>(null, primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); transactionalMap.putIfAbsent("Key", "Value"); TransactionalMap transactionalMap1 = new TransactionalMap<>("Map Name", - transactionalMap, new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + transactionalMap, new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertEquals("Key", transactionalMap1.higherKey("42")); assertEquals(1, transactionalMap1.entries().toList().size()); } @@ -307,12 +307,12 @@ public void testHigherKey2() { public void testCeilingKey() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertNull(transactionalMap.ceilingKey("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -321,14 +321,14 @@ public void testCeilingKey() { public void testCeilingKey2() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap transactionalMap = new TransactionalMap<>(null, primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); transactionalMap.put("42", "42"); TransactionalMap transactionalMap1 = new TransactionalMap<>("Map Name", - transactionalMap, new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + transactionalMap, new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertEquals("42", transactionalMap1.ceilingKey("42")); assertEquals(1, transactionalMap1.entries().toList().size()); } @@ -337,12 +337,12 @@ public void testCeilingKey2() { public void testLowerKey() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertNull(transactionalMap.lowerKey("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -351,12 +351,12 @@ public void testLowerKey() { public void testFloorKey() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertNull(transactionalMap.floorKey("42")); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -365,14 +365,14 @@ public void testFloorKey() { public void testFloorKey2() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap transactionalMap = new TransactionalMap<>(null, primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); transactionalMap.put("42", "42"); TransactionalMap transactionalMap1 = new TransactionalMap<>("Map Name", - transactionalMap, new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + transactionalMap, new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertEquals("42", transactionalMap1.floorKey("42")); assertEquals(1, transactionalMap1.entries().toList().size()); } @@ -381,12 +381,12 @@ public void testFloorKey2() { public void testIsEmpty() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertTrue(transactionalMap.isEmpty()); assertTrue(transactionalMap.entries().toList().isEmpty()); } @@ -395,13 +395,13 @@ public void testIsEmpty() { public void testIsEmpty2() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); transactionalMap.put("42", "42"); assertFalse(transactionalMap.isEmpty()); assertEquals(1, transactionalMap.entries().toList().size()); @@ -411,14 +411,14 @@ public void testIsEmpty2() { public void testIsEmpty3() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap transactionalMap = new TransactionalMap<>("", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); transactionalMap.put("42", "42"); TransactionalMap transactionalMap1 = new TransactionalMap<>("Map Name", - transactionalMap, new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + transactionalMap, new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); assertFalse(transactionalMap1.isEmpty()); assertEquals(1, transactionalMap1.entries().toList().size()); } @@ -427,9 +427,9 @@ public void testIsEmpty3() { public void testDrop() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, new InMemoryStore()); transactionalMap.drop(); @@ -442,9 +442,9 @@ public void testDrop() { public void testClose() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, new InMemoryStore()); transactionalMap.close(); @@ -457,58 +457,58 @@ public void testClose() { public void testConstructor() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - transactionalStore); - TransactionalStore transactionalStore1 = new TransactionalStore<>( - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + transactionStore); + TransactionStore transactionStore1 = new TransactionStore<>( + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); TransactionalMap actualTransactionalMap = new TransactionalMap<>("Map Name", - transactionalMap, transactionalStore1); + transactionalMap, transactionStore1); assertEquals("Map Name", actualTransactionalMap.getName()); assertEquals("Map Name", transactionalMap.getName()); - assertSame(transactionalStore1, actualTransactionalMap.getStore()); - assertSame(transactionalStore, transactionalMap.getStore()); + assertSame(transactionStore1, actualTransactionalMap.getStore()); + assertSame(transactionStore, transactionalMap.getStore()); } @Test public void testConstructor2() { TransactionalMap primary = new TransactionalMap<>("Map Name", null, null); TransactionalMap primary1 = new TransactionalMap<>("Map Name", primary, - new TransactionalStore<>(null)); + new TransactionStore<>(null)); TransactionalMap primary2 = new TransactionalMap<>("Map Name", primary1, - new TransactionalStore<>(new TransactionalStore<>(null))); + new TransactionStore<>(new TransactionStore<>(null))); TransactionalMap transactionalMap = new TransactionalMap<>("Map Name", primary2, - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); TransactionalMap actualTransactionalMap = new TransactionalMap<>("Map Name", - transactionalMap, transactionalStore); + transactionalMap, transactionStore); assertTrue(actualTransactionalMap.entries().toList().isEmpty()); assertEquals(0L, actualTransactionalMap.size()); assertTrue(actualTransactionalMap.isEmpty()); - assertSame(transactionalStore, actualTransactionalMap.getStore()); + assertSame(transactionStore, actualTransactionalMap.getStore()); assertEquals("Map Name", actualTransactionalMap.getName()); assertTrue(transactionalMap.entries().toList().isEmpty()); } @Test public void testConstructor3() { - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null)))); + TransactionStore transactionStore = new TransactionStore<>( + new TransactionStore<>( + new TransactionStore<>(new TransactionStore<>(null)))); TransactionalMap actualTransactionalMap = new TransactionalMap<>("Map Name", null, - transactionalStore); + transactionStore); assertTrue(actualTransactionalMap.entries().toList().isEmpty()); assertEquals(0L, actualTransactionalMap.size()); assertTrue(actualTransactionalMap.isEmpty()); - assertSame(transactionalStore, actualTransactionalMap.getStore()); + assertSame(transactionStore, actualTransactionalMap.getStore()); assertEquals("Map Name", actualTransactionalMap.getName()); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalStoreTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalStoreTest.java deleted file mode 100644 index 85e413192..000000000 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalStoreTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.transaction; - -import org.dizitart.no2.NitriteConfig; -import org.dizitart.no2.exceptions.InvalidOperationException; -import org.dizitart.no2.store.NitriteStore; -import org.dizitart.no2.store.StoreConfig; -import org.dizitart.no2.store.events.StoreEventListener; -import org.dizitart.no2.store.memory.InMemoryConfig; -import org.dizitart.no2.store.memory.InMemoryRTree; -import org.dizitart.no2.store.memory.InMemoryStore; -import org.junit.Test; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - -public class TransactionalStoreTest { - @Test - public void testCommit() { - assertThrows(InvalidOperationException.class, - () -> (new TransactionalStore<>(new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(null))))).commit()); - } - - @Test - public void testClose() throws Exception { - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(new InMemoryStore())))); - transactionalStore.close(); - assertNotNull(transactionalStore.getCatalog()); - } - - @Test - public void testHasMap() { - NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); - when(nitriteStore.hasMap(anyString())).thenReturn(true); - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(nitriteStore))); - assertTrue(transactionalStore.hasMap("Map Name")); - verify(nitriteStore).hasMap(anyString()); - assertNull(transactionalStore.getStoreVersion()); - } - - @Test - public void testHasMap2() { - NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); - when(nitriteStore.hasMap(anyString())).thenReturn(false); - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(nitriteStore))); - assertFalse(transactionalStore.hasMap("Map Name")); - verify(nitriteStore).hasMap(anyString()); - assertNull(transactionalStore.getStoreVersion()); - } - - @Test - public void testOpenRTree() { - NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); - when(nitriteStore.openRTree(anyString(), any(), any())) - .thenReturn(new InMemoryRTree<>()); - when(nitriteStore.hasMap(anyString())).thenReturn(true); - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(nitriteStore))); - Class keyType = Object.class; - assertEquals(0L, transactionalStore.openRTree("R Tree Name", keyType, Object.class).size()); - verify(nitriteStore, times(3)).hasMap(anyString()); - verify(nitriteStore).openRTree(anyString(), any(), any()); - assertNull(transactionalStore.getStoreVersion()); - } - - @Test - public void testOpenRTree2() { - NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); - when(nitriteStore.openRTree(anyString(), any(), any())) - .thenReturn(new InMemoryRTree<>()); - when(nitriteStore.hasMap(anyString())).thenReturn(false); - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(nitriteStore))); - Class keyType = Object.class; - assertEquals(0L, transactionalStore.openRTree("R Tree Name", keyType, Object.class).size()); - verify(nitriteStore).hasMap(anyString()); - assertNull(transactionalStore.getStoreVersion()); - } - - @Test - public void testGetStoreVersion() { - NitriteStore nitriteStore = (NitriteStore) mock(NitriteStore.class); - when(nitriteStore.getStoreVersion()).thenReturn("foo"); - assertEquals("foo", (new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(nitriteStore)))).getStoreVersion()); - verify(nitriteStore).getStoreVersion(); - } - - @Test - public void testConstructor() { - TransactionalStore transactionalStore = new TransactionalStore<>(new InMemoryStore()); - TransactionalStore actualTransactionalStore = new TransactionalStore<>(transactionalStore); - actualTransactionalStore.initialize(new NitriteConfig()); - actualTransactionalStore.openOrCreate(); - actualTransactionalStore.subscribe(mock(StoreEventListener.class)); - actualTransactionalStore.unsubscribe(mock(StoreEventListener.class)); - assertTrue(transactionalStore.hasUnsavedChanges()); - assertTrue(actualTransactionalStore.hasUnsavedChanges()); - assertFalse(transactionalStore.isClosed()); - assertFalse(actualTransactionalStore.isClosed()); - assertFalse(transactionalStore.isReadOnly()); - assertFalse(actualTransactionalStore.isReadOnly()); - } - - @Test - public void testConstructor2() { - TransactionalStore transactionalStore = new TransactionalStore<>( - new TransactionalStore<>( - new TransactionalStore<>(new TransactionalStore<>(new InMemoryStore())))); - TransactionalStore actualTransactionalStore = new TransactionalStore<>(transactionalStore); - assertNotNull(actualTransactionalStore.getCatalog()); - assertFalse(actualTransactionalStore.isReadOnly()); - assertFalse(actualTransactionalStore.isClosed()); - assertTrue(actualTransactionalStore.hasUnsavedChanges()); - assertNull(actualTransactionalStore.getStoreConfig()); - assertNotNull(transactionalStore.getCatalog()); - assertFalse(transactionalStore.isReadOnly()); - assertFalse(transactionalStore.isClosed()); - assertTrue(transactionalStore.hasUnsavedChanges()); - assertNull(transactionalStore.getStoreConfig()); - } -} - From 43fb4a0f2f780c5fa7873ff1f41bf7b1c3719e95 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 2 Jul 2021 01:41:22 +0530 Subject: [PATCH 05/78] bug fix --- .../dizitart/no2/mvstore/MVStoreModule.java | 13 +++------ .../dizitart/no2/mvstore/NitriteMVMap.java | 29 ++++++++++++++----- .../dizitart/no2/mvstore/NitriteMVStore.java | 10 +++++-- .../org/dizitart/no2/rocksdb/RocksDBMap.java | 28 +++++++++++++----- .../dizitart/no2/rocksdb/RocksDBModule.java | 11 ++----- .../dizitart/no2/rocksdb/RocksDBStore.java | 6 +++- .../no2/store/memory/InMemoryMap.java | 18 +++++++++--- .../no2/store/memory/InMemoryStore.java | 1 + .../no2/store/memory/InMemoryStoreModule.java | 12 +++----- .../no2/transaction/TransactionStore.java | 1 + .../no2/transaction/TransactionalMap.java | 23 ++++++++++++--- .../no2/common/module/PluginManagerTest.java | 3 +- 12 files changed, 103 insertions(+), 52 deletions(-) diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreModule.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreModule.java index 595d535ce..dc72c0d22 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreModule.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreModule.java @@ -34,8 +34,6 @@ * @author Anindya Chatterjee */ public class MVStoreModule implements StoreModule { - private NitriteStore nitriteStore; - @Setter(AccessLevel.PACKAGE) private MVStoreConfig storeConfig; @@ -46,7 +44,7 @@ public MVStoreModule(String path) { @Override public Set plugins() { - return setOf(nitriteStore); + return setOf(getStore()); } public static MVStoreModuleBuilder withConfig() { @@ -54,11 +52,8 @@ public static MVStoreModuleBuilder withConfig() { } public NitriteStore getStore() { - if (nitriteStore == null) { - NitriteMVStore store = new NitriteMVStore(); - store.setStoreConfig(storeConfig); - nitriteStore = store; - } - return nitriteStore; + NitriteMVStore store = new NitriteMVStore(); + store.setStoreConfig(storeConfig); + return store; } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVMap.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVMap.java index 47afe206f..08703bccc 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVMap.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVMap.java @@ -16,8 +16,8 @@ package org.dizitart.no2.mvstore; -import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.common.RecordStream; +import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; import org.h2.mvstore.MVMap; @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -36,11 +37,15 @@ class NitriteMVMap implements NitriteMap { private final MVMap mvMap; private final NitriteStore nitriteStore; private final MVStore mvStore; + private final AtomicBoolean droppedFlag; + private final AtomicBoolean closedFlag; NitriteMVMap(MVMap mvMap, NitriteStore nitriteStore) { this.mvMap = mvMap; this.nitriteStore = nitriteStore; this.mvStore = mvMap.getStore(); + this.closedFlag = new AtomicBoolean(false); + this.droppedFlag = new AtomicBoolean(false); } @Override @@ -176,17 +181,25 @@ public boolean isEmpty() { @Override public void drop() { - MVStore.TxCounter txCounter = mvStore.registerVersionUsage(); - try { - nitriteStore.closeMap(getName()); - nitriteStore.removeMap(getName()); - } finally { - mvStore.deregisterVersionUsage(txCounter); + if (!droppedFlag.get()) { + droppedFlag.compareAndSet(false, true); + closedFlag.compareAndSet(false, true); + + MVStore.TxCounter txCounter = mvStore.registerVersionUsage(); + try { + nitriteStore.closeMap(getName()); + nitriteStore.removeMap(getName()); + } finally { + mvStore.deregisterVersionUsage(txCounter); + } } } @Override public void close() { - nitriteStore.closeMap(getName()); + if (!closedFlag.get() && !droppedFlag.get()) { + closedFlag.compareAndSet(false, true); + nitriteStore.closeMap(getName()); + } } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java index dec0d85e1..d2203cee6 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java @@ -18,6 +18,7 @@ import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.index.BoundingBox; import org.dizitart.no2.store.AbstractNitriteStore; import org.dizitart.no2.store.NitriteMap; @@ -95,6 +96,7 @@ public void close() { mvStore.close(); alert(StoreEvents.Closed); + eventBus.close(); } @Override @@ -117,12 +119,16 @@ public NitriteMap openMap(String mapName, Class keyT @Override public void closeMap(String mapName) { - nitriteMapRegistry.remove(mapName); + if (!StringUtils.isNullOrEmpty(mapName)) { + nitriteMapRegistry.remove(mapName); + } } @Override public void closeRTree(String rTreeName) { - nitriteRTreeMapRegistry.remove(rTreeName); + if (!StringUtils.isNullOrEmpty(rTreeName)) { + nitriteRTreeMapRegistry.remove(rTreeName); + } } @Override diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java index 874c037da..0f045fab0 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java @@ -15,6 +15,7 @@ import org.rocksdb.util.BytewiseComparator; import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -24,7 +25,10 @@ public class RocksDBMap implements NitriteMap { private final String mapName; private final RocksDBReference reference; private final RocksDBStore store; + private AtomicLong size; + private AtomicBoolean droppedFlag; + private AtomicBoolean closedFlag; private RocksDB rocksDB; private ObjectFormatter objectFormatter; @@ -331,20 +335,30 @@ public boolean isEmpty() { @Override public void drop() { - store.removeMap(getName()); - close(); + if (!droppedFlag.get()) { + droppedFlag.compareAndSet(false, true); + closedFlag.compareAndSet(false, true); + + store.closeMap(getName()); + store.removeMap(getName()); + } + } + + @Override + public void close() { + if (!closedFlag.get() && !droppedFlag.get()) { + closedFlag.compareAndSet(false, true); + store.closeMap(getName()); + } } private void initialize() { this.size = new AtomicLong(0); // just initialized + this.closedFlag = new AtomicBoolean(false); + this.droppedFlag = new AtomicBoolean(false); this.objectFormatter = store.getStoreConfig().objectFormatter(); this.columnFamilyHandle = reference.getOrCreateColumnFamily(getName()); this.rocksDB = reference.getRocksDB(); this.bytewiseComparator = this.reference.getDbComparator(); } - - @Override - public void close() { - store.closeMap(getName()); - } } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBModule.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBModule.java index 2f5251ae3..acca1b5c3 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBModule.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBModule.java @@ -11,8 +11,6 @@ import static org.dizitart.no2.common.util.Iterables.setOf; public class RocksDBModule implements StoreModule { - private NitriteStore nitriteStore; - @Setter(AccessLevel.PACKAGE) private RocksDBConfig storeConfig; @@ -31,11 +29,8 @@ public static RocksDBModuleBuilder withConfig() { } public NitriteStore getStore() { - if (nitriteStore == null) { - RocksDBStore store = new RocksDBStore(); - store.setStoreConfig(storeConfig); - nitriteStore = store; - } - return nitriteStore; + RocksDBStore store = new RocksDBStore(); + store.setStoreConfig(storeConfig); + return store; } } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java index 26fedd17d..20f3d0f6a 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java @@ -2,6 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.common.UnknownType; +import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteException; import org.dizitart.no2.exceptions.NitriteIOException; @@ -80,6 +81,7 @@ public void close() { } alert(StoreEvents.Closed); + eventBus.close(); } catch (Exception e) { log.error("Error while closing the database", e); throw new NitriteIOException("failed to close database", e); @@ -116,7 +118,9 @@ public NitriteMap openMap(String mapName, @Override public void closeMap(String mapName) { - nitriteMapRegistry.remove(mapName); + if (!StringUtils.isNullOrEmpty(mapName)) { + nitriteMapRegistry.remove(mapName); + } } @Override diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java index 9fa4d0096..89590c6e9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.NavigableMap; import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicBoolean; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -25,6 +26,8 @@ public class InMemoryMap implements NitriteMap { private final NavigableMap backingMap; private final NitriteStore nitriteStore; private final String mapName; + private final AtomicBoolean droppedFlag; + private final AtomicBoolean closedFlag; /** * Instantiates a new {@link InMemoryMap}. @@ -35,9 +38,11 @@ public class InMemoryMap implements NitriteMap { public InMemoryMap(String mapName, NitriteStore nitriteStore) { this.mapName = mapName; this.nitriteStore = nitriteStore; - this.backingMap = new ConcurrentSkipListMap<>((o1, o2) -> Comparables.compare((Comparable) o1, (Comparable) o2)); + + this.closedFlag = new AtomicBoolean(false); + this.droppedFlag = new AtomicBoolean(false); } @Override @@ -156,13 +161,18 @@ public boolean isEmpty() { @Override public void drop() { - clear(); - getStore().removeMap(mapName); + if (!droppedFlag.get()) { + droppedFlag.compareAndSet(false, true); + clear(); + getStore().removeMap(mapName); + } } @Override public void close() { - // nothing to close + if (!closedFlag.get() && !droppedFlag.get()) { + closedFlag.compareAndSet(false, true); + } } private RecordStream> getStream(NavigableMap primaryMap) { diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java index 7a50adb94..ccd3f0ee1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java @@ -73,6 +73,7 @@ public void close() { nitriteMapRegistry.clear(); nitriteRTreeMapRegistry.clear(); alert(StoreEvents.Closed); + eventBus.close(); } @Override diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStoreModule.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStoreModule.java index 867d84cfd..e32ac6c96 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStoreModule.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStoreModule.java @@ -17,7 +17,6 @@ * @since 4.0 */ public class InMemoryStoreModule implements StoreModule { - private NitriteStore nitriteStore; @Setter(AccessLevel.PACKAGE) private InMemoryConfig storeConfig; @@ -40,16 +39,13 @@ public static InMemoryModuleBuilder withConfig() { @Override public NitriteStore getStore() { - if (nitriteStore == null) { - InMemoryStore store = new InMemoryStore(); - store.setStoreConfig(storeConfig); - nitriteStore = store; - } - return nitriteStore; + InMemoryStore store = new InMemoryStore(); + store.setStoreConfig(storeConfig); + return store; } @Override public Set plugins() { - return setOf(nitriteStore); + return setOf(getStore()); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java index 73d0e1e0a..95ba2c7aa 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java @@ -60,6 +60,7 @@ public void close() { mapRegistry.clear(); rTreeRegistry.clear(); + eventBus.close(); } @Override diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java index fc6ba15d6..b3053c000 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java @@ -8,6 +8,7 @@ import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; import static org.dizitart.no2.common.util.ObjectUtils.deepCopy; @@ -22,6 +23,9 @@ class TransactionalMap implements NitriteMap { private final String mapName; private final NitriteStore store; private final Set tombstones; + private final AtomicBoolean droppedFlag; + private final AtomicBoolean closedFlag; + private boolean cleared = false; public TransactionalMap(String mapName, NitriteMap primary, NitriteStore store) { @@ -30,6 +34,8 @@ public TransactionalMap(String mapName, NitriteMap primary, NitriteStore(mapName, store); this.tombstones = new HashSet<>(); + this.closedFlag = new AtomicBoolean(false); + this.droppedFlag = new AtomicBoolean(false); } @Override @@ -277,14 +283,23 @@ public boolean isEmpty() { @Override public void drop() { - cleared = true; - close(); + if (!droppedFlag.get()) { + droppedFlag.compareAndSet(false, true); + closedFlag.compareAndSet(false, true); + + cleared = true; + backingMap.clear(); + tombstones.clear(); + } } @Override public void close() { - backingMap.clear(); - tombstones.clear(); + if (!closedFlag.get() && !droppedFlag.get()) { + closedFlag.compareAndSet(false, true); + backingMap.clear(); + tombstones.clear(); + } } private RecordStream> getStream(RecordStream> primaryStream, diff --git a/nitrite/src/test/java/org/dizitart/no2/common/module/PluginManagerTest.java b/nitrite/src/test/java/org/dizitart/no2/common/module/PluginManagerTest.java index 591e93492..ceb44e2d5 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/module/PluginManagerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/module/PluginManagerTest.java @@ -18,6 +18,7 @@ package org.dizitart.no2.common.module; import org.dizitart.no2.NitriteConfig; +import org.dizitart.no2.exceptions.PluginException; import org.dizitart.no2.store.NitriteStore; import org.junit.Test; @@ -42,7 +43,7 @@ public void testLoadModule() { verify(nitriteModule, times(2)).plugins(); } - @Test + @Test(expected = PluginException.class) public void testLoadModule2() { PluginManager pluginManager = new PluginManager(new NitriteConfig()); From b417c32867bd000cf0c692d3b98240fc8be99e53 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 2 Jul 2021 10:35:16 +0530 Subject: [PATCH 06/78] some more bug fixed --- .../org/dizitart/no2/mvstore/Recovery.java | 5 ++-- .../dizitart/no2/sync/net/DataGateSocket.java | 3 ++- .../no2/collection/SnowflakeIdGenerator.java | 27 +++++++++---------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java index 575f7f732..358184810 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java @@ -16,6 +16,7 @@ package org.dizitart.no2.mvstore; +import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.exceptions.NitriteIOException; import org.h2.mvstore.Chunk; import org.h2.mvstore.DataUtils; @@ -46,6 +47,7 @@ * @author Anindya Chatterjee * @since 1.0 */ +@Slf4j public class Recovery { /** * The block size (physical sector size) of the disk. The store header is @@ -322,8 +324,7 @@ c.id, formatTimestamp(created, fileCreated), } pw.printf("\n"); } catch (Exception e) { - pw.println("ERROR: " + e); - e.printStackTrace(pw); + log.error("Error while reading summary information", e); return e.getMessage(); } pw.flush(); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java index 2ff16c81d..3aa5e9c60 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java @@ -221,7 +221,8 @@ public java.security.cert.X509Certificate[] getAcceptedIssuers() { } }; - final SSLContext sslContext = SSLContext.getInstance("SSL"); + // SSLContext needs to be compatible with TLS 1.2 + final SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java b/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java index 097629475..9ef5c1b73 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java @@ -21,9 +21,9 @@ import java.net.NetworkInterface; import java.net.SocketException; +import java.security.SecureRandom; import java.util.Enumeration; import java.util.NoSuchElementException; -import java.util.Random; /** * Generate unique IDs using the Twitter Snowflake algorithm (see https://github.com/twitter/snowflake). Snowflake IDs @@ -44,15 +44,9 @@ */ @Slf4j public class SnowflakeIdGenerator { + private final SecureRandom random; private final long nodeIdBits = 10L; - private final long maxNodeId = ~(-1L << nodeIdBits); - private final long sequenceBits = 12L; - private final long nodeIdShift = sequenceBits; - private final long timestampLeftShift = sequenceBits + nodeIdBits; - private final long sequenceMask = ~(-1L << sequenceBits); - - private final long twepoch = 1288834974657L; private long nodeId; private volatile long lastTimestamp = -1L; @@ -60,18 +54,18 @@ public class SnowflakeIdGenerator { public SnowflakeIdGenerator() { + random = new SecureRandom(); + long maxNodeId = ~(-1L << nodeIdBits); try { this.nodeId = getNodeId(); } catch (SocketException | NoSuchElementException | NullPointerException e) { log.warn("SNOWFLAKE: could not determine machine address; using random node id"); - Random rnd = new Random(); - this.nodeId = rnd.nextInt((int) maxNodeId) + 1; + this.nodeId = random.nextInt((int) maxNodeId) + 1; } if (this.nodeId > maxNodeId) { log.warn("SNOWFLAKE: nodeId > maxNodeId; using random node id"); - Random rnd = new Random(); - this.nodeId = rnd.nextInt((int) maxNodeId) + 1; + this.nodeId = random.nextInt((int) maxNodeId) + 1; } log.debug("SNOWFLAKE: initialised with node id {}", this.nodeId); } @@ -98,8 +92,7 @@ protected long getNodeId() throws SocketException { if (network != null) { byte[] mac = network.getHardwareAddress(); - Random rnd = new Random(); - byte rndByte = (byte) (rnd.nextInt() & 0x000000FF); + byte rndByte = (byte) (random.nextInt() & 0x000000FF); // take the last byte of the MAC address and a random byte as node id return ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) rndByte) << 8))) >> 6; @@ -123,7 +116,9 @@ public synchronized long getId() { } catch (InterruptedException ignore) { } } + long sequenceBits = 12L; if (lastTimestamp == timestamp) { + long sequenceMask = ~(-1L << sequenceBits); sequence = (sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tillNextMillis(lastTimestamp); @@ -132,7 +127,9 @@ public synchronized long getId() { sequence = 0; } lastTimestamp = timestamp; - long id = ((timestamp - twepoch) << timestampLeftShift) | (nodeId << nodeIdShift) | sequence; + long timestampLeftShift = sequenceBits + nodeIdBits; + long twepoch = 1288834974657L; + long id = ((timestamp - twepoch) << timestampLeftShift) | (nodeId << sequenceBits) | sequence; if (id < 0) { log.warn("Id is smaller than 0: {}", id); From 9bfc7e276c453422bc99901b8204fba999481a26 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sun, 4 Jul 2021 17:09:52 +0530 Subject: [PATCH 07/78] few test class added and multiple update added --- build.gradle | 10 -- .../dizitart/no2/mvstore/NitriteMVStore.java | 1 + .../dizitart/no2/integration/NitriteTest.java | 105 ++++++++++++++++++ .../collection/CollectionTest.java | 74 ++++++++++++ .../integration/DataGateIntegrationTest.java | 10 +- .../dizitart/no2/rocksdb/RocksDBStore.java | 1 + .../dizitart/no2/integration/NitriteTest.java | 105 ++++++++++++++++++ .../collection/CollectionTest.java | 74 ++++++++++++ .../collection/DefaultNitriteCollection.java | 9 +- .../collection/operation/WriteOperations.java | 13 ++- .../collection/operation/WriteResultImpl.java | 11 +- .../no2/common/PersistentCollection.java | 34 +++++- .../org/dizitart/no2/common/WriteResult.java | 5 +- .../org/dizitart/no2/store/StoreCatalog.java | 2 +- .../no2/store/memory/InMemoryStore.java | 1 + .../operation/WriteResultImplTest.java | 6 +- .../dizitart/no2/integration/NitriteTest.java | 12 +- .../collection/CollectionTest.java | 74 ++++++++++++ 18 files changed, 499 insertions(+), 48 deletions(-) create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java diff --git a/build.gradle b/build.gradle index 4c1b172cb..a1ba8f2e7 100644 --- a/build.gradle +++ b/build.gradle @@ -54,16 +54,6 @@ subprojects { mavenCentral() mavenLocal() } - - //Include JAXB dependencies if not included in JDK - if(JavaVersion.current() >= JavaVersion.VERSION_11) { - plugins.withType(JavaPlugin) { - dependencies { - testImplementation "javax.xml.bind:jaxb-api:2.3.1" - testImplementation "org.glassfish.jaxb:jaxb-runtime:3.0.1" - } - } - } } task clean(type: Delete) { diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java index d2203cee6..75d2462eb 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVStore.java @@ -135,6 +135,7 @@ public void closeRTree(String rTreeName) { public void removeMap(String name) { MVMap mvMap = mvStore.openMap(name); mvStore.removeMap(mvMap); + getCatalog().remove(name); nitriteMapRegistry.remove(name); } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java new file mode 100644 index 000000000..f5396fdfe --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import org.dizitart.no2.Nitrite; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.integration.repository.data.ClassA; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.dizitart.no2.integration.TestUtil.*; +import static org.junit.Assert.*; + +/** + * @author Anindya Chatterjee + */ +public class NitriteTest { + private final String fileName = getRandomTempDbFile(); + private Nitrite db; + + @Before + public void setUp() { + db = createDb(fileName); + NitriteCollection collection = db.getCollection("test"); + assertNotNull(collection); + } + + @After + public void cleanUp() { + if (db != null && !db.isClosed()) { + db.close(); + } + + deleteDb(fileName); + } + + @Test + public void testDestroyCollection() { + // close the db + db.close(); + + // reopen the db + db = createDb(fileName); + + // check if collection exists + // the collection is noty opened yet + db.hasCollection("test"); + + // destroy the collection + db.destroyCollection("test"); + + // collection should not be present in db + assertFalse(db.hasCollection("test")); + } + + @Test + public void testDestroyRepository() { + db.getRepository(ClassA.class); + + assertTrue(db.hasRepository(ClassA.class)); + + // close the db + db.close(); + + // reopen the db + db = createDb(fileName); + + db.destroyRepository(ClassA.class); + + assertFalse(db.hasRepository(ClassA.class)); + } + + @Test + public void testDestroyKeyedRepository() { + db.getRepository(ClassA.class, "test"); + + assertTrue(db.hasRepository(ClassA.class, "test")); + + // close the db + db.close(); + + // reopen the db + db = createDb(fileName); + + db.destroyRepository(ClassA.class, "test"); + + assertFalse(db.hasRepository(ClassA.class, "test")); + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java new file mode 100644 index 000000000..4da9d6ae6 --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.collection; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.exceptions.NitriteIOException; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author Anindya Chatterjee + */ +public class CollectionTest extends BaseCollectionTest { + + @Test + public void testGetName() { + assertEquals("test", collection.getName()); + } + + @Test + public void testDropCollection() { + // check if collection exists + // the collection is noty opened yet + db.hasCollection("test"); + + // destroy the collection + collection.drop(); + + // collection should not be present in db + assertFalse(db.hasCollection("test")); + + assertFalse(collection.isOpen()); + } + + @Test + public void testCloseConnection() { + collection.close(); + + assertFalse(collection.isOpen()); + } + + @Test(expected = NitriteIOException.class) + public void testDropAfterClose() { + collection.close(); + + assertFalse(collection.isOpen()); + + collection.drop(); + } + + @Test(expected = NitriteIOException.class) + public void testOperationAfterDrop() { + collection.drop(); + + collection.insert(Document.createDocument("test", "test")); + } +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index 8a1462dbb..d243415ac 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -62,8 +62,8 @@ public static void main(String[] args) { System.out.println("Token - " + jwt); Replica replica = Replica.builder() .of(collection) - .remote("wss://127.0.0.1:3030/ws/datagate/anidotnet@gmail.com/datagateIntegration") - .jwtAuth("anidotnet@gmail.com", jwt) + .remote("wss://127.0.0.1:3030/ws/datagate/abcd@gmail.com/datagateIntegration") + .jwtAuth("abcd@gmail.com", jwt) .acceptAllCertificates(true) .create(); @@ -98,7 +98,7 @@ public static void main(String[] args) { private static void createUser() throws Exception { OkHttpClient client = getUnsafeOkHttpClient(); Request request = new Request.Builder() - .url("https://127.0.0.1:3030/exists?email=anidotnet@gmail.com") + .url("https://127.0.0.1:3030/exists?email=abcd@gmail.com") .build(); Call call = client.newCall(request); @@ -113,7 +113,7 @@ private static void createUser() throws Exception { } String json = "{" + - "\"email\":\"anidotnet@gmail.com\"," + + "\"email\":\"abcd@gmail.com\"," + "\"password\":\"chang3me\"," + "\"firstName\":\"Anindya\"," + "\"lastName\":\"Chatterjee\"," + @@ -137,7 +137,7 @@ private static void createUser() throws Exception { private static String getToken() throws Exception { OkHttpClient client = getUnsafeOkHttpClient(); String json = "{" + - "\"email\":\"anidotnet@gmail.com\"," + + "\"email\":\"abcd@gmail.com\"," + "\"password\":\"chang3me\"}"; RequestBody body = RequestBody.create( MediaType.parse("application/json"), json); diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java index 20f3d0f6a..4199fbeb7 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java @@ -126,6 +126,7 @@ public void closeMap(String mapName) { @Override public void removeMap(String mapName) { reference.dropColumnFamily(mapName); + getCatalog().remove(mapName); nitriteMapRegistry.remove(mapName); } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java new file mode 100644 index 000000000..f5396fdfe --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import org.dizitart.no2.Nitrite; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.integration.repository.data.ClassA; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.dizitart.no2.integration.TestUtil.*; +import static org.junit.Assert.*; + +/** + * @author Anindya Chatterjee + */ +public class NitriteTest { + private final String fileName = getRandomTempDbFile(); + private Nitrite db; + + @Before + public void setUp() { + db = createDb(fileName); + NitriteCollection collection = db.getCollection("test"); + assertNotNull(collection); + } + + @After + public void cleanUp() { + if (db != null && !db.isClosed()) { + db.close(); + } + + deleteDb(fileName); + } + + @Test + public void testDestroyCollection() { + // close the db + db.close(); + + // reopen the db + db = createDb(fileName); + + // check if collection exists + // the collection is noty opened yet + db.hasCollection("test"); + + // destroy the collection + db.destroyCollection("test"); + + // collection should not be present in db + assertFalse(db.hasCollection("test")); + } + + @Test + public void testDestroyRepository() { + db.getRepository(ClassA.class); + + assertTrue(db.hasRepository(ClassA.class)); + + // close the db + db.close(); + + // reopen the db + db = createDb(fileName); + + db.destroyRepository(ClassA.class); + + assertFalse(db.hasRepository(ClassA.class)); + } + + @Test + public void testDestroyKeyedRepository() { + db.getRepository(ClassA.class, "test"); + + assertTrue(db.hasRepository(ClassA.class, "test")); + + // close the db + db.close(); + + // reopen the db + db = createDb(fileName); + + db.destroyRepository(ClassA.class, "test"); + + assertFalse(db.hasRepository(ClassA.class, "test")); + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java new file mode 100644 index 000000000..4da9d6ae6 --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.collection; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.exceptions.NitriteIOException; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author Anindya Chatterjee + */ +public class CollectionTest extends BaseCollectionTest { + + @Test + public void testGetName() { + assertEquals("test", collection.getName()); + } + + @Test + public void testDropCollection() { + // check if collection exists + // the collection is noty opened yet + db.hasCollection("test"); + + // destroy the collection + collection.drop(); + + // collection should not be present in db + assertFalse(db.hasCollection("test")); + + assertFalse(collection.isOpen()); + } + + @Test + public void testCloseConnection() { + collection.close(); + + assertFalse(collection.isOpen()); + } + + @Test(expected = NitriteIOException.class) + public void testDropAfterClose() { + collection.close(); + + assertFalse(collection.isOpen()); + + collection.drop(); + } + + @Test(expected = NitriteIOException.class) + public void testOperationAfterDrop() { + collection.drop(); + + collection.insert(Document.createDocument("test", "test")); + } +} diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 548592ae0..403feae0e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -455,14 +455,7 @@ private void initialize() { private void checkOpened() { if (isOpen()) return; - - if (isDropped) { - throw new NitriteIOException("collection has been dropped"); - } - - if (nitriteStore == null || nitriteStore.isClosed()) { - throw new NitriteIOException("store is closed"); - } + throw new NitriteIOException("collection is closed"); } private void validateRebuildIndex(IndexDescriptor indexDescriptor) { diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java index b010409a1..ca0727f86 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java @@ -17,20 +17,23 @@ package org.dizitart.no2.collection.operation; import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.collection.*; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.DocumentCursor; +import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.collection.UpdateOptions; import org.dizitart.no2.collection.events.CollectionEventInfo; import org.dizitart.no2.collection.events.CollectionEventListener; import org.dizitart.no2.collection.events.EventType; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.event.EventBus; +import org.dizitart.no2.common.processors.ProcessorChain; import org.dizitart.no2.exceptions.IndexingException; import org.dizitart.no2.exceptions.UniqueConstraintException; import org.dizitart.no2.filters.Filter; -import org.dizitart.no2.common.processors.ProcessorChain; import org.dizitart.no2.store.NitriteMap; -import java.util.HashSet; -import java.util.Set; +import java.util.ArrayList; +import java.util.List; import static org.dizitart.no2.common.Constants.*; @@ -58,7 +61,7 @@ class WriteOperations { } WriteResult insert(Document... documents) { - Set nitriteIds = new HashSet<>(documents.length); + List nitriteIds = new ArrayList<>(documents.length); log.debug("Total {} document(s) to be inserted in {}", documents.length, nitriteMap.getName()); for (Document document : documents) { diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteResultImpl.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteResultImpl.java index 284c8e768..9e8a1d219 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteResultImpl.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteResultImpl.java @@ -27,24 +27,19 @@ */ @ToString class WriteResultImpl implements WriteResult { - private Set nitriteIds; + private List nitriteIds; - void setNitriteIds(Set nitriteIds) { + void setNitriteIds(List nitriteIds) { this.nitriteIds = nitriteIds; } void addToList(NitriteId nitriteId) { if (nitriteIds == null) { - nitriteIds = new HashSet<>(); + nitriteIds = new ArrayList<>(); } nitriteIds.add(nitriteId); } - public int getAffectedCount() { - if (nitriteIds == null) return 0; - return nitriteIds.size(); - } - @Override public Iterator iterator() { return nitriteIds == null ? Collections.emptyIterator() diff --git a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java index 531cd66c0..601657824 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java @@ -22,14 +22,20 @@ import org.dizitart.no2.collection.events.EventAware; import org.dizitart.no2.collection.events.EventType; import org.dizitart.no2.collection.meta.MetadataAware; +import org.dizitart.no2.common.processors.Processor; +import org.dizitart.no2.common.util.Iterables; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.store.NitriteStore; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; + +import static org.dizitart.no2.common.util.ValidationUtils.containsNull; +import static org.dizitart.no2.common.util.ValidationUtils.notNull; /** * The interface Persistent collection. @@ -202,6 +208,32 @@ default WriteResult update(T element) { */ WriteResult update(T element, boolean insertIfAbsent); + + /** + * Updates {@code elements} in the collection. Specified {@code elements} must have an id. + * If the {@code elements} are not found in the collection, it will be inserted only if {@code insertIfAbsent} + * is set to {@code true}. + * + * @param elements the elements to update. + * @param insertIfAbsent if set to {@code true}, {@code elements} will be inserted if not found. + * @return the result of the update operation. + * @throws org.dizitart.no2.exceptions.ValidationException if the {@code elements} is {@code null}. + * @throws org.dizitart.no2.exceptions.NotIdentifiableException if the {@code elements} does not have any id field. + */ + default WriteResult update(T[] elements, boolean insertIfAbsent) { + notNull(elements, "a null element cannot be updated"); + containsNull(elements, "a null element cannot be updated"); + + List affectedIds = new ArrayList<>(); + + for (T element : elements) { + WriteResult writeResult = update(element, insertIfAbsent); + affectedIds.addAll(Iterables.toList(writeResult)); + } + + return affectedIds::iterator; + } + /** * Deletes the {@code element} from the collection. The {@code element} must have an id. * diff --git a/nitrite/src/main/java/org/dizitart/no2/common/WriteResult.java b/nitrite/src/main/java/org/dizitart/no2/common/WriteResult.java index fe13f34b8..737ad7025 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/WriteResult.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/WriteResult.java @@ -18,6 +18,7 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.util.Iterables; /** * An interface to represent the result of a modification operation @@ -34,6 +35,8 @@ public interface WriteResult extends Iterable { * * @return the affected document count. */ - int getAffectedCount(); + default int getAffectedCount() { + return (int) Iterables.size(this); + } } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java b/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java index 9f7cb43dc..106c8c849 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java @@ -34,7 +34,7 @@ * repositories and keyed-repositories. * * @author Anindya Chatterjee - * @since 4.0 + * @since 4.0.0 */ public class StoreCatalog { private final NitriteMap catalogMap; diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java index ccd3f0ee1..1568dee3e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java @@ -111,6 +111,7 @@ public void removeMap(String mapName) { if (nitriteMapRegistry.containsKey(mapName)) { nitriteMapRegistry.get(mapName).clear(); nitriteMapRegistry.remove(mapName); + getCatalog().remove(mapName); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/operation/WriteResultImplTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/operation/WriteResultImplTest.java index aded72221..364eaf515 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/operation/WriteResultImplTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/operation/WriteResultImplTest.java @@ -3,7 +3,7 @@ import org.dizitart.no2.collection.NitriteId; import org.junit.Test; -import java.util.HashSet; +import java.util.ArrayList; import static org.junit.Assert.assertEquals; @@ -11,7 +11,7 @@ public class WriteResultImplTest { @Test public void testSetNitriteIds() { WriteResultImpl writeResultImpl = new WriteResultImpl(); - writeResultImpl.setNitriteIds(new HashSet<>()); + writeResultImpl.setNitriteIds(new ArrayList<>()); assertEquals("WriteResultImpl(nitriteIds=[])", writeResultImpl.toString()); } @@ -25,7 +25,7 @@ public void testAddToList() { @Test public void testAddToList2() { WriteResultImpl writeResultImpl = new WriteResultImpl(); - writeResultImpl.setNitriteIds(new HashSet<>()); + writeResultImpl.setNitriteIds(new ArrayList<>()); writeResultImpl.addToList(NitriteId.newId()); assertEquals(1, writeResultImpl.getAffectedCount()); } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java index 9e698a829..76055bfd1 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -103,7 +103,7 @@ public void setUp() throws ParseException { } @After - public void tearDown() throws Exception { + public void tearDown() { if (collection.isOpen()) { collection.remove(ALL); collection.close(); @@ -202,28 +202,28 @@ public void testGetRepositoryInvalid() { } @Test(expected = NitriteIOException.class) - public void testGetCollectionNullStore() throws Exception { + public void testGetCollectionNullStore() { db = Nitrite.builder().openOrCreate(); db.close(); db.getCollection("test"); } @Test(expected = NitriteIOException.class) - public void testGetRepositoryNullStore() throws Exception { + public void testGetRepositoryNullStore() { db = Nitrite.builder().openOrCreate(); db.close(); db.getRepository(NitriteTest.class); } @Test(expected = NitriteIOException.class) - public void testGetKeyedRepositoryNullStore() throws Exception { + public void testGetKeyedRepositoryNullStore() { db = Nitrite.builder().openOrCreate(); db.close(); db.getRepository(NitriteTest.class, "key"); } @Test(expected = NitriteIOException.class) - public void testCommitNullStore() throws Exception { + public void testCommitNullStore() { db = Nitrite.builder().openOrCreate(); db.close(); db.commit(); @@ -352,7 +352,7 @@ public void run() { e.printStackTrace(); } } - }; + } Thread t0 = new Thread(new ThreadRunner()); Thread t1 = new Thread(new ThreadRunner()); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java new file mode 100644 index 000000000..4da9d6ae6 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.collection; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.exceptions.NitriteIOException; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * @author Anindya Chatterjee + */ +public class CollectionTest extends BaseCollectionTest { + + @Test + public void testGetName() { + assertEquals("test", collection.getName()); + } + + @Test + public void testDropCollection() { + // check if collection exists + // the collection is noty opened yet + db.hasCollection("test"); + + // destroy the collection + collection.drop(); + + // collection should not be present in db + assertFalse(db.hasCollection("test")); + + assertFalse(collection.isOpen()); + } + + @Test + public void testCloseConnection() { + collection.close(); + + assertFalse(collection.isOpen()); + } + + @Test(expected = NitriteIOException.class) + public void testDropAfterClose() { + collection.close(); + + assertFalse(collection.isOpen()); + + collection.drop(); + } + + @Test(expected = NitriteIOException.class) + public void testOperationAfterDrop() { + collection.drop(); + + collection.insert(Document.createDocument("test", "test")); + } +} From 04195e31915f997bcaba00e706c007f0ae1f2e9f Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sun, 4 Jul 2021 19:32:26 +0530 Subject: [PATCH 08/78] test fix --- .../no2/transaction/DefaultTransactionalRepositoryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java index 1845b6a87..efa732946 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java @@ -127,7 +127,7 @@ public void testUpdate() { DefaultTransactionalRepository.class); when(defaultTransactionalRepository.update(any(), anyBoolean())).thenReturn(null); defaultTransactionalRepository.update("Element", true); - verify(defaultTransactionalRepository).update(any(), anyBoolean()); + verify(defaultTransactionalRepository).update(any(Object.class), anyBoolean()); } @Test From a40ab721647d4383ba3c6a25137f86b8ee6b387a Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 28 Jul 2021 12:42:59 +0530 Subject: [PATCH 09/78] milestone 1 --- nitrite-android-example/build.gradle | 10 +- nitrite-replication/build.gradle | 4 +- .../no2/sync/BatchChangeScheduler.java | 101 ---- .../dizitart/no2/sync/BatchChangeSender.java | 142 ++++++ .../java/org/dizitart/no2/sync/Config.java | 9 +- .../org/dizitart/no2/sync/DataGateClient.java | 154 ++++++ .../org/dizitart/no2/sync/FeedJournal.java | 133 ------ .../org/dizitart/no2/sync/FeedLedger.java | 116 +++++ .../dizitart/no2/sync/MessageDispatcher.java | 161 ------- .../org/dizitart/no2/sync/MessageFactory.java | 27 +- .../dizitart/no2/sync/MessageTemplate.java | 100 ++-- .../dizitart/no2/sync/MessageTransformer.java | 2 +- .../java/org/dizitart/no2/sync/Replica.java | 100 ++-- .../org/dizitart/no2/sync/ReplicaBuilder.java | 118 ++++- .../no2/sync/ReplicaChangeListener.java | 102 ---- .../no2/sync/ReplicatedCollection.java | 148 ++++++ .../no2/sync/ReplicationTemplate.java | 202 -------- .../ConflictFreeReplicatedDataType.java} | 29 +- .../no2/sync/crdt/LastWriteWinMap.java | 54 ++- .../no2/sync/crdt/LastWriteWinState.java | 8 +- .../org/dizitart/no2/sync/crdt/Tombstone.java | 31 ++ .../sync/event/CollectionChangeListener.java | 67 +++ .../no2/sync/handlers/BatchAckHandler.java | 19 +- .../handlers/BatchChangeContinueHandler.java | 22 +- .../sync/handlers/BatchChangeEndHandler.java | 35 +- .../handlers/BatchChangeStartHandler.java | 22 +- .../no2/sync/handlers/BatchEndAckHandler.java | 17 +- .../no2/sync/handlers/ConnectAckHandler.java | 19 +- .../sync/handlers/DataGateFeedAckHandler.java | 23 +- .../sync/handlers/DataGateFeedHandler.java | 29 +- .../no2/sync/handlers/DisconnectHandler.java | 16 +- .../no2/sync/handlers/ErrorHandler.java | 12 +- .../no2/sync/handlers/MessageHandler.java | 3 +- .../no2/sync/handlers/ReceiptAckSender.java | 27 +- ...rnalAware.java => ReceiptLedgerAware.java} | 41 +- .../dizitart/no2/sync/message/BatchAck.java | 4 +- .../no2/sync/message/BatchChangeContinue.java | 4 +- .../no2/sync/message/BatchChangeEnd.java | 5 +- .../no2/sync/message/BatchChangeStart.java | 6 +- .../no2/sync/message/BatchEndAck.java | 4 +- .../no2/sync/message/MessageHeader.java | 2 + .../no2/sync/message/ReceiptAware.java | 8 +- .../no2/sync/message/TimeBoundMessage.java | 29 ++ .../dizitart/no2/sync/net/DataGateSocket.java | 237 +--------- .../net/{Status.java => WebSocketCode.java} | 18 +- .../dizitart/no2/{integration => }/Retry.java | 19 +- .../no2/{integration => }/TestUtils.java | 9 +- .../integration/DataGateIntegrationTest.java | 12 +- .../server/SimpleDataGateEndpoint.java | 440 ------------------ .../ReplicaNegativeTest.java | 40 +- .../{integration => mock}/ReplicaTest.java | 142 ++++-- .../no2/mock/server/MockDataGateEndpoint.java | 398 ++++++++++++++++ .../server/MockDataGateServer.java} | 14 +- .../server/MockRepository.java} | 18 +- .../mock/server/ServerLastWriteWinMap.java | 146 ++++++ .../no2/sync/message/BatchChangeEndTest.java | 8 +- .../src/test/resources/log4j2.xml | 37 -- .../src/test/resources/logback.xml | 31 ++ .../java/org/dizitart/no2/NitriteBuilder.java | 2 +- .../common/concurrent/ThreadPoolManager.java | 66 ++- 60 files changed, 1971 insertions(+), 1831 deletions(-) delete mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeScheduler.java create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java delete mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedJournal.java create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java delete mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageDispatcher.java delete mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaChangeListener.java create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java delete mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationTemplate.java rename nitrite-replication/src/main/java/org/dizitart/no2/sync/{ReplicationOperation.java => crdt/ConflictFreeReplicatedDataType.java} (85%) create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Tombstone.java create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/event/CollectionChangeListener.java rename nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/{JournalAware.java => ReceiptLedgerAware.java} (59%) create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/message/TimeBoundMessage.java rename nitrite-replication/src/main/java/org/dizitart/no2/sync/net/{Status.java => WebSocketCode.java} (59%) rename nitrite-replication/src/test/java/org/dizitart/no2/{integration => }/Retry.java (66%) rename nitrite-replication/src/test/java/org/dizitart/no2/{integration => }/TestUtils.java (94%) delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/server/SimpleDataGateEndpoint.java rename nitrite-replication/src/test/java/org/dizitart/no2/{integration => mock}/ReplicaNegativeTest.java (67%) rename nitrite-replication/src/test/java/org/dizitart/no2/{integration => mock}/ReplicaTest.java (82%) create mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java rename nitrite-replication/src/test/java/org/dizitart/no2/{integration/server/SimpleDataGateServer.java => mock/server/MockDataGateServer.java} (74%) rename nitrite-replication/src/test/java/org/dizitart/no2/{integration/server/Repository.java => mock/server/MockRepository.java} (76%) create mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java delete mode 100644 nitrite-replication/src/test/resources/log4j2.xml create mode 100644 nitrite-replication/src/test/resources/logback.xml diff --git a/nitrite-android-example/build.gradle b/nitrite-android-example/build.gradle index 2fa896985..d7f9ffbca 100644 --- a/nitrite-android-example/build.gradle +++ b/nitrite-android-example/build.gradle @@ -17,11 +17,11 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:4.2.0' } } @@ -29,7 +29,7 @@ apply plugin: 'com.android.application' repositories { google() - jcenter() + mavenCentral() } android { @@ -46,12 +46,12 @@ android { exclude 'META-INF/LGPL2.1' } - compileSdkVersion 28 + compileSdkVersion 30 defaultConfig { applicationId "org.dizitart.no2.example.android" minSdkVersion 19 - targetSdkVersion 28 + targetSdkVersion 30 versionCode 1 versionName "1.0" diff --git a/nitrite-replication/build.gradle b/nitrite-replication/build.gradle index af350a6fa..260ce041e 100644 --- a/nitrite-replication/build.gradle +++ b/nitrite-replication/build.gradle @@ -56,8 +56,8 @@ dependencies { testImplementation "org.glassfish.tyrus:tyrus-server:2.0.0" testImplementation "org.glassfish.tyrus:tyrus-container-grizzly-server:2.0.0" testImplementation "org.awaitility:awaitility:4.1.0" - testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.14.1" - testImplementation "org.apache.logging.log4j:log4j-core:2.14.1" + testImplementation "ch.qos.logback:logback-core:1.2.4" + testImplementation "ch.qos.logback:logback-classic:1.2.4" testImplementation "junit:junit:4.13.2" } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeScheduler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeScheduler.java deleted file mode 100644 index 2beb1adf9..000000000 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeScheduler.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.sync; - -import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.dizitart.no2.sync.message.BatchChangeContinue; -import org.dizitart.no2.sync.message.BatchChangeEnd; -import org.dizitart.no2.sync.message.BatchChangeStart; - -import java.util.Timer; -import java.util.TimerTask; - -/** - * @author Anindya Chatterjee - */ -class BatchChangeScheduler { - private Timer timer; - private final ReplicationTemplate replica; - private final MessageFactory factory; - private final MessageTemplate messageTemplate; - private final FeedJournal journal; - - public BatchChangeScheduler(ReplicationTemplate replica) { - this.replica = replica; - this.factory = replica.getMessageFactory(); - this.messageTemplate = replica.getMessageTemplate(); - this.journal = replica.getFeedJournal(); - } - - public void schedule() { - if (replica.isConnected()) { - Long lastSyncTime = replica.getLastSyncTime(); - - BatchChangeStart message = createStart(factory, lastSyncTime); - messageTemplate.sendMessage(message); - journal.write(message.getFeed()); - - timer = new Timer(); - timer.scheduleAtFixedRate(new TimerTask() { - boolean hasMore = true; - int start = replica.getConfig().getChunkSize(); - - @Override - public void run() { - LastWriteWinState state = replica.getCrdt().getChangesSince(lastSyncTime, start, - replica.getConfig().getChunkSize()); - if (state.getChanges().size() == 0 && state.getTombstones().size() == 0) { - hasMore = false; - } - - if (hasMore) { - BatchChangeContinue message = factory.createChangeContinue(replica.getConfig(), - replica.getReplicaId(), "", state); - - messageTemplate.sendMessage(message); - journal.write(state); - start = start + replica.getConfig().getChunkSize(); - } - - if (!hasMore) { - timer.cancel(); - } - } - }, 0, replica.getConfig().getDebounce()); - - BatchChangeEnd endMessage = factory.createChangeEnd(replica.getConfig(), replica.getReplicaId(), "", lastSyncTime); - messageTemplate.sendMessage(endMessage); - } - } - - public void stop() { - if (timer != null) { - timer.cancel(); - } - } - - private BatchChangeStart createStart(MessageFactory factory, Long lastSyncTime) { - BatchChangeStart startMessage = factory.createChangeStart(replica.getConfig(), - replica.getReplicaId(), ""); - - LastWriteWinState state = replica.getCrdt().getChangesSince(lastSyncTime, 0, - replica.getConfig().getChunkSize()); - - startMessage.setFeed(state); - return startMessage; - } -} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java new file mode 100644 index 000000000..99879e2a6 --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017-2020. Nitrite author or authors. + * + * 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 org.dizitart.no2.sync; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.WebSocket; +import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.message.*; + +/** + * @author Anindya Chatterjee + */ +@Slf4j +public class BatchChangeSender { + private final Config config; + private final ReplicatedCollection replicatedCollection; + private final DataGateClient dataGateClient; + + private boolean hasMore; + private int offset; + private State currentState; + private FeedLedger feedLedger; + private MessageFactory messageFactory; + private Long lastSyncTime; + private Long endTime; + + public BatchChangeSender(Config config, + ReplicatedCollection replicatedCollection, + DataGateClient dataGateClient) { + this.config = config; + this.replicatedCollection = replicatedCollection; + this.dataGateClient = dataGateClient; + configure(); + } + + public void sendAndReceive(WebSocket webSocket, String correlationId, Long checkpoint) { + if (endTime == null) { + endTime = checkpoint; + } + + switch (currentState) { + case ReadyToSend: + sendStartMessage(webSocket, correlationId); + break; + case StartSent: + sendChanges(webSocket, correlationId); + break; + case ChangesSent: + break; + } + } + + private void sendStartMessage(WebSocket webSocket, String correlationId) { + BatchChangeStart startMessage = messageFactory.createChangeStart(config, + replicatedCollection.getReplicaId(), correlationId); + startMessage.setStartTime(lastSyncTime); + startMessage.setEndTime(endTime); + + LastWriteWinState state = replicatedCollection.getChangesSince(lastSyncTime, endTime, + 0, config.getChunkSize()); + + startMessage.setFeed(state); + dataGateClient.sendMessage(webSocket, startMessage); + feedLedger.writeEntry(startMessage.getFeed()); + + offset += config.getChunkSize(); + currentState = State.StartSent; + } + + private void sendChanges(WebSocket webSocket, String correlationId) { + LastWriteWinState state = replicatedCollection.getChangesSince(lastSyncTime, endTime, + offset, config.getChunkSize()); + + if (state.getChangeSet().size() == 0 && state.getTombstoneMap().size() == 0) { + hasMore = false; + } + + if (hasMore) { + BatchChangeContinue message = messageFactory.createChangeContinue(config, + replicatedCollection.getReplicaId(), correlationId, state); + message.setStartTime(lastSyncTime); + message.setEndTime(endTime); + + dataGateClient.sendMessage(webSocket, message); + feedLedger.writeEntry(state); + offset += config.getChunkSize(); + } else { + Receipt finalReceipt = replicatedCollection.getFeedLedger().getFinalReceipt(); + if (replicatedCollection.shouldRetry(finalReceipt)) { + state = replicatedCollection.createState(finalReceipt); + BatchChangeContinue message = messageFactory.createChangeContinue(config, + replicatedCollection.getReplicaId(), correlationId, state); + + message.setStartTime(lastSyncTime); + message.setEndTime(endTime); + dataGateClient.sendMessage(webSocket, message); + feedLedger.writeEntry(state); + } else { + sendEndMessage(webSocket, correlationId); + currentState = State.ChangesSent; + } + } + } + + private void sendEndMessage(WebSocket webSocket, String correlationId) { + BatchChangeEnd endMessage = messageFactory.createChangeEnd(config, + replicatedCollection.getReplicaId(), correlationId); + endMessage.setStartTime(lastSyncTime); + endMessage.setEndTime(endTime); + dataGateClient.sendMessage(webSocket, endMessage); + } + + private void configure() { + this.feedLedger = replicatedCollection.getFeedLedger(); + this.messageFactory = new MessageFactory(); + this.hasMore = true; + this.offset = config.getChunkSize(); + this.currentState = State.ReadyToSend; + this.lastSyncTime = replicatedCollection.getLastSyncTime(); + this.endTime = null; + } + + private enum State { + ReadyToSend, + StartSent, + ChangesSent + } +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java index 89eba4e56..c1ce25b12 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java @@ -20,12 +20,16 @@ import lombok.Data; import okhttp3.Request; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.sync.event.ReplicationEventListener; import java.net.Proxy; -import java.util.concurrent.Callable; +import java.util.List; /** + * Represents the replication configuration + * * @author Anindya Chatterjee + * @since 4.0.0 */ @Data public class Config { @@ -39,5 +43,6 @@ public class Config { private Proxy proxy; private String authToken; private boolean acceptAllCertificates; - private Callable networkConnectivityChecker; + private List eventListeners; + private String replicaName; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java new file mode 100644 index 000000000..136dd4b90 --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.sync; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; +import org.dizitart.no2.common.util.StringUtils; +import org.dizitart.no2.sync.event.ReplicationEvent; +import org.dizitart.no2.sync.event.ReplicationEventBus; +import org.dizitart.no2.sync.event.ReplicationEventListener; +import org.dizitart.no2.sync.event.ReplicationEventType; +import org.dizitart.no2.sync.message.Connect; +import org.dizitart.no2.sync.message.DataGateMessage; +import org.dizitart.no2.sync.net.WebSocketCode; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +import static org.dizitart.no2.sync.event.ReplicationEventType.Started; +import static org.dizitart.no2.sync.event.ReplicationEventType.Stopped; + +/** + * @author Anindya Chatterjee + */ +@Slf4j +public class DataGateClient extends WebSocketListener { + private final Config config; + private final ReplicatedCollection replicatedCollection; + + private ReplicationEventBus eventBus; + private MessageFactory messageFactory; + private MessageTemplate messageTemplate; + private MessageTransformer transformer; + private WebSocket connectedWebsocket; + + public DataGateClient(Config config, ReplicatedCollection replicatedCollection) { + this.config = config; + this.replicatedCollection = replicatedCollection; + configure(); + } + + @Override + public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { + try { + if (!StringUtils.isNullOrEmpty(config.getReplicaName())) { + Thread.currentThread().setName(config.getReplicaName()); + } + + log.debug("Connection opened, sending Connect message"); + this.connectedWebsocket = webSocket; + String correlationId = UUID.randomUUID().toString(); + Connect message = messageFactory.createConnect(config, replicatedCollection.getReplicaId(), correlationId); + messageTemplate.postMessage(webSocket, message); + eventBus.post(new ReplicationEvent(Started)); + } catch (Exception e) { + log.error("Opening websocket failed", e); + eventBus.post(new ReplicationEvent(ReplicationEventType.Error, e)); + closeConnection(webSocket, "Error - " + e.getMessage()); + } + } + + @Override + public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { + try { + DataGateMessage message = transformer.transform(text); + log.debug("Received message from server " + message); + messageTemplate.validateMessage(message); + messageTemplate.dispatchMessage(webSocket, message); + } catch (Exception e) { + log.error("Error while processing message", e); + eventBus.post(new ReplicationEvent(ReplicationEventType.Error, e)); + + if (e instanceof ReplicationException) { + if (((ReplicationException) e).isFatal()) { + closeConnection(webSocket, "Error - " + e.getMessage()); + } + } + } + } + + @Override + public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { + log.error("Communication failure", t); + eventBus.post(new ReplicationEvent(ReplicationEventType.Error, t)); + closeConnection(webSocket,"Error - " + t.getMessage()); + } + + @Override + public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { + log.warn("Connection to server is closed due to {}", reason); + eventBus.post(new ReplicationEvent(Stopped)); + replicatedCollection.setConnected(false); + } + + public void sendMessage(WebSocket webSocket, M message) { + try { + if (replicatedCollection.isConnected()) { + log.debug("Sending message to server " + message); + messageTemplate.postMessage(webSocket, message); + } else { + throw new IllegalStateException("datagate client is not connected"); + } + } catch (Exception e) { + log.error("Failed to send message", e); + eventBus.post(new ReplicationEvent(ReplicationEventType.Error, e)); + closeConnection(webSocket, "Error - " + e.getMessage()); + } + } + + public void closeConnection(WebSocket webSocket, String reason) { + log.debug("Closing connection due to " + reason); + replicatedCollection.setConnected(false); + if (webSocket != null) { + webSocket.close(WebSocketCode.NORMAL_CLOSE, reason); + } else { + if (connectedWebsocket != null) { + connectedWebsocket.close(WebSocketCode.NORMAL_CLOSE, reason); + } + } + + eventBus.post(new ReplicationEvent(Stopped)); + } + + private void configure() { + eventBus = new ReplicationEventBus(); + messageFactory = new MessageFactory(); + messageTemplate = new MessageTemplate(config, replicatedCollection); + transformer = new MessageTransformer(config.getObjectMapper()); + + if (config.getEventListeners() != null) { + for (ReplicationEventListener eventListener : config.getEventListeners()) { + eventBus.register(eventListener); + } + } + } +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedJournal.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedJournal.java deleted file mode 100644 index a14e6715b..000000000 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedJournal.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.sync; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.meta.Attributes; -import org.dizitart.no2.common.util.StringUtils; -import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.dizitart.no2.sync.message.Receipt; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.locks.ReentrantLock; - -/** - * @author Anindya Chatterjee - */ -@Slf4j -public class FeedJournal { - private static final String JOURNAL = "journal"; - - private final ReplicationTemplate replicationTemplate; - private final ReentrantLock lock; - - public FeedJournal(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; - this.lock = new ReentrantLock(); - } - - public Receipt accumulate(Receipt receipt) { - try { - lock.lock(); - Receipt current = getCurrent(); - if (receipt != null && current != null) { - for (String id : receipt.getAdded()) { - current.getAdded().remove(id); - } - - for (String id : receipt.getRemoved()) { - current.getRemoved().remove(id); - } - } - setCurrent(current); - return current; - } finally { - lock.unlock(); - } - } - - public void write(LastWriteWinState state) { - try { - lock.lock(); - if (state != null) { - Receipt receipt = getCurrent(); - - Set changes = state.getChanges(); - if (changes != null && !changes.isEmpty()) { - for (Document change : changes) { - receipt.getAdded().add(change.getId().getIdValue()); - } - } - - Map tombstones = state.getTombstones(); - if (tombstones != null && !tombstones.isEmpty()) { - for (Map.Entry entry : tombstones.entrySet()) { - receipt.getRemoved().add(entry.getKey()); - } - } - - setCurrent(receipt); - } - } finally { - lock.unlock(); - } - } - - public Receipt getFinalReceipt() { - try { - lock.lock(); - return getCurrent(); - } finally { - lock.unlock(); - } - } - - private Receipt getCurrent() { - try { - Attributes attributes = replicationTemplate.getAttributes(); - String json = attributes.get(JOURNAL); - if (StringUtils.isNullOrEmpty(json)) { - return new Receipt(new HashSet<>(), new HashSet<>()); - } - - ObjectMapper objectMapper = replicationTemplate.getConfig().getObjectMapper(); - return objectMapper.readValue(json, Receipt.class); - } catch (JsonProcessingException e) { - log.error("Error while opening replica ledger", e); - throw new ReplicationException("failed to open replica ledger", e, false); - } - } - - private void setCurrent(Receipt receipt) { - try { - ObjectMapper objectMapper = replicationTemplate.getConfig().getObjectMapper(); - String json = objectMapper.writeValueAsString(receipt); - Attributes attributes = replicationTemplate.getAttributes(); - attributes.set(JOURNAL, json); - - replicationTemplate.saveAttributes(attributes); - } catch (JsonProcessingException e) { - log.error("Error while writing replica ledger", e); - throw new ReplicationException("failed to write replica ledger", e, false); - } - } -} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java new file mode 100644 index 000000000..22936c949 --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017-2020. Nitrite author or authors. + * + * 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 org.dizitart.no2.sync; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.util.StringUtils; +import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.message.Receipt; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @author Anindya Chatterjee + */ +@Slf4j +public class FeedLedger { + private static final String JOURNAL = "no2_feed_ledger"; + + private final Config config; + private final ReplicatedCollection replicatedCollection; + + public FeedLedger(Config config, ReplicatedCollection replicatedCollection) { + this.config = config; + this.replicatedCollection = replicatedCollection; + } + + public void writeOff(Receipt receipt) { + Receipt current = getCurrent(); + if (receipt != null && current != null) { + for (String id : receipt.getAdded()) { + current.getAdded().remove(id); + } + + for (String id : receipt.getRemoved()) { + current.getRemoved().remove(id); + } + } + setCurrent(current); + } + + public void writeEntry(LastWriteWinState state) { + if (state != null) { + Receipt receipt = getCurrent(); + + Set changes = state.getChangeSet(); + if (changes != null && !changes.isEmpty()) { + for (Document change : changes) { + receipt.getAdded().add(change.getId().getIdValue()); + } + } + + Map tombstones = state.getTombstoneMap(); + if (tombstones != null && !tombstones.isEmpty()) { + for (Map.Entry entry : tombstones.entrySet()) { + receipt.getRemoved().add(entry.getKey()); + } + } + + setCurrent(receipt); + } + } + + public Receipt getFinalReceipt() { + return getCurrent(); + } + + private Receipt getCurrent() { + try { + Attributes attributes = replicatedCollection.getAttributes(); + String json = attributes.get(JOURNAL); + if (StringUtils.isNullOrEmpty(json)) { + return new Receipt(new HashSet<>(), new HashSet<>()); + } + + ObjectMapper objectMapper = config.getObjectMapper(); + return objectMapper.readValue(json, Receipt.class); + } catch (JsonProcessingException e) { + log.error("Error while opening replica ledger", e); + throw new ReplicationException("failed to open replica ledger", e, false); + } + } + + private void setCurrent(Receipt receipt) { + try { + ObjectMapper objectMapper = config.getObjectMapper(); + String json = objectMapper.writeValueAsString(receipt); + Attributes attributes = replicatedCollection.getAttributes(); + attributes.set(JOURNAL, json); + + replicatedCollection.saveAttributes(attributes); + } catch (JsonProcessingException e) { + log.error("Error while writing replica ledger", e); + throw new ReplicationException("failed to write replica ledger", e, false); + } + } +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageDispatcher.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageDispatcher.java deleted file mode 100644 index a279130d4..000000000 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageDispatcher.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.sync; - -import lombok.extern.slf4j.Slf4j; -import okhttp3.Response; -import org.dizitart.no2.common.Constants; -import org.dizitart.no2.common.concurrent.ThreadPoolManager; -import org.dizitart.no2.common.util.StringUtils; -import org.dizitart.no2.sync.event.ReplicationEvent; -import org.dizitart.no2.sync.event.ReplicationEventType; -import org.dizitart.no2.sync.handlers.*; -import org.dizitart.no2.sync.message.DataGateMessage; -import org.dizitart.no2.sync.net.DataGateSocketListener; - -import java.util.concurrent.ExecutorService; - -/** - * @author Anindya Chatterjee - */ -@Slf4j -class MessageDispatcher implements DataGateSocketListener, AutoCloseable { - private final ReplicationTemplate replicationTemplate; - private final MessageTransformer transformer; - private ExecutorService executorService; - - public MessageDispatcher(Config config, ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; - this.transformer = new MessageTransformer(config.getObjectMapper()); - } - - @Override - public void onMessage(String text) { - try { - log.debug("Message received from server {}", text); - DataGateMessage message = transformer.transform(text); - validateMessage(message); - dispatch(message); - } catch (Exception e) { - log.error("Error while processing message", e); - replicationTemplate.postEvent(new ReplicationEvent(ReplicationEventType.Error, e)); - replicationTemplate.stopReplication("Error - " + e.getMessage()); - } - } - - @Override - public void onFailure(Throwable t, Response response) { - log.error("Communication failure", t); - replicationTemplate.postEvent(new ReplicationEvent(ReplicationEventType.Error, t)); - replicationTemplate.stopReplication("Error - " + t.getMessage()); - } - - @Override - public void onClosed(int code, String reason) { - log.warn("Connection to server is closed due to {}", reason); - } - - private void dispatch(M message) { - MessageHandler handler = findHandler(message); - if (handler != null) { - getExecutorService().submit(() -> { - try { - handler.handleMessage(message); - } catch (ReplicationException error) { - log.error("Error occurred while handling {} message", message.getHeader().getMessageType(), error); - if (error.isFatal()) { - replicationTemplate.postEvent(new ReplicationEvent(ReplicationEventType.Error, error)); - replicationTemplate.stopReplication("Error - " + error.getMessage()); - } - } catch (Exception e) { - log.error("Error occurred while handling {} message", message.getHeader().getMessageType(), e); - replicationTemplate.postEvent(new ReplicationEvent(ReplicationEventType.Error, e)); - replicationTemplate.stopReplication("Error - " + e.getMessage()); - } - }); - } - } - - @SuppressWarnings("unchecked") - private MessageHandler findHandler(DataGateMessage message) { - switch (message.getHeader().getMessageType()) { - case Error: - return (MessageHandler) new ErrorHandler(replicationTemplate); - case Connect: - // impossible case, server will never initiate connection - break; - case ConnectAck: - return (MessageHandler) new ConnectAckHandler(replicationTemplate); - case Disconnect: - return (MessageHandler) new DisconnectHandler(replicationTemplate); - case BatchChangeStart: - return (MessageHandler) new BatchChangeStartHandler(replicationTemplate); - case BatchChangeContinue: - return (MessageHandler) new BatchChangeContinueHandler(replicationTemplate); - case BatchChangeEnd: - return (MessageHandler) new BatchChangeEndHandler(replicationTemplate); - case BatchAck: - return (MessageHandler) new BatchAckHandler(replicationTemplate); - case BatchEndAck: - return (MessageHandler) new BatchEndAckHandler(replicationTemplate); - case DataGateFeed: - if (replicationTemplate.shouldExchangeFeed()) { - return (MessageHandler) new DataGateFeedHandler(replicationTemplate); - } - break; - case DataGateFeedAck: - if (replicationTemplate.shouldExchangeFeed()) { - return (MessageHandler) new DataGateFeedAckHandler(replicationTemplate); - } - break; - } - return null; - } - - private void validateMessage(DataGateMessage message) { - if (message == null) { - throw new ReplicationException("a null message is received for " - + replicationTemplate.getReplicaId(), true); - } else if (message.getHeader() == null) { - throw new ReplicationException("a message without header is received for " - + replicationTemplate.getReplicaId(), true); - } else if (StringUtils.isNullOrEmpty(message.getHeader().getCollection())) { - throw new ReplicationException("a message without collection info is received for " - + replicationTemplate.getReplicaId(), true); - } else if (message.getHeader().getMessageType() == null) { - throw new ReplicationException("a message without any type is received for " - + replicationTemplate.getReplicaId(), true); - } - } - - private ExecutorService getExecutorService() { - if (executorService == null - || executorService.isShutdown() - || executorService.isTerminated()) { - int core = Runtime.getRuntime().availableProcessors(); - executorService = ThreadPoolManager.getThreadPool(core, Constants.SYNC_THREAD_NAME); - } - return executorService; - } - - @Override - public void close() { - if (executorService != null) { - executorService.shutdown(); - } - } -} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java index a84239ef5..d80cdd8ca 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java @@ -25,55 +25,56 @@ * @author Anindya Chatterjee */ public class MessageFactory { - public Connect createConnect(Config config, String replicaId) { + public Connect createConnect(Config config, String replicaId, String correlationId) { Connect message = new Connect(); message.setHeader(createHeader(MessageType.Connect, config.getCollection().getName(), - "", replicaId, config.getUserName())); + correlationId, replicaId, config.getUserName())); message.setAuthToken(config.getAuthToken()); return message; } - public Disconnect createDisconnect(Config config, String replicaId) { + public Disconnect createDisconnect(Config config, String replicaId, String correlationId) { Disconnect message = new Disconnect(); message.setHeader(createHeader(MessageType.Disconnect, config.getCollection().getName(), - "", replicaId, config.getUserName())); + correlationId, replicaId, config.getUserName())); return message; } - public BatchChangeStart createChangeStart(Config config, String replicaId, String uuid) { + public BatchChangeStart createChangeStart(Config config, String replicaId, String correlationId) { BatchChangeStart message = new BatchChangeStart(); message.setHeader(createHeader(MessageType.BatchChangeStart, config.getCollection().getName(), - uuid, replicaId, config.getUserName())); + correlationId, replicaId, config.getUserName())); message.setBatchSize(config.getChunkSize()); message.setDebounce(config.getDebounce()); return message; } public BatchChangeContinue createChangeContinue(Config config, String replicaId, - String uuid, LastWriteWinState state) { + String correlationId, LastWriteWinState state) { BatchChangeContinue message = new BatchChangeContinue(); message.setHeader(createHeader(MessageType.BatchChangeContinue, config.getCollection().getName(), - uuid, replicaId, config.getUserName())); + correlationId, replicaId, config.getUserName())); message.setBatchSize(config.getChunkSize()); message.setDebounce(config.getDebounce()); message.setFeed(state); return message; } - public BatchChangeEnd createChangeEnd(Config config, String replicaId, String uuid, Long lastSyncTime) { + public BatchChangeEnd createChangeEnd(Config config, String replicaId, + String correlationId) { BatchChangeEnd message = new BatchChangeEnd(); message.setHeader(createHeader(MessageType.BatchChangeEnd, config.getCollection().getName(), - uuid, replicaId, config.getUserName())); + correlationId, replicaId, config.getUserName())); message.setBatchSize(config.getChunkSize()); message.setDebounce(config.getDebounce()); - message.setLastSynced(lastSyncTime); return message; } - public DataGateFeed createFeedMessage(Config config, String replicaId, LastWriteWinState state) { + public DataGateFeed createFeedMessage(Config config, String replicaId, + String correlationId, LastWriteWinState state) { DataGateFeed feed = new DataGateFeed(); feed.setHeader(createHeader(MessageType.DataGateFeed, config.getCollection().getName(), - "", replicaId, config.getUserName())); + correlationId, replicaId, config.getUserName())); feed.setFeed(state); return feed; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTemplate.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTemplate.java index 89e1f0062..c7c09e083 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTemplate.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTemplate.java @@ -1,79 +1,101 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.sync; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; +import okhttp3.WebSocket; +import org.dizitart.no2.common.util.StringUtils; +import org.dizitart.no2.sync.handlers.*; import org.dizitart.no2.sync.message.DataGateMessage; -import org.dizitart.no2.sync.net.DataGateSocket; /** * @author Anindya Chatterjee */ @Slf4j -public class MessageTemplate implements AutoCloseable { +public class MessageTemplate { private final Config config; - private final ReplicationTemplate replica; - private DataGateSocket dataGateSocket; - private MessageDispatcher dispatcher; + private final ReplicatedCollection replicatedCollection; - public MessageTemplate(Config config, ReplicationTemplate replica) { + public MessageTemplate(Config config, ReplicatedCollection replicatedCollection) { this.config = config; - this.replica = replica; + this.replicatedCollection = replicatedCollection; } - public void sendMessage(DataGateMessage message) { - if (dataGateSocket != null && dataGateSocket.isConnected()) { - if (!dataGateSocket.sendMessage(message)) { - throw new ReplicationException("failed to deliver message " + message, true); - } + public void validateMessage(DataGateMessage message) { + if (message == null) { + throw new ReplicationException("a null message is received for " + + replicatedCollection.getReplicaId(), true); + } else if (message.getHeader() == null) { + throw new ReplicationException("a message without header is received for " + + replicatedCollection.getReplicaId(), true); + } else if (StringUtils.isNullOrEmpty(message.getHeader().getCollection())) { + throw new ReplicationException("a message without collection info is received for " + + replicatedCollection.getReplicaId(), true); + } else if (message.getHeader().getMessageType() == null) { + throw new ReplicationException("a message without any type is received for " + + replicatedCollection.getReplicaId(), true); } } - public void openConnection() { - try { - dataGateSocket = new DataGateSocket(config); - dispatcher = new MessageDispatcher(config, replica); - - dataGateSocket.setListener(dispatcher); - dataGateSocket.startConnect(); - } catch (Exception e) { - log.error("Error while establishing connection from {}", getReplicaId(), e); - throw new ReplicationException("failed to open connection to server", e, true); + public void dispatchMessage(WebSocket webSocket, M message) { + MessageHandler handler = findHandler(message); + if (handler != null) { + handler.handleMessage(webSocket, message); } } - public void closeConnection(String reason) { - if (dataGateSocket != null) { - dataGateSocket.stopConnect(reason); + public void postMessage(WebSocket webSocket, M message) { + try { + String text = config.getObjectMapper().writeValueAsString(message); + log.debug("Sending message to datagate server {}", text); + webSocket.send(text); + } catch (JsonProcessingException e) { + throw new ReplicationException("malformed datagate message", e, true); } } - @Override - public void close() { - if (dataGateSocket != null) { - dataGateSocket.stopConnect("normal close"); + @SuppressWarnings("unchecked") + private MessageHandler findHandler(DataGateMessage message) { + switch (message.getHeader().getMessageType()) { + case Error: + return (MessageHandler) new ErrorHandler(); + case ConnectAck: + return (MessageHandler) new ConnectAckHandler(replicatedCollection); + case Disconnect: + return (MessageHandler) new DisconnectHandler(replicatedCollection); + case BatchChangeStart: + return (MessageHandler) new BatchChangeStartHandler(replicatedCollection); + case BatchChangeContinue: + return (MessageHandler) new BatchChangeContinueHandler(replicatedCollection); + case BatchChangeEnd: + return (MessageHandler) new BatchChangeEndHandler(replicatedCollection); + case BatchAck: + return (MessageHandler) new BatchAckHandler(replicatedCollection); + case BatchEndAck: + return (MessageHandler) new BatchEndAckHandler(replicatedCollection); + case DataGateFeed: + return (MessageHandler) new DataGateFeedHandler(replicatedCollection); + case DataGateFeedAck: + return (MessageHandler) new DataGateFeedAckHandler(replicatedCollection); + default: + break; } - - if (dispatcher != null) { - dispatcher.close(); - } - } - - private String getReplicaId() { - return replica.getReplicaId(); + return null; } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTransformer.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTransformer.java index 8e7b652f0..6a6618d52 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTransformer.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTransformer.java @@ -25,7 +25,7 @@ * @author Anindya Chatterjee */ public class MessageTransformer { - private ObjectMapper objectMapper; + private final ObjectMapper objectMapper; public MessageTransformer(ObjectMapper objectMapper) { this.objectMapper = objectMapper; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java index d8417fdd2..24c21813b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java @@ -1,35 +1,47 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.sync; -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.sync.event.ReplicationEvent; -import org.dizitart.no2.sync.event.ReplicationEventListener; -import org.dizitart.no2.sync.event.ReplicationEventType; +import org.dizitart.no2.common.concurrent.ThreadPoolManager; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import static org.dizitart.no2.common.Constants.SYNC_THREAD_NAME; /** + * Represents a remote replica of the local {@link org.dizitart.no2.collection.NitriteCollection} + * or {@link org.dizitart.no2.repository.ObjectRepository}. + * * @author Anindya Chatterjee + * @since 4.0.0 */ -@Slf4j -public final class Replica implements AutoCloseable { - private final ReplicationTemplate replicationTemplate; +public class Replica implements AutoCloseable { + private final Config config; + private final ReplicatedCollection replicatedCollection; + private ScheduledExecutorService scheduledExecutorService; + private ScheduledFuture replicationTask; - Replica(Config config) { - this.replicationTemplate = new ReplicationTemplate(config); + Replica(Config config, ReplicatedCollection replicatedCollection) { + this.config = config; + this.replicatedCollection = replicatedCollection; + configure(); } public static ReplicaBuilder builder() { @@ -37,50 +49,56 @@ public static ReplicaBuilder builder() { } public void connect() { - try { - replicationTemplate.connect(); - } catch (Exception e) { - log.error("Error while connecting the replica {}", getReplicaId(), e); - replicationTemplate.postEvent(new ReplicationEvent(ReplicationEventType.Error, e)); - if (e instanceof ReplicationException) { - throw e; + if (scheduledExecutorService != null) { + if (scheduledExecutorService.isShutdown() || scheduledExecutorService.isTerminated()) { + scheduledExecutorService = getSyncThreadPool(); } - throw new ReplicationException("failed to open connection", e, true); + + if (replicationTask == null || replicationTask.isCancelled()) { + replicationTask = scheduledExecutorService.scheduleAtFixedRate(() -> { + if (!isConnected()) { + replicatedCollection.startReplication(); + } + }, 0, config.getDebounce(), TimeUnit.MILLISECONDS); + } + } else { + throw new ReplicationException("replica is not configured properly", true); } } public void disconnect() { - try { - replicationTemplate.disconnect(); - } catch (Exception e) { - replicationTemplate.postEvent(new ReplicationEvent(ReplicationEventType.Error, e)); - log.error("Error while disconnecting the replica {}", getReplicaId(), e); - if (e instanceof ReplicationException) { - throw e; - } - throw new ReplicationException("failed to disconnect the replica", e, true); - } + disconnectInternal(false); } - public void subscribe(ReplicationEventListener listener) { - replicationTemplate.subscribe(listener); + public void disconnectNow() { + disconnectInternal(true); } - public void unsubscribe(ReplicationEventListener listener) { - replicationTemplate.unsubscribe(listener); + public boolean isConnected() { + return replicatedCollection.isConnected(); } - private String getReplicaId() { - return replicationTemplate.getReplicaId(); + public void close() { + replicatedCollection.stopReplication(null, "User close"); + ThreadPoolManager.shutdownThreadPool(scheduledExecutorService); } - public boolean isConnected() { - return replicationTemplate.isConnected(); + private void disconnectInternal(boolean mayInterruptIfRunning) { + if (scheduledExecutorService != null) { + if (!scheduledExecutorService.isShutdown() && !scheduledExecutorService.isTerminated()) { + replicationTask.cancel(mayInterruptIfRunning); + ThreadPoolManager.shutdownThreadPool(scheduledExecutorService); + } + } else { + throw new ReplicationException("replica is not configured properly", true); + } } - @Override - public void close() { - replicationTemplate.stopReplication("Normal shutdown"); - replicationTemplate.close(); + private void configure() { + this.scheduledExecutorService = getSyncThreadPool(); + } + + private static ScheduledExecutorService getSyncThreadPool() { + return ThreadPoolManager.getScheduledThreadPool(1, SYNC_THREAD_NAME); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java index 4572b1b3e..325631cd4 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java @@ -20,20 +20,25 @@ import okhttp3.Request; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.repository.ObjectRepository; +import org.dizitart.no2.sync.event.ReplicationEventListener; import org.dizitart.no2.sync.module.DocumentModule; import java.math.BigInteger; import java.net.Proxy; import java.nio.charset.StandardCharsets; -import java.util.concurrent.Callable; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; /** + * A builder api for creating a nitrite {@link Replica}. + * * @author Anindya Chatterjee. + * @since 4.0.0 */ public class ReplicaBuilder { private NitriteCollection collection; - private String replicationServer; + private String datagateServerUrl; private String authToken; private TimeSpan timeout; private TimeSpan debounce; @@ -42,77 +47,167 @@ public class ReplicaBuilder { private ObjectMapper objectMapper; private Proxy proxy; private boolean acceptAllCertificates = false; - private Callable networkConnectivityChecker = () -> true; + private final List eventListeners; + private String replicaName; + /** + * Instantiates a new {@link ReplicaBuilder}. + */ ReplicaBuilder() { chunkSize = 10; timeout = new TimeSpan(5, TimeUnit.SECONDS); debounce = new TimeSpan(1, TimeUnit.SECONDS); objectMapper = new ObjectMapper(); objectMapper.registerModule(new DocumentModule()); + eventListeners = new ArrayList<>(); } + /** + * Creates a replica of a {@link NitriteCollection}. + * + * @param collection the collection + * @return the replica builder + */ public ReplicaBuilder of(NitriteCollection collection) { this.collection = collection; return this; } + /** + * Creates a replica of an {@link ObjectRepository}. + * + * @param repository the repository + * @return the replica builder + */ public ReplicaBuilder of(ObjectRepository repository) { return of(repository.getDocumentCollection()); } - public ReplicaBuilder remote(String replicationServer) { - this.replicationServer = replicationServer; + /** + * Sets the remote datagate server url. + * + * @param datagateServerUrl the replication server + * @return the replica builder + */ + public ReplicaBuilder remote(String datagateServerUrl) { + this.datagateServerUrl = datagateServerUrl; return this; } + /** + * Sets the JWT auth token and user name. + * + * @param userName the user name + * @param authToken the auth token + * @return the replica builder + */ public ReplicaBuilder jwtAuth(String userName, String authToken) { this.authToken = authToken; this.userName = userName; return this; } + /** + * Sets the basic auth token. + * + * @param userName the user name + * @param password the password + * @return the replica builder + */ public ReplicaBuilder basicAuth(String userName, String password) { this.authToken = toHex(userName + ":" + password); this.userName = userName; return this; } + /** + * Sets the connection timeout. + * + * @param timeSpan the time span + * @return the replica builder + */ public ReplicaBuilder timeout(TimeSpan timeSpan) { this.timeout = timeSpan; return this; } + /** + * Sets the chunk size of changes that will be transmitted. + * + * @param size the size + * @return the replica builder + */ public ReplicaBuilder chunkSize(Integer size) { this.chunkSize = size; return this; } + /** + * Sets the debounce value. + * + * @param timeSpan the time span + * @return the replica builder + */ public ReplicaBuilder debounce(TimeSpan timeSpan) { this.debounce = timeSpan; return this; } + /** + * Sets the {@link ObjectMapper} instance. + * + * @param objectMapper the object mapper + * @return the replica builder + */ public ReplicaBuilder objectMapper(ObjectMapper objectMapper) { this.objectMapper = objectMapper; return this; } + /** + * Sets the proxy details. + * + * @param proxy the proxy + * @return the replica builder + */ public ReplicaBuilder proxy(Proxy proxy) { this.proxy = proxy; return this; } + /** + * Sets a flag to accept all certificates. + * + * @param accept the accept + * @return the replica builder + */ public ReplicaBuilder acceptAllCertificates(boolean accept) { this.acceptAllCertificates = accept; return this; } - public ReplicaBuilder networkConnectivityChecker(Callable callable) { - this.networkConnectivityChecker = callable; + /** + * Add a replication event listener to the replica. + * + * @param listener the listener + * @return the replica builder + */ + public ReplicaBuilder addReplicationEventListener(ReplicationEventListener listener) { + this.eventListeners.add(listener); + return this; + } + + public ReplicaBuilder replicaName(String name) { + this.replicaName = name; return this; } + + /** + * Creates a {@link Replica}. + * + * @return the replica + */ public Replica create() { if (collection != null) { Request.Builder builder = createRequestBuilder(); @@ -128,8 +223,11 @@ public Replica create() { config.setProxy(proxy); config.setAcceptAllCertificates(acceptAllCertificates); config.setAuthToken(authToken); - config.setNetworkConnectivityChecker(networkConnectivityChecker); - return new Replica(config); + config.setEventListeners(eventListeners); + config.setReplicaName(replicaName); + + ReplicatedCollection replicatedCollection = new ReplicatedCollection(config); + return new Replica(config, replicatedCollection); } else { throw new ReplicationException("no collection or repository has been specified for replication", true); } @@ -137,7 +235,7 @@ public Replica create() { private Request.Builder createRequestBuilder() { Request.Builder builder = new Request.Builder(); - builder.url(replicationServer); + builder.url(datagateServerUrl); return builder; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaChangeListener.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaChangeListener.java deleted file mode 100644 index f511cb32d..000000000 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaChangeListener.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.sync; - -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.collection.events.CollectionEventInfo; -import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.dizitart.no2.sync.event.ReplicationEvent; -import org.dizitart.no2.sync.event.ReplicationEventType; -import org.dizitart.no2.sync.message.DataGateFeed; - -import java.util.Collections; - -import static org.dizitart.no2.common.Constants.REPLICATOR; - -/** - * @author Anindya Chatterjee - */ -@Slf4j -class ReplicaChangeListener implements CollectionEventListener { - private final ReplicationTemplate replicationTemplate; - private final MessageTemplate messageTemplate; - - public ReplicaChangeListener(ReplicationTemplate replicationTemplate, MessageTemplate messageTemplate) { - this.replicationTemplate = replicationTemplate; - this.messageTemplate = messageTemplate; - } - - @Override - public void onEvent(CollectionEventInfo eventInfo) { - try { - if (eventInfo != null) { - if (!REPLICATOR.equals(eventInfo.getOriginator())) { - switch (eventInfo.getEventType()) { - case Insert: - case Update: - Document document = (Document) eventInfo.getItem(); - handleModifyEvent(document); - break; - case Remove: - document = (Document) eventInfo.getItem(); - handleRemoveEvent(document); - break; - case IndexStart: - case IndexEnd: - break; - } - } - } - } catch (Exception e) { - log.error("Error while processing collection event", e); - replicationTemplate.postEvent(new ReplicationEvent(ReplicationEventType.Error, e)); - } - } - - private void handleRemoveEvent(Document document) { - LastWriteWinState state = new LastWriteWinState(); - NitriteId nitriteId = document.getId(); - Long deleteTime = document.getLastModifiedSinceEpoch(); - - if (replicationTemplate.getCrdt() != null) { - replicationTemplate.getCrdt().getTombstones().put(nitriteId, deleteTime); - state.setTombstones(Collections.singletonMap(nitriteId.getIdValue(), deleteTime)); - sendFeed(state); - } - } - - private void handleModifyEvent(Document document) { - LastWriteWinState state = new LastWriteWinState(); - state.setChanges(Collections.singleton(document)); - sendFeed(state); - } - - private void sendFeed(LastWriteWinState state) { - if (replicationTemplate.shouldExchangeFeed() && messageTemplate != null) { - MessageFactory factory = replicationTemplate.getMessageFactory(); - DataGateFeed feedMessage = factory.createFeedMessage(replicationTemplate.getConfig(), - replicationTemplate.getReplicaId(), state); - - FeedJournal journal = replicationTemplate.getFeedJournal(); - messageTemplate.sendMessage(feedMessage); - journal.write(state); - } - } -} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java new file mode 100644 index 000000000..83576d747 --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.sync; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import okhttp3.WebSocket; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.tuples.Pair; +import org.dizitart.no2.common.util.StringUtils; +import org.dizitart.no2.sync.crdt.LastWriteWinMap; +import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.event.CollectionChangeListener; +import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; +import org.dizitart.no2.sync.net.DataGateSocket; +import org.dizitart.no2.sync.handlers.ReceiptLedgerAware; +import org.dizitart.no2.sync.message.Receipt; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.dizitart.no2.collection.meta.Attributes.REPLICA; + +/** + * @author Anindya Chatterjee + */ +@Slf4j +public class ReplicatedCollection implements ConflictFreeReplicatedDataType, ReceiptLedgerAware { + private String replicaId; + private AtomicBoolean connectedIndicator; + private CollectionChangeListener changeListener; + + @Getter private final Config config; + @Getter private FeedLedger feedLedger; + @Getter private NitriteCollection collection; + @Getter private DataGateClient dataGateClient; + @Getter private LastWriteWinMap lastWriteWinMap; + @Getter private BatchChangeSender batchChangeSender; + + public ReplicatedCollection(Config config) { + this.config = config; + configure(); + } + + public void startReplication() { + log.debug("Starting replication for " + getReplicaId()); + DataGateSocket dataGateSocket = new DataGateSocket(config); + dataGateClient = new DataGateClient(config, this); + batchChangeSender = new BatchChangeSender(config, this, dataGateClient); + dataGateSocket.setListener(dataGateClient); + } + + public void stopReplication(WebSocket webSocket, String reason) { + dataGateClient.closeConnection(webSocket, reason); + reset(); + } + + public LastWriteWinState getChangesSince(Long startTime, Long endTime, int start, Integer chunkSize) { + return lastWriteWinMap.getChangesSince(startTime, endTime, start, chunkSize); + } + + public void sendAndReceive(WebSocket webSocket, String correlationId, Long checkpoint) { + batchChangeSender.sendAndReceive(webSocket, correlationId, checkpoint); + } + + public void collectGarbage(Long ttl) { + if (ttl != null && ttl > 0) { + long collectTime = System.currentTimeMillis() - ttl; + if (lastWriteWinMap != null && lastWriteWinMap.getTombstoneMap() != null) { + Set removeSet = new HashSet<>(); + for (Pair entry : lastWriteWinMap.getTombstoneMap().entries()) { + if (entry.getSecond() < collectTime) { + removeSet.add(entry.getFirst()); + } + } + + Receipt garbage = new Receipt(); + for (NitriteId nitriteId : removeSet) { + lastWriteWinMap.getTombstoneMap().remove(nitriteId); + garbage.getRemoved().add(nitriteId.getIdValue()); + } + + feedLedger.writeOff(garbage); + } + } + } + + public void setConnected(boolean connected) { + this.connectedIndicator.set(connected); + } + + public boolean isConnected() { + return connectedIndicator.get(); + } + + public String getReplicaId() { + if (StringUtils.isNullOrEmpty(replicaId)) { + Attributes attributes = getAttributes(); + if (!attributes.hasKey(Attributes.REPLICA)) { + String name = StringUtils.isNullOrEmpty(config.getReplicaName()) + ? UUID.randomUUID().toString() + : config.getReplicaName() + "[" + UUID.randomUUID() + "]"; + attributes.set(REPLICA, name); + } + replicaId = attributes.get(Attributes.REPLICA); + } + return replicaId; + } + + private void configure() { + connectedIndicator = new AtomicBoolean(false); + collection = config.getCollection(); + lastWriteWinMap = createConflictFreeReplicatedDataType(); + feedLedger = new FeedLedger(config, this); + changeListener = new CollectionChangeListener(lastWriteWinMap); + getCollection().subscribe(changeListener); + } + + private void reset() { + connectedIndicator = new AtomicBoolean(false); + collection = config.getCollection(); + lastWriteWinMap = createConflictFreeReplicatedDataType(); + feedLedger = new FeedLedger(config, this); + + getCollection().unsubscribe(changeListener); + changeListener = new CollectionChangeListener(lastWriteWinMap); + getCollection().subscribe(changeListener); + } +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationTemplate.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationTemplate.java deleted file mode 100644 index ac7feea96..000000000 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationTemplate.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.sync; - -import lombok.AccessLevel; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.collection.meta.Attributes; -import org.dizitart.no2.common.tuples.Pair; -import org.dizitart.no2.common.util.StringUtils; -import org.dizitart.no2.sync.crdt.LastWriteWinMap; -import org.dizitart.no2.sync.event.ReplicationEvent; -import org.dizitart.no2.sync.event.ReplicationEventBus; -import org.dizitart.no2.sync.event.ReplicationEventListener; -import org.dizitart.no2.sync.message.Connect; -import org.dizitart.no2.sync.message.Disconnect; -import org.dizitart.no2.sync.message.Receipt; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.dizitart.no2.collection.meta.Attributes.REPLICA; -import static org.dizitart.no2.sync.event.ReplicationEventType.Started; -import static org.dizitart.no2.sync.event.ReplicationEventType.Stopped; - -/** - * @author Anindya Chatterjee - */ -@Slf4j -@Getter -public class ReplicationTemplate implements ReplicationOperation { - private final Config config; - private MessageFactory messageFactory; - private MessageTemplate messageTemplate; - private LastWriteWinMap crdt; - private FeedJournal feedJournal; - - @Getter(AccessLevel.NONE) - private BatchChangeScheduler batchChangeScheduler; - - @Getter(AccessLevel.NONE) - private String replicaId; - - @Getter(AccessLevel.NONE) - private ReplicaChangeListener replicaChangeListener; - - @Getter(AccessLevel.NONE) - private AtomicBoolean connected; - - @Getter(AccessLevel.NONE) - private AtomicBoolean exchangeFlag; - - @Getter(AccessLevel.NONE) - private AtomicBoolean acceptCheckpoint; - - @Getter(AccessLevel.NONE) - private ReplicationEventBus eventBus; - - public ReplicationTemplate(Config config) { - this.config = config; - initialize(); - } - - public void connect() { - messageTemplate.openConnection(); - Connect message = messageFactory.createConnect(config, getReplicaId()); - messageTemplate.sendMessage(message); - eventBus.post(new ReplicationEvent(Started)); - } - - public void setConnected() { - connected.compareAndSet(false, true); - } - - public boolean isConnected() { - return connected.get(); - } - - public void disconnect() { - Disconnect message = messageFactory.createDisconnect(config, getReplicaId()); - messageTemplate.sendMessage(message); - stopReplication("User disconnect"); - } - - public void stopReplication(String reason) { - batchChangeScheduler.stop(); - eventBus.post(new ReplicationEvent(Stopped)); - connected.set(false); - exchangeFlag.set(false); - acceptCheckpoint.set(false); - messageTemplate.closeConnection(reason); - } - - public void sendChanges() { - batchChangeScheduler.schedule(); - } - - public void startFeedExchange() { - this.exchangeFlag.compareAndSet(false, true); - } - - public boolean shouldExchangeFeed() { - return exchangeFlag.get(); - } - - public String getReplicaId() { - if (StringUtils.isNullOrEmpty(replicaId)) { - Attributes attributes = getAttributes(); - if (!attributes.hasKey(Attributes.REPLICA)) { - attributes.set(REPLICA, UUID.randomUUID().toString()); - } - replicaId = attributes.get(Attributes.REPLICA); - } - return replicaId; - } - - public void setAcceptCheckpoint() { - acceptCheckpoint.compareAndSet(false, true); - } - - public boolean shouldAcceptCheckpoint() { - return acceptCheckpoint.get(); - } - - @Override - public NitriteCollection getCollection() { - return config.getCollection(); - } - - public void subscribe(ReplicationEventListener listener) { - eventBus.register(listener); - } - - public void unsubscribe(ReplicationEventListener listener) { - eventBus.deregister(listener); - } - - public void postEvent(ReplicationEvent event) { - eventBus.post(event); - } - - public void close() { - eventBus.close(); - messageTemplate.close(); - batchChangeScheduler.stop(); - this.getCollection().unsubscribe(replicaChangeListener); - } - - public void collectGarbage(Long ttl) { - if (ttl != null && ttl > 0) { - long collectTime = System.currentTimeMillis() - ttl; - if (crdt != null && crdt.getTombstones() != null) { - Set removeSet = new HashSet<>(); - for (Pair entry : crdt.getTombstones().entries()) { - if (entry.getSecond() < collectTime) { - removeSet.add(entry.getFirst()); - } - } - - Receipt garbage = new Receipt(); - for (NitriteId nitriteId : removeSet) { - crdt.getTombstones().remove(nitriteId); - garbage.getRemoved().add(nitriteId.getIdValue()); - } - - feedJournal.accumulate(garbage); - } - } - } - - private void initialize() { - this.messageFactory = new MessageFactory(); - this.connected = new AtomicBoolean(false); - this.exchangeFlag = new AtomicBoolean(false); - this.acceptCheckpoint = new AtomicBoolean(false); - this.eventBus = new ReplicationEventBus(); - this.messageTemplate = new MessageTemplate(config, this); - this.crdt = createReplicatedDataType(); - this.feedJournal = new FeedJournal(this); - this.batchChangeScheduler = new BatchChangeScheduler(this); - this.replicaChangeListener = new ReplicaChangeListener(this, messageTemplate); - this.getCollection().subscribe(replicaChangeListener); - } -} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationOperation.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java similarity index 85% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationOperation.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java index 1fb887063..ecb81484e 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationOperation.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java @@ -1,20 +1,21 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.sync; +package org.dizitart.no2.sync.crdt; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; @@ -22,9 +23,6 @@ import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; -import org.dizitart.no2.sync.crdt.LastWriteWinMap; - -import java.util.UUID; import static org.dizitart.no2.collection.meta.Attributes.LAST_SYNCED; import static org.dizitart.no2.collection.meta.Attributes.TOMBSTONE; @@ -33,9 +31,11 @@ /** * @author Anindya Chatterjee */ -interface ReplicationOperation { +public interface ConflictFreeReplicatedDataType { NitriteCollection getCollection(); + String getReplicaId(); + default Attributes getAttributes() { Attributes attributes = getCollection().getAttributes(); if (attributes == null) { @@ -45,21 +45,17 @@ default Attributes getAttributes() { return attributes; } - default void saveAttributes(Attributes attributes) { - getCollection().setAttributes(attributes); - } - default Long getLastSyncTime() { Attributes attributes = getAttributes(); String syncTimeStr = attributes.get(LAST_SYNCED); if (StringUtils.isNullOrEmpty(syncTimeStr)) { - return Long.MIN_VALUE; + return 0L; } else { return Long.parseLong(syncTimeStr); } } - default LastWriteWinMap createReplicatedDataType() { + default LastWriteWinMap createConflictFreeReplicatedDataType() { Attributes attributes = getAttributes(); String tombstoneName = getTombstoneName(attributes); saveAttributes(attributes); @@ -73,8 +69,7 @@ default String getTombstoneName(Attributes attributes) { String tombstoneName = attributes.get(TOMBSTONE); if (StringUtils.isNullOrEmpty(tombstoneName)) { tombstoneName = getCollection().getName() - + INTERNAL_NAME_SEPARATOR + TOMBSTONE - + INTERNAL_NAME_SEPARATOR + UUID.randomUUID(); + + INTERNAL_NAME_SEPARATOR + TOMBSTONE; attributes.set(TOMBSTONE, tombstoneName); } return tombstoneName; @@ -85,4 +80,8 @@ default void saveLastSyncTime(Long lastSyncTime) { attributes.set(LAST_SYNCED, Long.toString(lastSyncTime)); saveAttributes(attributes); } + + default void saveAttributes(Attributes attributes) { + getCollection().setAttributes(attributes); + } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java index 4e011ebec..cd02c8ad4 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java @@ -17,7 +17,11 @@ package org.dizitart.no2.sync.crdt; import lombok.Data; -import org.dizitart.no2.collection.*; +import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.DocumentCursor; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.store.NitriteMap; @@ -25,47 +29,59 @@ import static org.dizitart.no2.collection.FindOptions.skipBy; import static org.dizitart.no2.common.Constants.*; +import static org.dizitart.no2.filters.Filter.and; import static org.dizitart.no2.filters.FluentFilter.where; /** * @author Anindya Chatterjee. */ +@Slf4j @Data public class LastWriteWinMap { private NitriteCollection collection; - private NitriteMap tombstones; + private NitriteMap tombstoneMap; - public LastWriteWinMap(NitriteCollection collection, NitriteMap tombstones) { + public LastWriteWinMap(NitriteCollection collection, NitriteMap tombstoneMap) { this.collection = collection; - this.tombstones = tombstones; + this.tombstoneMap = tombstoneMap; } public void merge(LastWriteWinState snapshot) { - if (snapshot.getChanges() != null) { - for (Document entry : snapshot.getChanges()) { + if (snapshot.getChangeSet() != null) { + for (Document entry : snapshot.getChangeSet()) { put(entry); } } - if (snapshot.getTombstones() != null) { - for (Map.Entry entry : snapshot.getTombstones().entrySet()) { + if (snapshot.getTombstoneMap() != null) { + for (Map.Entry entry : snapshot.getTombstoneMap().entrySet()) { remove(NitriteId.createId(entry.getKey()), entry.getValue()); } } + + log.debug("Collection after merge - " + collection.find().toList()); + log.debug("Tombstone after merge - " + tombstoneMap.entries().toList()); } - public LastWriteWinState getChangesSince(Long since, int offset, int size) { + public LastWriteWinState getChangesSince(Long startTime, Long endTime, + int offset, int size) { LastWriteWinState state = new LastWriteWinState(); - DocumentCursor cursor = collection.find(where(DOC_MODIFIED).gte(since), skipBy(offset).limit(size)); - state.getChanges().addAll(cursor.toSet()); + DocumentCursor cursor = collection.find( + and( + where(DOC_MODIFIED).gte(startTime), + where(DOC_MODIFIED).lte(endTime) + ), skipBy(offset).limit(size)); + + state.getChangeSet().addAll(cursor.toSet()); + // send tombstone info in first batch only if (offset == 0) { // don't repeat for other offsets - for (Pair entry : tombstones.entries()) { + for (Pair entry : tombstoneMap.entries()) { Long timestamp = entry.getSecond(); - if (timestamp >= since) { - state.getTombstones().put(entry.getFirst().getIdValue(), entry.getSecond()); + if (timestamp > startTime && timestamp <= endTime) { + state.getTombstoneMap().put(entry.getFirst().getIdValue(), entry.getSecond()); } } } @@ -79,14 +95,14 @@ private void put(Document value) { Document entry = collection.getById(key); if (entry == null) { - if (tombstones.containsKey(key)) { - Long tombstoneTime = tombstones.get(key); + if (tombstoneMap.containsKey(key)) { + Long tombstoneTime = tombstoneMap.get(key); Long docModifiedTime = value.getLastModifiedSinceEpoch(); if (docModifiedTime >= tombstoneTime) { value.put(DOC_SOURCE, REPLICATOR); collection.insert(value); - tombstones.remove(key); + tombstoneMap.remove(key); } } else { value.put(DOC_SOURCE, REPLICATOR); @@ -111,8 +127,10 @@ private void remove(NitriteId key, long timestamp) { Document entry = collection.getById(key); if (entry != null) { entry.put(DOC_SOURCE, REPLICATOR); + + log.debug("Removing {} from collection for tombstone", key.getIdValue()); collection.remove(entry); - tombstones.put(key, timestamp); + tombstoneMap.put(key, timestamp); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinState.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinState.java index 282350085..8937f55f1 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinState.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinState.java @@ -32,11 +32,11 @@ @Data public class LastWriteWinState { @JsonDeserialize(contentUsing = DocumentDeserializer.class) - private Set changes; - private Map tombstones; + private Set changeSet; + private Map tombstoneMap; public LastWriteWinState() { - changes = new LinkedHashSet<>(); - tombstones = new LinkedHashMap<>(); + changeSet = new LinkedHashSet<>(); + tombstoneMap = new LinkedHashMap<>(); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Tombstone.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Tombstone.java new file mode 100644 index 000000000..15856d5b4 --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Tombstone.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.sync.crdt; + +import lombok.Data; +import org.dizitart.no2.collection.NitriteId; + +/** + * @author Anindya Chatterjee + */ +@Data +public class Tombstone { + private NitriteId nitriteId; + private Long deleteTimestamp; + private Long syncTimestamp; +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/event/CollectionChangeListener.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/event/CollectionChangeListener.java new file mode 100644 index 000000000..66a567ad7 --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/event/CollectionChangeListener.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.sync.event; + +import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.collection.events.CollectionEventInfo; +import org.dizitart.no2.collection.events.CollectionEventListener; +import org.dizitart.no2.sync.crdt.LastWriteWinMap; + +import static org.dizitart.no2.common.Constants.REPLICATOR; + +/** + * @author Anindya Chatterjee + */ +@Slf4j +public class CollectionChangeListener implements CollectionEventListener { + private final LastWriteWinMap lastWriteWinMap; + + public CollectionChangeListener(LastWriteWinMap lastWriteWinMap) { + this.lastWriteWinMap = lastWriteWinMap; + } + + @Override + public void onEvent(CollectionEventInfo eventInfo) { + if (eventInfo != null) { + if (!REPLICATOR.equals(eventInfo.getOriginator())) { + switch (eventInfo.getEventType()) { + case Remove: + Document document = (Document) eventInfo.getItem(); + handleRemoveEvent(document); + break; + case Insert: + case Update: + case IndexStart: + case IndexEnd: + break; + } + } + } + } + + private void handleRemoveEvent(Document document) { + NitriteId nitriteId = document.getId(); + Long deleteTime = document.getLastModifiedSinceEpoch(); + + if (lastWriteWinMap != null) { + lastWriteWinMap.getTombstoneMap().put(nitriteId, deleteTime); + } + } +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java index c6a808d8d..d60831110 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java @@ -16,8 +16,9 @@ package org.dizitart.no2.sync.handlers; -import org.dizitart.no2.sync.FeedJournal; -import org.dizitart.no2.sync.ReplicationTemplate; +import okhttp3.WebSocket; +import org.dizitart.no2.sync.FeedLedger; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.BatchAck; import org.dizitart.no2.sync.message.Receipt; @@ -25,16 +26,18 @@ * @author Anindya Chatterjee */ public class BatchAckHandler implements MessageHandler { - private final ReplicationTemplate replicationTemplate; + private final ReplicatedCollection replicatedCollection; - public BatchAckHandler(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; + public BatchAckHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(BatchAck message) { + public void handleMessage(WebSocket webSocket, BatchAck message) { Receipt receipt = message.getReceipt(); - FeedJournal journal = replicationTemplate.getFeedJournal(); - journal.accumulate(receipt); + FeedLedger feedLedger = replicatedCollection.getFeedLedger(); + feedLedger.writeOff(receipt); + replicatedCollection.sendAndReceive(webSocket, message.getHeader().getCorrelationId(), + message.getHeader().getTimestamp()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java index 3a21faede..bedc68ba9 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java @@ -16,9 +16,10 @@ package org.dizitart.no2.sync.handlers; -import lombok.Data; +import lombok.Getter; +import okhttp3.WebSocket; import org.dizitart.no2.sync.MessageFactory; -import org.dizitart.no2.sync.ReplicationTemplate; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.BatchAck; import org.dizitart.no2.sync.message.BatchChangeContinue; import org.dizitart.no2.sync.message.Receipt; @@ -26,23 +27,22 @@ /** * @author Anindya Chatterjee */ -@Data public class BatchChangeContinueHandler implements MessageHandler, ReceiptAckSender { - private ReplicationTemplate replicationTemplate; + @Getter private final ReplicatedCollection replicatedCollection; - public BatchChangeContinueHandler(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; + public BatchChangeContinueHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(BatchChangeContinue message) { - sendAck(message); + public void handleMessage(WebSocket webSocket, BatchChangeContinue message) { + sendAck(webSocket, message); } @Override public BatchAck createAck(String correlationId, Receipt receipt) { - MessageFactory factory = replicationTemplate.getMessageFactory(); - return factory.createBatchAck(replicationTemplate.getConfig(), - correlationId, replicationTemplate.getReplicaId(), receipt); + MessageFactory factory = new MessageFactory(); + return factory.createBatchAck(replicatedCollection.getConfig(), + correlationId, replicatedCollection.getReplicaId(), receipt); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java index 41cdd9d12..9ab29cb20 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java @@ -16,32 +16,37 @@ package org.dizitart.no2.sync.handlers; +import lombok.extern.slf4j.Slf4j; +import okhttp3.WebSocket; import org.dizitart.no2.sync.MessageFactory; -import org.dizitart.no2.sync.MessageTemplate; -import org.dizitart.no2.sync.ReplicationTemplate; +import org.dizitart.no2.sync.DataGateClient; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.BatchChangeEnd; import org.dizitart.no2.sync.message.BatchEndAck; /** * @author Anindya Chatterjee */ +@Slf4j public class BatchChangeEndHandler implements MessageHandler { - private final ReplicationTemplate replicationTemplate; + private final ReplicatedCollection replicatedCollection; - public BatchChangeEndHandler(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; + public BatchChangeEndHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(BatchChangeEnd message) { - MessageFactory factory = replicationTemplate.getMessageFactory(); - BatchEndAck batchEndAck = factory.createBatchEndAck(replicationTemplate.getConfig(), - replicationTemplate.getReplicaId(), message.getHeader().getId()); - - MessageTemplate messageTemplate = replicationTemplate.getMessageTemplate(); - messageTemplate.sendMessage(batchEndAck); - Long time = message.getHeader().getTimestamp(); - replicationTemplate.saveLastSyncTime(time); - replicationTemplate.setAcceptCheckpoint(); + public void handleMessage(WebSocket webSocket, BatchChangeEnd message) { + MessageFactory factory = new MessageFactory(); + BatchEndAck batchEndAck = factory.createBatchEndAck(replicatedCollection.getConfig(), + replicatedCollection.getReplicaId(), message.getHeader().getId()); + + DataGateClient dataGateClient = replicatedCollection.getDataGateClient(); + dataGateClient.sendMessage(webSocket, batchEndAck); + + Long time = message.getEndTime(); + + log.debug("Saving last sync time - " + time); + replicatedCollection.saveLastSyncTime(time); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeStartHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeStartHandler.java index 7575ab818..94c80cd37 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeStartHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeStartHandler.java @@ -16,9 +16,10 @@ package org.dizitart.no2.sync.handlers; -import lombok.Data; +import lombok.Getter; +import okhttp3.WebSocket; import org.dizitart.no2.sync.MessageFactory; -import org.dizitart.no2.sync.ReplicationTemplate; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.BatchAck; import org.dizitart.no2.sync.message.BatchChangeStart; import org.dizitart.no2.sync.message.Receipt; @@ -26,23 +27,22 @@ /** * @author Anindya Chatterjee */ -@Data public class BatchChangeStartHandler implements MessageHandler, ReceiptAckSender { - private ReplicationTemplate replicationTemplate; + @Getter private final ReplicatedCollection replicatedCollection; - public BatchChangeStartHandler(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; + public BatchChangeStartHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(BatchChangeStart message) { - sendAck(message); + public void handleMessage(WebSocket webSocket, BatchChangeStart message) { + sendAck(webSocket, message); } @Override public BatchAck createAck(String correlationId, Receipt receipt) { - MessageFactory factory = replicationTemplate.getMessageFactory(); - return factory.createBatchAck(replicationTemplate.getConfig(), - replicationTemplate.getReplicaId(), correlationId, receipt); + MessageFactory factory = new MessageFactory(); + return factory.createBatchAck(replicatedCollection.getConfig(), + replicatedCollection.getReplicaId(), correlationId, receipt); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java index 37ee84a08..a3faa456b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java @@ -17,24 +17,23 @@ package org.dizitart.no2.sync.handlers; import lombok.Getter; -import org.dizitart.no2.sync.ReplicationTemplate; +import okhttp3.WebSocket; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.BatchEndAck; -import org.dizitart.no2.sync.message.Receipt; /** * @author Anindya Chatterjee */ @Getter -public class BatchEndAckHandler implements MessageHandler, JournalAware { - private final ReplicationTemplate replicationTemplate; +public class BatchEndAckHandler implements MessageHandler { + private final ReplicatedCollection replicatedCollection; - public BatchEndAckHandler(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; + public BatchEndAckHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(BatchEndAck message) { - Receipt finalReceipt = getJournal().getFinalReceipt(); - retryFailed(finalReceipt); + public void handleMessage(WebSocket webSocket, BatchEndAck message) { + // nothing to be done here } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java index ca528cd52..64bb099d6 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java @@ -17,7 +17,8 @@ package org.dizitart.no2.sync.handlers; import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.sync.ReplicationTemplate; +import okhttp3.WebSocket; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.ConnectAck; /** @@ -25,17 +26,17 @@ */ @Slf4j public class ConnectAckHandler implements MessageHandler { - private final ReplicationTemplate replica; + private final ReplicatedCollection replicatedCollection; - public ConnectAckHandler(ReplicationTemplate replica) { - this.replica = replica; + public ConnectAckHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(ConnectAck message) { - replica.collectGarbage(message.getTombstoneTtl()); - replica.setConnected(); - replica.startFeedExchange(); - replica.sendChanges(); + public void handleMessage(WebSocket webSocket, ConnectAck message) { + replicatedCollection.collectGarbage(message.getTombstoneTtl()); + replicatedCollection.setConnected(true); + replicatedCollection.sendAndReceive(webSocket, message.getHeader().getCorrelationId(), + message.getHeader().getTimestamp()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedAckHandler.java index c2ab9dec9..e0bee23e8 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedAckHandler.java @@ -17,31 +17,24 @@ package org.dizitart.no2.sync.handlers; import lombok.Getter; -import org.dizitart.no2.sync.ReplicationTemplate; +import okhttp3.WebSocket; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.DataGateFeedAck; import org.dizitart.no2.sync.message.Receipt; /** * @author Anindya Chatterjee */ -@Getter -public class DataGateFeedAckHandler implements MessageHandler, JournalAware { - private final ReplicationTemplate replicationTemplate; +public class DataGateFeedAckHandler implements MessageHandler { + @Getter private final ReplicatedCollection replicatedCollection; - public DataGateFeedAckHandler(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; + public DataGateFeedAckHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(DataGateFeedAck message) { + public void handleMessage(WebSocket webSocket, DataGateFeedAck message) { Receipt receipt = message.getReceipt(); - - Receipt finalReceipt = getJournal().accumulate(receipt); - retryFailed(finalReceipt); - - if (replicationTemplate.shouldAcceptCheckpoint()) { - Long time = message.getHeader().getTimestamp(); - replicationTemplate.saveLastSyncTime(time); - } + replicatedCollection.getFeedLedger().writeOff(receipt); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java index 92b37da52..d4166ce2a 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java @@ -16,9 +16,10 @@ package org.dizitart.no2.sync.handlers; -import lombok.Data; +import lombok.Getter; +import okhttp3.WebSocket; import org.dizitart.no2.sync.MessageFactory; -import org.dizitart.no2.sync.ReplicationTemplate; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.DataGateFeed; import org.dizitart.no2.sync.message.DataGateFeedAck; import org.dizitart.no2.sync.message.Receipt; @@ -26,27 +27,25 @@ /** * @author Anindya Chatterjee */ -@Data public class DataGateFeedHandler implements MessageHandler, ReceiptAckSender { - private ReplicationTemplate replicationTemplate; + @Getter private final ReplicatedCollection replicatedCollection; - public DataGateFeedHandler(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; + public DataGateFeedHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(DataGateFeed message) { - sendAck(message); - if (replicationTemplate.shouldAcceptCheckpoint()) { - Long time = message.getHeader().getTimestamp(); - replicationTemplate.saveLastSyncTime(time); - } + public void handleMessage(WebSocket webSocket, DataGateFeed message) { + sendAck(webSocket, message); + + Long time = message.getHeader().getTimestamp(); + replicatedCollection.saveLastSyncTime(time); } @Override public DataGateFeedAck createAck(String correlationId, Receipt receipt) { - MessageFactory factory = replicationTemplate.getMessageFactory(); - return factory.createFeedAck(replicationTemplate.getConfig(), - replicationTemplate.getReplicaId(), correlationId, receipt); + MessageFactory factory = new MessageFactory(); + return factory.createFeedAck(replicatedCollection.getConfig(), + replicatedCollection.getReplicaId(), correlationId, receipt); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java index f07d752f1..d69fef496 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java @@ -16,21 +16,25 @@ package org.dizitart.no2.sync.handlers; -import org.dizitart.no2.sync.ReplicationTemplate; +import lombok.extern.slf4j.Slf4j; +import okhttp3.WebSocket; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.Disconnect; /** * @author Anindya Chatterjee */ +@Slf4j public class DisconnectHandler implements MessageHandler { - private final ReplicationTemplate replicationTemplate; + private final ReplicatedCollection replicatedCollection; - public DisconnectHandler(ReplicationTemplate replicationTemplate) { - this.replicationTemplate = replicationTemplate; + public DisconnectHandler(ReplicatedCollection replicatedCollection) { + this.replicatedCollection = replicatedCollection; } @Override - public void handleMessage(Disconnect message) { - replicationTemplate.stopReplication("Server disconnect"); + public void handleMessage(WebSocket webSocket, Disconnect message) { + log.debug("Disconnecting from server"); + replicatedCollection.stopReplication(webSocket, "Server disconnect"); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ErrorHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ErrorHandler.java index a2f5cfaca..fea9349ca 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ErrorHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ErrorHandler.java @@ -17,7 +17,8 @@ package org.dizitart.no2.sync.handlers; import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.sync.ReplicationTemplate; +import okhttp3.WebSocket; +import org.dizitart.no2.sync.ReplicationException; import org.dizitart.no2.sync.message.ErrorMessage; /** @@ -25,15 +26,10 @@ */ @Slf4j public class ErrorHandler implements MessageHandler { - private final ReplicationTemplate replica; - - public ErrorHandler(ReplicationTemplate replica) { - this.replica = replica; - } @Override - public void handleMessage(ErrorMessage message) { + public void handleMessage(WebSocket webSocket, ErrorMessage message) { log.error("Received error message from server - {}", message.getError()); - replica.stopReplication(message.getError()); + throw new ReplicationException(message.getError(), true); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/MessageHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/MessageHandler.java index 3c1ba3a5c..73e0bef70 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/MessageHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/MessageHandler.java @@ -16,11 +16,12 @@ package org.dizitart.no2.sync.handlers; +import okhttp3.WebSocket; import org.dizitart.no2.sync.message.DataGateMessage; /** * @author Anindya Chatterjee */ public interface MessageHandler { - void handleMessage(M message) throws Exception; + void handleMessage(WebSocket webSocket, M message); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java index a286ea2b7..a3d0abeb4 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java @@ -16,30 +16,41 @@ package org.dizitart.no2.sync.handlers; -import org.dizitart.no2.sync.MessageTemplate; -import org.dizitart.no2.sync.ReplicationTemplate; +import okhttp3.WebSocket; import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.DataGateClient; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.DataGateMessage; import org.dizitart.no2.sync.message.Receipt; import org.dizitart.no2.sync.message.ReceiptAware; +import org.dizitart.no2.sync.message.TimeBoundMessage; /** * @author Anindya Chatterjee */ public interface ReceiptAckSender { - ReplicationTemplate getReplicationTemplate(); + ReplicatedCollection getReplicatedCollection(); Ack createAck(String correlationId, Receipt receipt); - default void sendAck(ReceiptAware message) { + default void sendAck(WebSocket webSocket, ReceiptAware message) { if (message != null) { LastWriteWinState state = message.getFeed(); - getReplicationTemplate().getCrdt().merge(state); + getReplicatedCollection().getLastWriteWinMap().merge(state); Receipt receipt = message.calculateReceipt(); - Ack ack = createAck(message.getHeader().getId(), receipt); - MessageTemplate messageTemplate = getReplicationTemplate().getMessageTemplate(); - messageTemplate.sendMessage(ack); + Ack ack = createAck(message.getHeader().getCorrelationId(), receipt); + + if (ack instanceof TimeBoundMessage && message instanceof TimeBoundMessage) { + TimeBoundMessage timeBoundMessage = (TimeBoundMessage) message; + TimeBoundMessage timeBoundAck = (TimeBoundMessage) ack; + + timeBoundAck.setStartTime(timeBoundMessage.getStartTime()); + timeBoundAck.setEndTime(timeBoundMessage.getEndTime()); + } + + DataGateClient dataGateClient = getReplicatedCollection().getDataGateClient(); + dataGateClient.sendMessage(webSocket, ack); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/JournalAware.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java similarity index 59% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/JournalAware.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java index 6f97dfadb..0060147d6 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/JournalAware.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java @@ -19,13 +19,10 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.sync.FeedJournal; -import org.dizitart.no2.sync.MessageFactory; -import org.dizitart.no2.sync.MessageTemplate; -import org.dizitart.no2.sync.ReplicationTemplate; +import org.dizitart.no2.sync.Config; +import org.dizitart.no2.sync.DataGateClient; import org.dizitart.no2.sync.crdt.LastWriteWinMap; import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.dizitart.no2.sync.message.DataGateFeed; import org.dizitart.no2.sync.message.Receipt; import java.util.HashMap; @@ -34,49 +31,39 @@ /** * @author Anindya Chatterjee */ -public interface JournalAware { - ReplicationTemplate getReplicationTemplate(); +public interface ReceiptLedgerAware { + NitriteCollection getCollection(); - default FeedJournal getJournal() { - return getReplicationTemplate().getFeedJournal(); - } + String getReplicaId(); - default void retryFailed(Receipt receipt) { - if (shouldRetry(receipt)) { - LastWriteWinState state = createState(receipt); + LastWriteWinMap getLastWriteWinMap(); - MessageFactory factory = getReplicationTemplate().getMessageFactory(); - DataGateFeed feedMessage = factory.createFeedMessage(getReplicationTemplate().getConfig(), - getReplicationTemplate().getReplicaId(), state); + Config getConfig(); - MessageTemplate messageTemplate = getReplicationTemplate().getMessageTemplate(); - messageTemplate.sendMessage(feedMessage); - } - } + DataGateClient getDataGateClient(); default LastWriteWinState createState(Receipt receipt) { LastWriteWinState state = new LastWriteWinState(); - state.setTombstones(new HashMap<>()); - state.setChanges(new HashSet<>()); + state.setTombstoneMap(new HashMap<>()); + state.setChangeSet(new HashSet<>()); - NitriteCollection collection = getReplicationTemplate().getCollection(); - LastWriteWinMap crdt = getReplicationTemplate().getCrdt(); + NitriteCollection collection = getCollection(); if (receipt != null) { if (receipt.getAdded() != null) { for (String id : receipt.getAdded()) { Document document = collection.getById(NitriteId.createId(id)); if (document != null) { - state.getChanges().add(document); + state.getChangeSet().add(document); } } } if (receipt.getRemoved() != null) { for (String id : receipt.getRemoved()) { - Long timestamp = crdt.getTombstones().get(NitriteId.createId(id)); + Long timestamp = getLastWriteWinMap().getTombstoneMap().get(NitriteId.createId(id)); if (timestamp != null) { - state.getTombstones().put(id, timestamp); + state.getTombstoneMap().put(id, timestamp); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java index 91ba6c856..28ab91e35 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java @@ -17,12 +17,14 @@ package org.dizitart.no2.sync.message; import lombok.Data; +import lombok.EqualsAndHashCode; /** * @author Anindya Chatterjee */ @Data -public class BatchAck implements DataGateMessage { +@EqualsAndHashCode(callSuper = true) +public class BatchAck extends TimeBoundMessage implements DataGateMessage { private MessageHeader header; private Receipt receipt; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java index 0c673b657..2d79f5fac 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java @@ -17,13 +17,15 @@ package org.dizitart.no2.sync.message; import lombok.Data; +import lombok.EqualsAndHashCode; import org.dizitart.no2.sync.crdt.LastWriteWinState; /** * @author Anindya Chatterjee */ @Data -public class BatchChangeContinue implements ReceiptAware { +@EqualsAndHashCode(callSuper = true) +public class BatchChangeContinue extends TimeBoundMessage implements ReceiptAware { private MessageHeader header; private LastWriteWinState feed; private Integer batchSize; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java index e41289990..28f4dd3a8 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java @@ -17,14 +17,15 @@ package org.dizitart.no2.sync.message; import lombok.Data; +import lombok.EqualsAndHashCode; /** * @author Anindya Chatterjee */ @Data -public class BatchChangeEnd implements DataGateMessage { +@EqualsAndHashCode(callSuper = true) +public class BatchChangeEnd extends TimeBoundMessage implements DataGateMessage { private MessageHeader header; - private Long lastSynced; private Integer batchSize; private Integer debounce; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java index 4f80ab10a..9af619952 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java @@ -17,13 +17,17 @@ package org.dizitart.no2.sync.message; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import org.dizitart.no2.sync.crdt.LastWriteWinState; /** * @author Anindya Chatterjee */ @Data -public class BatchChangeStart implements ReceiptAware { +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BatchChangeStart extends TimeBoundMessage implements ReceiptAware { private MessageHeader header; private Integer batchSize; private Integer debounce; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java index 2dc73e2a0..42d4c2228 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java @@ -17,11 +17,13 @@ package org.dizitart.no2.sync.message; import lombok.Data; +import lombok.EqualsAndHashCode; /** * @author Anindya Chatterjee */ @Data -public class BatchEndAck implements DataGateMessage { +@EqualsAndHashCode(callSuper = true) +public class BatchEndAck extends TimeBoundMessage implements DataGateMessage { private MessageHeader header; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java index 0c4136871..9681f8956 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java @@ -17,11 +17,13 @@ package org.dizitart.no2.sync.message; import lombok.Data; +import lombok.ToString; /** * @author Anindya Chatterjee */ @Data +@ToString(onlyExplicitlyIncluded = true) public class MessageHeader { private String id; private String correlationId; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ReceiptAware.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ReceiptAware.java index a3ec12480..aa67587c3 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ReceiptAware.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ReceiptAware.java @@ -34,14 +34,14 @@ default Receipt calculateReceipt() { Set removed = new HashSet<>(); if (getFeed() != null) { - if (getFeed().getChanges() != null) { - for (Document change : getFeed().getChanges()) { + if (getFeed().getChangeSet() != null) { + for (Document change : getFeed().getChangeSet()) { added.add(change.getId().getIdValue()); } } - if (getFeed().getTombstones() != null) { - for (Map.Entry entry : getFeed().getTombstones().entrySet()) { + if (getFeed().getTombstoneMap() != null) { + for (Map.Entry entry : getFeed().getTombstoneMap().entrySet()) { removed.add(entry.getKey()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/TimeBoundMessage.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/TimeBoundMessage.java new file mode 100644 index 000000000..26364208a --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/TimeBoundMessage.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.sync.message; + +import lombok.Data; + +/** + * @author Anindya Chatterjee + */ +@Data +public abstract class TimeBoundMessage { + private Long startTime; + private Long endTime; +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java index 3aa5e9c60..f9946a596 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java @@ -1,185 +1,62 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.sync.net; - -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; -import okhttp3.*; -import okio.ByteString; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.WebSocketListener; import org.dizitart.no2.sync.Config; import org.dizitart.no2.sync.ReplicationException; -import org.dizitart.no2.sync.message.DataGateMessage; -import org.jetbrains.annotations.NotNull; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; /** * @author Anindya Chatterjee */ @Slf4j public class DataGateSocket { - private final static int RECONNECT_INTERVAL = 10 * 1000; - private final static long RECONNECT_MAX_TIME = 120 * 1000; - private final OkHttpClient httpClient; - private final Request request; - private final Lock lock; - private final ObjectMapper objectMapper; private final Config config; - private final Callable networkConnectivityChecker; - private WebSocket mWebSocket; - private int currentStatus = Status.DISCONNECTED; - private boolean manualClose; - private DataGateSocketListener listener; - private int reconnectCount = 0; - private Timer reconnectTimer; - private CountDownLatch latch; - private final WebSocketListener webSocketListener = new WebSocketListener() { - @Override - public void onOpen(@NotNull WebSocket webSocket, @NotNull final Response response) { - mWebSocket = webSocket; - setCurrentStatus(Status.CONNECTED); - if (latch != null) { - latch.countDown(); - } - - connected(); - if (listener != null) { - listener.onOpen(response); - } - } - @Override - public void onMessage(@NotNull WebSocket webSocket, @NotNull final String text) { - if (listener != null) { - listener.onMessage(text); - } - } - - @Override - public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) { - if (listener != null) { - listener.onMessage(bytes); - } - } - - @Override - public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) { - if (listener != null) { - listener.onClosing(code, reason); - } - } - - @Override - public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { - if (listener != null) { - listener.onClosed(code, reason); - } - } - - @Override - public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, Response response) { - if (listener != null) { - listener.onFailure(t, response); - } - } - }; + private OkHttpClient httpClient; + private Request request; public DataGateSocket(Config config) { this.config = config; - this.networkConnectivityChecker = config.getNetworkConnectivityChecker(); - this.lock = new ReentrantLock(); - this.httpClient = createClient(); - this.request = config.getRequestBuilder().build(); - this.objectMapper = config.getObjectMapper(); - } - - public void setListener(DataGateSocketListener listener) { - this.listener = listener; + configure(config); } - public boolean isConnected() { - return currentStatus == Status.CONNECTED; - } - - public int getCurrentStatus() { - return currentStatus; - } - - public void setCurrentStatus(int status) { - this.currentStatus = status; - } - - public void startConnect() { - manualClose = false; - buildConnect(); - } - - public void stopConnect(String reason) { - manualClose = true; - disconnect(reason); - } - - public boolean sendMessage(DataGateMessage message) { - boolean isSent = false; + public void setListener(WebSocketListener listener) { try { - if (mWebSocket != null && isConnected()) { - String text = objectMapper.writeValueAsString(message); - log.debug("Sending message to server {}", text); - isSent = mWebSocket.send(text); - - if (!isSent) { - tryReconnect(); - } - } + createWebSocket(listener); } catch (Exception e) { - log.error("Error while sending message", e); - isSent = false; + log.error("Failed to connect to remote datagate server", e); + throw new ReplicationException("remote datagate connection failed", e, true); } - return isSent; } - private void initWebSocket() { - if (httpClient != null) { - httpClient.dispatcher().cancelAll(); - } - try { - lock.lockInterruptibly(); - try { - latch = new CountDownLatch(1); - if (httpClient != null) { - httpClient.newWebSocket(request, webSocketListener); - } - latch.await(); - } finally { - lock.unlock(); - } - } catch (InterruptedException ignored) { - } + private void configure(Config config) { + this.httpClient = createClient(); + this.request = config.getRequestBuilder().build(); } private OkHttpClient createClient() { @@ -238,84 +115,10 @@ public java.security.cert.X509Certificate[] getAcceptedIssuers() { return builder.build(); } - private void tryReconnect() { - if (manualClose) { - return; - } - - if (isNetworkDisconnected()) { - setCurrentStatus(Status.DISCONNECTED); - return; - } - - setCurrentStatus(Status.RECONNECT); - reconnectTimer = new Timer(); - - long delay = (long) reconnectCount * RECONNECT_INTERVAL; - reconnectTimer.schedule(new TimerTask() { - @Override - public void run() { - if (listener != null) { - listener.onReconnect(); - } - buildConnect(); - } - }, Math.min(delay, RECONNECT_MAX_TIME)); - reconnectCount++; - } - - private void cancelReconnect() { - if (reconnectTimer != null) { - reconnectTimer.cancel(); - } - reconnectCount = 0; - } - - private void connected() { - cancelReconnect(); - } - - private void disconnect(String reason) { - if (currentStatus == Status.DISCONNECTED) { - return; - } - - cancelReconnect(); - - if (mWebSocket != null) { - boolean isClosed = mWebSocket.close(Status.CODE.NORMAL_CLOSE, reason); - if (!isClosed) { - if (listener != null) { - listener.onClosed(Status.CODE.ABNORMAL_CLOSE, reason); - } - } - } - - setCurrentStatus(Status.DISCONNECTED); - } - - private void buildConnect() { - if (isNetworkDisconnected()) { - setCurrentStatus(Status.DISCONNECTED); - return; - } - - switch (getCurrentStatus()) { - case Status.CONNECTED: - case Status.CONNECTING: - break; - default: - setCurrentStatus(Status.CONNECTING); - initWebSocket(); - } - } - - private boolean isNetworkDisconnected() { - try { - return !networkConnectivityChecker.call(); - } catch (Exception e) { - log.error("Network connectivity failed", e); - return true; + private void createWebSocket(WebSocketListener webSocketListener) { + if (httpClient != null) { + httpClient.dispatcher().cancelAll(); + httpClient.newWebSocket(request, webSocketListener); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/Status.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/WebSocketCode.java similarity index 59% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/net/Status.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/net/WebSocketCode.java index 16738c08e..49bf362df 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/Status.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/WebSocketCode.java @@ -1,17 +1,18 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.sync.net; @@ -19,14 +20,7 @@ /** * @author Anindya Chatterjee */ -public class Status { - public final static int CONNECTED = 1; - public final static int CONNECTING = 0; - public final static int RECONNECT = 2; - public final static int DISCONNECTED = -1; - - interface CODE { - int NORMAL_CLOSE = 1000; - int ABNORMAL_CLOSE = 1001; - } +public interface WebSocketCode { + int NORMAL_CLOSE = 1000; + int ABNORMAL_CLOSE = 1001; } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java b/nitrite-replication/src/test/java/org/dizitart/no2/Retry.java similarity index 66% rename from nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java rename to nitrite-replication/src/test/java/org/dizitart/no2/Retry.java index 71ee701da..b7e69a7f2 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/Retry.java @@ -1,4 +1,21 @@ -package org.dizitart.no2.integration; +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2; import org.junit.rules.TestRule; import org.junit.runner.Description; diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/TestUtils.java b/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java similarity index 94% rename from nitrite-replication/src/test/java/org/dizitart/no2/integration/TestUtils.java rename to nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java index 2091323b5..377daa7e1 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/TestUtils.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java @@ -1,22 +1,22 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.integration; +package org.dizitart.no2; -import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.common.Constants; @@ -74,6 +74,7 @@ public static Document trimMeta(Document document) { document.remove(Constants.DOC_REVISION); document.remove(Constants.DOC_MODIFIED); document.remove(Constants.DOC_SOURCE); + document.remove("_synced"); return document; } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index d243415ac..538973098 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -33,7 +33,7 @@ import java.nio.file.Path; import static org.dizitart.no2.collection.Document.createDocument; -import static org.dizitart.no2.integration.TestUtils.createDb; +import static org.dizitart.no2.TestUtils.createDb; /** * @author Anindya Chatterjee @@ -59,7 +59,7 @@ public static void main(String[] args) { String jwt = getToken(); - System.out.println("Token - " + jwt); + log.info("Token - " + jwt); Replica replica = Replica.builder() .of(collection) .remote("wss://127.0.0.1:3030/ws/datagate/abcd@gmail.com/datagateIntegration") @@ -75,15 +75,9 @@ public static void main(String[] args) { // }); replica.connect(); - System.out.println("Connected"); Thread.sleep(10000); - System.out.println("Completed"); - System.out.println("Collection Size - " + collection.size()); - for (Document d : collection.find()) { - System.out.println(d); - } } catch (Exception e) { - e.printStackTrace(); + log.error("Exception during replication", e); } finally { try { if (dbPath != null) { diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/server/SimpleDataGateEndpoint.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/server/SimpleDataGateEndpoint.java deleted file mode 100644 index d83bc8a28..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/server/SimpleDataGateEndpoint.java +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.integration.server; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.websocket.*; -import jakarta.websocket.server.PathParam; -import jakarta.websocket.server.ServerEndpoint; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.store.NitriteMap; -import org.dizitart.no2.sync.MessageFactory; -import org.dizitart.no2.sync.MessageTransformer; -import org.dizitart.no2.sync.ReplicationException; -import org.dizitart.no2.sync.crdt.LastWriteWinMap; -import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.dizitart.no2.sync.message.*; -import java.io.IOException; -import java.util.*; - -/** - * @author Anindya Chatterjee - */ -@Slf4j -@Data -@ServerEndpoint(value = "/datagate/{user}/{collection}") -public class SimpleDataGateEndpoint { - private ObjectMapper objectMapper; - private Repository repository; - private MessageFactory factory; - private MessageTransformer transformer; - - public SimpleDataGateEndpoint() { - objectMapper = new ObjectMapper(); - repository = Repository.getInstance(); - factory = new MessageFactory(); - transformer = new MessageTransformer(objectMapper); - } - - @OnOpen - public void onOpen(@PathParam("user") String user, - @PathParam("collection") String collection, - Session session) { - log.info("DataGate server connection established"); - session.getUserProperties().put("collection", user + "@" + collection); - session.getUserProperties().put("authorized", false); - } - - @OnClose - public void onClose(CloseReason reason, Session session) { - log.warn("DataGate server closed due to {}", reason.getReasonPhrase()); - repository.getAuthorizedSessions().remove(session); - } - - @OnMessage - public void onMessage(String message, Session session) { - try { - log.info("Message received at server {}", message); - DataGateMessage dataGateMessage = transformer.transform(message); - if (dataGateMessage instanceof Connect) { - Connect connect = (Connect) dataGateMessage; - handleConnect(session, connect); - } else if (dataGateMessage instanceof BatchChangeStart) { - BatchChangeStart batchChangeStart = (BatchChangeStart) dataGateMessage; - handleBatchChangeStart(session, batchChangeStart); - } else if (dataGateMessage instanceof BatchChangeContinue) { - BatchChangeContinue batchChangeContinue = (BatchChangeContinue) dataGateMessage; - handleBatchChangeContinue(session, batchChangeContinue); - } else if (dataGateMessage instanceof BatchChangeEnd) { - BatchChangeEnd batchChangeEnd = (BatchChangeEnd) dataGateMessage; - handleBatchChangeEnd(session, batchChangeEnd); - } else if (dataGateMessage instanceof DataGateFeed) { - DataGateFeed dataGateFeed = (DataGateFeed) dataGateMessage; - handleDataGateFeed(session, dataGateFeed); - } else if (dataGateMessage instanceof Disconnect) { - Disconnect disconnect = (Disconnect) dataGateMessage; - handleDisconnect(session, disconnect); - } - } catch (Exception e) { - log.error("Error while handling message {}", message, e); - } - } - - @OnError - public void onError(Session session, Throwable ex) { - log.error("Error in DataGate server", ex); - - try { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setHeader(createHeader(MessageType.Error, null, null, - repository.getServerId(), "")); - errorMessage.setError(ex.getMessage()); - String message = objectMapper.writeValueAsString(errorMessage); - session.getBasicRemote().sendText(message); - } catch (Exception e) { - throw new ReplicationException("failed to send ErrorMessage", e, false); - } - } - - protected void handleConnect(Session session, Connect connect) throws IOException { - String replicaId = connect.getHeader().getOrigin(); - String userName = connect.getHeader().getUserName(); - String collection = userName + "@" + connect.getHeader().getCollection(); - - if (isValidAuth(userName, connect.getAuthToken())) { - session.getUserProperties().put("authorized", true); - session.getUserProperties().put("collection", collection); - session.getUserProperties().put("replica", replicaId); - - repository.getAuthorizedSessions().add(session); - - if (repository.getCollectionReplicaMap().containsKey(collection)) { - List replicas = repository.getCollectionReplicaMap().get(collection); - if (!replicas.contains(replicaId)) { - replicas.add(replicaId); - } - repository.getCollectionReplicaMap().put(collection, replicas); - } else { - List replicas = new ArrayList<>(); - replicas.add(replicaId); - repository.getCollectionReplicaMap().put(collection, replicas); - } - - if (repository.getUserReplicaMap().containsKey(userName)) { - List replicas = repository.getUserReplicaMap().get(userName); - if (!replicas.contains(replicaId)) { - replicas.add(replicaId); - } - repository.getUserReplicaMap().put(userName, replicas); - } else { - List replicas = new ArrayList<>(); - replicas.add(replicaId); - repository.getUserReplicaMap().put(userName, replicas); - } - - if (!repository.getReplicaStore().containsKey(collection)) { - LastWriteWinMap replica = createCrdt(collection); - repository.getReplicaStore().put(collection, replica); - } - - ConnectAck ack = new ConnectAck(); - ack.setHeader(createHeader(MessageType.ConnectAck, - connect.getHeader().getCollection(), userName, - repository.getServerId(), connect.getHeader().getId())); - ack.setTombstoneTtl(repository.getGcTtl()); - String message = objectMapper.writeValueAsString(ack); - session.getBasicRemote().sendText(message); - } else { - session.getUserProperties().put("authorized", false); - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setError("Unauthorized"); - errorMessage.setHeader(createHeader(MessageType.Error, - connect.getHeader().getCollection(), userName, - repository.getServerId(), connect.getHeader().getId())); - String message = objectMapper.writeValueAsString(errorMessage); - session.getBasicRemote().sendText(message); - } - } - - protected void handleDisconnect(Session session, Disconnect connect) { - String replicaId = connect.getHeader().getOrigin(); - String userName = connect.getHeader().getUserName(); - String collection = userName + "@" + connect.getHeader().getCollection(); - - repository.getCollectionReplicaMap().get(collection).remove(replicaId); - repository.getUserReplicaMap().get(userName).remove(replicaId); - repository.getAuthorizedSessions().remove(session); - } - - protected void handleDataGateFeed(Session channel, DataGateFeed feed) { - String userName = feed.getHeader().getUserName(); - String collection = userName + "@" + feed.getHeader().getCollection(); - String replicaId = feed.getHeader().getOrigin(); - - LastWriteWinMap replica = repository.getReplicaStore().get(collection); - replica.merge(feed.getFeed()); - - try { - Long syncTime = System.currentTimeMillis(); - String ackMessage = createAck(feed.getHeader().getCollection(), userName, - syncTime, feed.calculateReceipt(), feed.getHeader().getId()); - channel.getBasicRemote().sendText(ackMessage); - - // other peers will take this time as last sync times - feed.getHeader().setTimestamp(syncTime); - String message = objectMapper.writeValueAsString(feed); - broadcast(replicaId, collection, message); - } catch (Exception e) { - throw new ReplicationException("failed to broadcast DataGateFeed", e, false); - } - } - - private void broadcast(String origin, String collection, String message) { - repository.getAuthorizedSessions().stream() - .filter(s -> collection.equals(s.getUserProperties().get("collection"))) - .filter(s -> !origin.equals(s.getUserProperties().get("replica"))) - .forEach(s -> s.getAsyncRemote().sendText(message)); - } - - protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeEnd) throws IOException { - Long lastSync = batchChangeEnd.getLastSynced(); - Integer batchSize = batchChangeEnd.getBatchSize(); - Integer debounce = batchChangeEnd.getDebounce(); - String userName = batchChangeEnd.getHeader().getUserName(); - String collection = userName + "@" + batchChangeEnd.getHeader().getCollection(); - - BatchEndAck ack = new BatchEndAck(); - ack.setHeader(createHeader(MessageType.BatchEndAck, batchChangeEnd.getHeader().getCollection(), - userName, repository.getServerId(), batchChangeEnd.getHeader().getId())); - - String message = objectMapper.writeValueAsString(ack); - session.getBasicRemote().sendText(message); - - LastWriteWinMap replica = repository.getReplicaStore().get(collection); - sendChanges(batchChangeEnd.getHeader().getCollection(), userName, lastSync, - batchSize, debounce, replica, session, repository.getServerId()); - } - - protected void handleBatchChangeContinue(Session session, BatchChangeContinue batchChangeContinue) { - DataGateFeed feed = new DataGateFeed(); - - String userName = batchChangeContinue.getHeader().getUserName(); - String collection = userName + "@" + batchChangeContinue.getHeader().getCollection(); - String replicaId = batchChangeContinue.getHeader().getOrigin(); - LastWriteWinMap replica = repository.getReplicaStore().get(collection); - replica.merge(batchChangeContinue.getFeed()); - - feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeContinue.getHeader().getCollection(), - userName, replicaId, batchChangeContinue.getHeader().getId())); - feed.setFeed(batchChangeContinue.getFeed()); - - BatchAck ack = new BatchAck(); - ack.setReceipt(feed.calculateReceipt()); - ack.setHeader(createHeader(MessageType.BatchAck, batchChangeContinue.getHeader().getCollection(), - userName, repository.getServerId(), batchChangeContinue.getHeader().getId())); - - try { - String message = objectMapper.writeValueAsString(ack); - session.getBasicRemote().sendText(message); - - message = objectMapper.writeValueAsString(feed); - broadcast(replicaId, collection, message); - } catch (Exception e) { - throw new ReplicationException("failed to broadcast DataGateFeed", e, false); - } - } - - protected void handleBatchChangeStart(Session session, BatchChangeStart batchChangeStart) { - log.debug("BatchChangeStart message received " + batchChangeStart); - DataGateFeed feed = new DataGateFeed(); - - String userName = batchChangeStart.getHeader().getUserName(); - String collection = userName + "@" + batchChangeStart.getHeader().getCollection(); - String replicaId = batchChangeStart.getHeader().getOrigin(); - LastWriteWinMap replica = repository.getReplicaStore().get(collection); - replica.merge(batchChangeStart.getFeed()); - - feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeStart.getHeader().getCollection(), - userName, replicaId, batchChangeStart.getHeader().getId())); - feed.setFeed(batchChangeStart.getFeed()); - - BatchAck ack = new BatchAck(); - ack.setReceipt(feed.calculateReceipt()); - ack.setHeader(createHeader(MessageType.BatchAck, batchChangeStart.getHeader().getCollection(), - userName, repository.getServerId(), batchChangeStart.getHeader().getId())); - - try { - String message = objectMapper.writeValueAsString(ack); - session.getBasicRemote().sendText(message); - - message = objectMapper.writeValueAsString(feed); - broadcast(replicaId, collection, message); - } catch (Exception e) { - throw new ReplicationException("failed to broadcast DataGateFeed", e, false); - } - } - - private LastWriteWinMap createCrdt(String collection) { - NitriteCollection nc = repository.getDb().getCollection(collection); - NitriteMap nitriteMap = - repository.getDb().getConfig().getNitriteStore().openMap(collection + "-replica", - NitriteId.class, Long.class); - return new LastWriteWinMap(nc, nitriteMap); - } - - private void sendChanges(String collection, String userName, - Long lastSyncTime, Integer chunkSize, - Integer debounce, LastWriteWinMap crdt, - Session channel, String replicaId) { - try { - try { - String initMessage = createChangeStart(crdt, lastSyncTime, collection, userName, - chunkSize, debounce); - log.info("Sending BatchChangeStart message {} from server to {}", initMessage, replicaId); - channel.getBasicRemote().sendText(initMessage); - } catch (Exception e) { - log.error("Error while sending BatchChangeStart to " + replicaId, e); - } - - final Timer timer = new Timer(); - timer.scheduleAtFixedRate(new TimerTask() { - boolean hasMore = true; - int start = chunkSize; - - @Override - public void run() { - LastWriteWinState state = crdt.getChangesSince(lastSyncTime, start, chunkSize); - if (state.getChanges().size() == 0 && state.getTombstones().size() == 0) { - hasMore = false; - } - - if (hasMore) { - try { - String message = createChangeContinue(state, collection, userName, - chunkSize, debounce); - log.info("Sending BatchChangeContinue message {} from server to {}", message, replicaId); - channel.getBasicRemote().sendText(message); - } catch (Exception e) { - log.error("Error while sending BatchChangeContinue for " + replicaId, e); - } - - start = start + chunkSize; - } - - if (!hasMore) { - timer.cancel(); - } - } - }, 0, debounce); - - try { - String endMessage = createChangeEnd(collection, userName, - chunkSize, debounce); - log.info("Sending BatchChangeEnd message {} from server to {}", endMessage, replicaId); - channel.getBasicRemote().sendText(endMessage); - } catch (Exception e) { - log.error("Error while sending BatchChangeEnd for " + replicaId, e); - } - } catch (Exception e) { - throw new ReplicationException("failed to send local changes message for " + replicaId, e, false); - } - } - - private String createChangeStart(LastWriteWinMap crdt, Long lastSyncTime, - String collection, String userName, - Integer chunkSize, Integer debounce) { - try { - BatchChangeStart message = new BatchChangeStart(); - message.setHeader(createHeader(MessageType.BatchChangeStart, - collection, userName, repository.getServerId(), "")); - message.setBatchSize(chunkSize); - message.setDebounce(debounce); - - LastWriteWinState state = crdt.getChangesSince(lastSyncTime, 0, chunkSize); - message.setFeed(state); - return objectMapper.writeValueAsString(message); - } catch (JsonProcessingException e) { - throw new ReplicationException("failed to create BatchChangeStart message", e, false); - } - } - - private String createChangeContinue(LastWriteWinState state, - String collection, String userName, - Integer chunkSize, Integer debounce) { - try { - BatchChangeContinue message = new BatchChangeContinue(); - message.setHeader(createHeader(MessageType.BatchChangeContinue, - collection, userName, repository.getServerId(), "")); - message.setFeed(state); - message.setBatchSize(chunkSize); - message.setDebounce(debounce); - return objectMapper.writeValueAsString(message); - } catch (JsonProcessingException e) { - throw new ReplicationException("failed to create BatchChangeContinue message", e, false); - } - } - - private String createChangeEnd(String collection, String userName, - Integer chunkSize, Integer debounce) { - try { - BatchChangeEnd message = new BatchChangeEnd(); - message.setHeader(createHeader(MessageType.BatchChangeEnd, - collection, userName, repository.getServerId(), "")); - message.setLastSynced(System.currentTimeMillis()); - message.setBatchSize(chunkSize); - message.setDebounce(debounce); - return objectMapper.writeValueAsString(message); - } catch (JsonProcessingException e) { - throw new ReplicationException("failed to create BatchChangeEnd message", e, false); - } - } - - private String createAck(String collection, String userName, Long syncTime, Receipt receipt, String corrId) { - try { - DataGateFeedAck ack = new DataGateFeedAck(); - MessageHeader header = createHeader(MessageType.DataGateFeedAck, collection, - userName, repository.getServerId(), corrId); - header.setTimestamp(syncTime); - ack.setHeader(header); - ack.setReceipt(receipt); - return objectMapper.writeValueAsString(ack); - } catch (JsonProcessingException e) { - throw new ReplicationException("failed to create DataGateAck message", e, false); - } - } - - private MessageHeader createHeader(MessageType messageType, String collection, - String userName, String origin, String corrId) { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setId(UUID.randomUUID().toString()); - messageHeader.setCorrelationId(corrId); - messageHeader.setCollection(collection); - messageHeader.setMessageType(messageType); - messageHeader.setOrigin(origin); - messageHeader.setTimestamp(System.currentTimeMillis()); - messageHeader.setUserName(userName); - return messageHeader; - } - - private boolean isValidAuth(String userName, String authToken) { - return repository.getUserMap().get(userName).equals(authToken); - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/ReplicaNegativeTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java similarity index 67% rename from nitrite-replication/src/test/java/org/dizitart/no2/integration/ReplicaNegativeTest.java rename to nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java index 50e7c89e8..d405c4d02 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/ReplicaNegativeTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java @@ -1,29 +1,31 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.integration; +package org.dizitart.no2.mock; import org.dizitart.no2.Nitrite; +import org.dizitart.no2.Retry; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.common.concurrent.ThreadPoolManager; +import org.dizitart.no2.mock.server.MockDataGateServer; +import org.dizitart.no2.mock.server.MockRepository; +import org.dizitart.no2.mock.server.ServerLastWriteWinMap; import org.dizitart.no2.sync.Replica; -import org.dizitart.no2.sync.crdt.LastWriteWinMap; -import org.dizitart.no2.integration.server.Repository; -import org.dizitart.no2.integration.server.SimpleDataGateServer; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -33,9 +35,9 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static org.awaitility.Awaitility.await; -import static org.dizitart.no2.integration.ReplicaTest.getRandomTempDbFile; -import static org.dizitart.no2.integration.TestUtils.createDb; -import static org.dizitart.no2.integration.TestUtils.randomDocument; +import static org.dizitart.no2.TestUtils.createDb; +import static org.dizitart.no2.TestUtils.randomDocument; +import static org.dizitart.no2.mock.ReplicaTest.getRandomTempDbFile; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -43,10 +45,10 @@ * @author Anindya Chatterjee */ public class ReplicaNegativeTest { - private SimpleDataGateServer server; + private MockDataGateServer server; private String dbFile; private ExecutorService executorService; - private Repository repository; + private MockRepository mockRepository; @Rule public Retry retry = new Retry(3); @@ -54,10 +56,10 @@ public class ReplicaNegativeTest { @Before public void setUp() throws Exception { dbFile = getRandomTempDbFile(); - server = new SimpleDataGateServer(9090); + server = new MockDataGateServer(9090); executorService = ThreadPoolManager.getThreadPool(2, "ReplicaNegativeTest"); server.start(); - repository = Repository.getInstance(); + mockRepository = MockRepository.getInstance(); } @After @@ -70,7 +72,7 @@ public void cleanUp() { @Test public void testServerClose() { - repository.getUserMap().put("anidotnet", "abcd"); + mockRepository.getUserMap().put("anidotnet", "abcd"); Nitrite db1 = createDb(dbFile); @@ -91,11 +93,11 @@ public void testServerClose() { } }); - await().atMost(5, SECONDS).until(() -> repository.getCollectionReplicaMap().size() == 1); - assertEquals(repository.getUserReplicaMap().size(), 1); - assertTrue(repository.getUserReplicaMap().containsKey("anidotnet")); - assertTrue(repository.getCollectionReplicaMap().containsKey("anidotnet@testServerClose")); - LastWriteWinMap lastWriteWinMap = repository.getReplicaStore().get("anidotnet@testServerClose"); + await().atMost(5, SECONDS).until(() -> mockRepository.getCollectionReplicaMap().size() == 1); + assertEquals(mockRepository.getUserReplicaMap().size(), 1); + assertTrue(mockRepository.getUserReplicaMap().containsKey("anidotnet")); + assertTrue(mockRepository.getCollectionReplicaMap().containsKey("anidotnet@testServerClose")); + ServerLastWriteWinMap lastWriteWinMap = mockRepository.getReplicaStore().get("anidotnet@testServerClose"); await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().find().size() == 10); server.stop(); diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/ReplicaTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java similarity index 82% rename from nitrite-replication/src/test/java/org/dizitart/no2/integration/ReplicaTest.java rename to nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java index 4e1e33252..0539e1fca 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/ReplicaTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java @@ -1,53 +1,57 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.integration; +package org.dizitart.no2.mock; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.Nitrite; +import org.dizitart.no2.TestUtils; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.filters.Filter; -import org.dizitart.no2.integration.server.Repository; -import org.dizitart.no2.integration.server.SimpleDataGateServer; +import org.dizitart.no2.mock.server.MockDataGateServer; +import org.dizitart.no2.mock.server.MockRepository; +import org.dizitart.no2.mock.server.ServerLastWriteWinMap; import org.dizitart.no2.sync.Replica; -import org.dizitart.no2.sync.ReplicationTemplate; +import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.crdt.LastWriteWinMap; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import java.io.File; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; import java.util.Random; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; import static java.util.concurrent.TimeUnit.SECONDS; import static org.awaitility.Awaitility.await; +import static org.dizitart.no2.TestUtils.createDb; +import static org.dizitart.no2.TestUtils.randomDocument; import static org.dizitart.no2.collection.Document.createDocument; import static org.dizitart.no2.common.util.DocumentUtils.isSimilar; import static org.dizitart.no2.filters.FluentFilter.where; -import static org.dizitart.no2.integration.TestUtils.createDb; -import static org.dizitart.no2.integration.TestUtils.randomDocument; import static org.junit.Assert.*; /** @@ -55,12 +59,12 @@ */ @Slf4j public class ReplicaTest { - private static SimpleDataGateServer server; - @Rule - public Retry retry = new Retry(3); + private static MockDataGateServer server; +// @Rule +// public Retry retry = new Retry(3); private String dbFile; private ExecutorService executorService; - private Repository repository; + private MockRepository mockRepository; private Nitrite db; public static String getRandomTempDbFile() { @@ -69,27 +73,29 @@ public static String getRandomTempDbFile() { if (!file.exists()) { assertTrue(file.mkdirs()); } - return file.getPath() + File.separator + UUID.randomUUID().toString() + ".db"; + return file.getPath() + File.separator + UUID.randomUUID() + ".db"; } @Before public void setUp() throws Exception { - server = new SimpleDataGateServer(9090); + server = new MockDataGateServer(9090); server.start(); dbFile = getRandomTempDbFile(); executorService = Executors.newCachedThreadPool(); - repository = Repository.getInstance(); + mockRepository = MockRepository.getInstance(); } @After public void cleanUp() throws Exception { - executorService.awaitTermination(2, SECONDS); - executorService.shutdown(); + if(!executorService.awaitTermination(2, SECONDS)) { + executorService.shutdown(); + } if (db != null && !db.isClosed()) { db.close(); } + mockRepository.reset(); if (Files.exists(Paths.get(dbFile))) { Files.delete(Paths.get(dbFile)); } @@ -98,7 +104,7 @@ public void cleanUp() throws Exception { @Test public void testSingleUserSingleReplica() { - repository.getUserMap().put("anidotnet", "abcd"); + mockRepository.getUserMap().put("anidotnet", "abcd"); db = createDb(dbFile); NitriteCollection collection = db.getCollection("testSingleUserSingleReplica"); @@ -117,11 +123,11 @@ public void testSingleUserSingleReplica() { replica.connect(); - await().atMost(5, SECONDS).until(() -> repository.getCollectionReplicaMap().size() == 1); - assertEquals(repository.getUserReplicaMap().size(), 1); - assertTrue(repository.getUserReplicaMap().containsKey("anidotnet")); - assertTrue(repository.getCollectionReplicaMap().containsKey("anidotnet@testSingleUserSingleReplica")); - LastWriteWinMap lastWriteWinMap = repository.getReplicaStore().get("anidotnet@testSingleUserSingleReplica"); + await().atMost(5, SECONDS).until(() -> mockRepository.getCollectionReplicaMap().size() == 1); + assertEquals(mockRepository.getUserReplicaMap().size(), 1); + assertTrue(mockRepository.getUserReplicaMap().containsKey("anidotnet")); + assertTrue(mockRepository.getCollectionReplicaMap().containsKey("anidotnet@testSingleUserSingleReplica")); + ServerLastWriteWinMap lastWriteWinMap = mockRepository.getReplicaStore().get("anidotnet@testSingleUserSingleReplica"); await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().find().size() == 1); Document doc = lastWriteWinMap.getCollection().find(where("firstName").eq("Anindya")).firstOrNull(); @@ -149,11 +155,13 @@ public void testSingleUserSingleReplica() { await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().size() == 0); doc = lastWriteWinMap.getCollection().find(where("firstName").eq("Anindya")).firstOrNull(); assertNull(doc); + + replica.disconnectNow(); } @Test public void testSingleUserMultiReplica() { - repository.getUserMap().put("anidotnet", "abcd"); + mockRepository.getUserMap().put("anidotnet", "abcd"); db = createDb(dbFile); @@ -166,12 +174,14 @@ public void testSingleUserMultiReplica() { .of(c1) .remote("ws://127.0.0.1:9090/datagate/anidotnet/testSingleUserMultiReplica") .jwtAuth("anidotnet", "abcd") + .replicaName("r1") .create(); Replica r2 = Replica.builder() .of(c2) .remote("ws://127.0.0.1:9090/datagate/anidotnet/testSingleUserMultiReplica") .jwtAuth("anidotnet", "abcd") + .replicaName("r2") .create(); r1.connect(); @@ -200,6 +210,7 @@ public void testSingleUserMultiReplica() { e.printStackTrace(); } } + System.out.println("All r1 data inserted"); }); executorService.submit(() -> { @@ -212,9 +223,15 @@ public void testSingleUserMultiReplica() { e.printStackTrace(); } } + System.out.println("All r2 data inserted"); }); - await().atMost(10, SECONDS).until(() -> c1.size() == 40); + await().atMost(10, SECONDS).until(() -> { + System.out.println("C1 Size - " + c1.size()); + System.out.println(c1.find().toList()); +// FIXME: Always coming as 30, check batch continue logic and scrutinize logs + return c1.size() == 40; + }); assertEquals(c2.size(), 40); r1.disconnect(); @@ -254,13 +271,16 @@ public void testSingleUserMultiReplica() { await().atMost(10, SECONDS).until(() -> c2.size() == 0); await().atMost(5, SECONDS).until(() -> c1.size() == 0); TestUtils.assertEquals(c1, c2); + + r1.disconnectNow(); + r2.disconnectNow(); } @Test public void testMultiUserSingleReplica() { - repository.getUserMap().put("user1", "abcd"); - repository.getUserMap().put("user2", "abcd"); - repository.getUserMap().put("user3", "abcd"); + mockRepository.getUserMap().put("user1", "abcd"); + mockRepository.getUserMap().put("user2", "abcd"); + mockRepository.getUserMap().put("user3", "abcd"); Nitrite db1 = createDb(); NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica"); @@ -318,12 +338,16 @@ public void testMultiUserSingleReplica() { TestUtils.assertNotEquals(c1, c2); TestUtils.assertNotEquals(c1, c3); TestUtils.assertNotEquals(c2, c3); + + r1.disconnectNow(); + r2.disconnectNow(); + r3.disconnectNow(); } @Test public void testMultiUserMultiReplica() { - repository.getUserMap().put("user1", "abcd"); - repository.getUserMap().put("user2", "abcd"); + mockRepository.getUserMap().put("user1", "abcd"); + mockRepository.getUserMap().put("user2", "abcd"); Nitrite db1 = createDb(); NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica1"); @@ -362,11 +386,13 @@ public void testMultiUserMultiReplica() { await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20); TestUtils.assertNotEquals(c1, c2); + r1.disconnectNow(); + r2.disconnectNow(); } @Test public void testSecurityInCorrectCredentials() { - repository.getUserMap().put("user", "abcd"); + mockRepository.getUserMap().put("user", "abcd"); Nitrite db1 = createDb(); NitriteCollection c1 = db1.getCollection("testSecurity"); @@ -385,11 +411,12 @@ public void testSecurityInCorrectCredentials() { assertEquals(c1.size(), 10); await().atMost(5, SECONDS).until(() -> !r1.isConnected()); + r1.disconnectNow(); } @Test public void testCloseDbAndReconnect() { - repository.getUserMap().put("anidotnet", "abcd"); + mockRepository.getUserMap().put("anidotnet", "abcd"); db = createDb(dbFile); @@ -497,11 +524,14 @@ public void testCloseDbAndReconnect() { await().atMost(10, SECONDS).until(() -> c2.size() == 0); await().atMost(5, SECONDS).until(() -> finalC.size() == 0); TestUtils.assertEquals(c1, c2); + + r1.disconnectNow(); + r2.disconnectNow(); } @Test public void testDelayedConnect() { - repository.getUserMap().put("anidotnet", "abcd"); + mockRepository.getUserMap().put("anidotnet", "abcd"); Nitrite db1 = createDb(dbFile); NitriteCollection c1 = db1.getCollection("testDelayedConnect"); @@ -532,11 +562,14 @@ public void testDelayedConnect() { .create(); r2.connect(); await().atMost(5, SECONDS).until(() -> c2.size() == 10); + + r1.disconnectNow(); + r2.disconnectNow(); } @Test public void testDelayedConnectRemoveAll() { - repository.getUserMap().put("anidotnet", "abcd"); + mockRepository.getUserMap().put("anidotnet", "abcd"); db = createDb(dbFile); NitriteCollection c1 = db.getCollection("testDelayedConnect"); @@ -544,21 +577,25 @@ public void testDelayedConnectRemoveAll() { .of(c1) .remote("ws://127.0.0.1:9090/datagate/anidotnet/testDelayedConnect") .jwtAuth("anidotnet", "abcd") + .replicaName("r1") .create(); r1.connect(); + System.out.println("r1 connected"); for (int i = 0; i < 10; i++) { Document document = randomDocument(); c1.insert(document); } - await().atMost(5, SECONDS).until(() -> c1.size() == 10); + c1.remove(Filter.ALL); + System.out.println("Removed all"); assertEquals(c1.size(), 0); r1.disconnect(); r1.close(); db.close(); + System.out.println("r1 disconnected"); Nitrite db2 = createDb(); NitriteCollection c2 = db2.getCollection("testDelayedConnect"); @@ -566,8 +603,11 @@ public void testDelayedConnectRemoveAll() { .of(c2) .remote("ws://127.0.0.1:9090/datagate/anidotnet/testDelayedConnect") .jwtAuth("anidotnet", "abcd") + .replicaName("r2") .create(); + r2.connect(); + System.out.println("r2 connected"); for (int i = 0; i < 5; i++) { Document document = randomDocument(); @@ -580,18 +620,24 @@ public void testDelayedConnectRemoveAll() { .of(c3) .remote("ws://127.0.0.1:9090/datagate/anidotnet/testDelayedConnect") .jwtAuth("anidotnet", "abcd") + .replicaName("r1") .create(); r1.connect(); - await().atMost(5, SECONDS).until(() -> c3.size() == 5 && c2.size() == 5); - TestUtils.assertEquals(c3, c2); - LastWriteWinMap lastWriteWinMap = repository.getReplicaStore().get("anidotnet@testDelayedConnect"); - assertEquals(lastWriteWinMap.getTombstones().size(), 10); + System.out.println("r1 connected again"); + await().atMost(5, SECONDS).until(() -> { + List l1 = c3.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); + List l2 = c2.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); + return l1.equals(l2); + }); + + r1.disconnectNow(); + r2.disconnectNow(); } @Test - public void testGarbageCollect() throws InterruptedException { - repository.getUserMap().put("anidotnet", "abcd"); + public void testGarbageCollect() { + mockRepository.getUserMap().put("anidotnet", "abcd"); db = createDb(dbFile); NitriteCollection c1 = db.getCollection("testGarbageCollect"); Replica r1 = Replica.builder() @@ -614,7 +660,7 @@ public void testGarbageCollect() throws InterruptedException { r1.close(); db.close(); - repository.setGcTtl(1L); + mockRepository.setGcTtl(1L); db = createDb(dbFile); NitriteCollection c2 = db.getCollection("testGarbageCollect"); @@ -629,14 +675,16 @@ public void testGarbageCollect() throws InterruptedException { LastWriteWinMap lastWriteWinMap = getCrdt(r1); await().atMost(5, SECONDS).until(() -> c2.size() == 0 - && lastWriteWinMap.getTombstones().size() == 0); + && lastWriteWinMap.getTombstoneMap().size() == 0); + + r1.disconnectNow(); } @SneakyThrows private LastWriteWinMap getCrdt(Replica replica) { - Field field = Replica.class.getDeclaredField("replicationTemplate"); + Field field = Replica.class.getDeclaredField("replicatedCollection"); field.setAccessible(true); - ReplicationTemplate replicationTemplate = (ReplicationTemplate) field.get(replica); - return replicationTemplate.getCrdt(); + ReplicatedCollection replicatedCollection = (ReplicatedCollection) field.get(replica); + return replicatedCollection.getLastWriteWinMap(); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java new file mode 100644 index 000000000..385a3dfda --- /dev/null +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2017-2020. Nitrite author or authors. + * + * 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 org.dizitart.no2.mock.server; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.websocket.*; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.store.NitriteMap; +import org.dizitart.no2.sync.FeedLedger; +import org.dizitart.no2.sync.MessageFactory; +import org.dizitart.no2.sync.MessageTransformer; +import org.dizitart.no2.sync.ReplicationException; +import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.Tombstone; +import org.dizitart.no2.sync.message.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author Anindya Chatterjee + */ +@Slf4j +@Data +@ServerEndpoint(value = "/datagate/{user}/{collection}") +public class MockDataGateEndpoint { + private ObjectMapper objectMapper; + private MockRepository mockRepository; + private MessageFactory factory; + private MessageTransformer transformer; + + public MockDataGateEndpoint() { + objectMapper = new ObjectMapper(); + mockRepository = MockRepository.getInstance(); + factory = new MessageFactory(); + transformer = new MessageTransformer(objectMapper); + } + + @OnOpen + public void onOpen(@PathParam("user") String user, + @PathParam("collection") String collection, + Session session) { + log.info("DataGate server connection established"); + session.getUserProperties().put("user", user); + session.getUserProperties().put("collection", user + "@" + collection); + session.getUserProperties().put("authorized", false); + } + + @OnClose + public void onClose(CloseReason reason, Session session) { + log.warn("DataGate server closed due to {}", reason.getReasonPhrase()); + mockRepository.getAuthorizedSessions().remove(session); + } + + @OnMessage + public void onMessage(String message, Session session) throws IOException { + try { + log.info("Message received at server {}", message); + DataGateMessage dataGateMessage = transformer.transform(message); + if (dataGateMessage instanceof Connect) { + Connect connect = (Connect) dataGateMessage; + handleConnect(session, connect); + } else if (dataGateMessage instanceof BatchChangeStart) { + checkAuthorized(session); + BatchChangeStart batchChangeStart = (BatchChangeStart) dataGateMessage; + handleBatchChangeStart(session, batchChangeStart); + } else if (dataGateMessage instanceof BatchChangeContinue) { + checkAuthorized(session); + BatchChangeContinue batchChangeContinue = (BatchChangeContinue) dataGateMessage; + handleBatchChangeContinue(session, batchChangeContinue); + } else if (dataGateMessage instanceof BatchChangeEnd) { + checkAuthorized(session); + BatchChangeEnd batchChangeEnd = (BatchChangeEnd) dataGateMessage; + handleBatchChangeEnd(session, batchChangeEnd); + } else if (dataGateMessage instanceof DataGateFeed) { + checkAuthorized(session); + DataGateFeed dataGateFeed = (DataGateFeed) dataGateMessage; + handleDataGateFeed(session, dataGateFeed); + } else if (dataGateMessage instanceof BatchAck) { + checkAuthorized(session); + BatchAck batchAck = (BatchAck) dataGateMessage; + handleBatchAck(session, batchAck); + } else if (dataGateMessage instanceof BatchEndAck) { + checkAuthorized(session); + BatchEndAck batchEndAck = (BatchEndAck) dataGateMessage; + handleBatchEndAck(session, batchEndAck); + } else if (dataGateMessage instanceof Disconnect) { + checkAuthorized(session); + Disconnect disconnect = (Disconnect) dataGateMessage; + handleDisconnect(session, disconnect); + } + } catch (Exception e) { + log.error("Error while handling message {}", message, e); + e.printStackTrace(); + sendErrorMessage(session, e); + } + } + + @OnError + public void onError(Session session, Throwable ex) { + log.error("Error in DataGate server", ex); + + try { + ErrorMessage errorMessage = new ErrorMessage(); + errorMessage.setHeader(createHeader(MessageType.Error, null, null, + mockRepository.getServerId(), "")); + errorMessage.setError(ex.getMessage()); + String message = objectMapper.writeValueAsString(errorMessage); + session.getBasicRemote().sendText(message); + } catch (Exception e) { + throw new ReplicationException("failed to send ErrorMessage", e, false); + } + } + + protected void handleConnect(Session session, Connect connect) throws IOException { + String replicaId = connect.getHeader().getOrigin(); + String userName = connect.getHeader().getUserName(); + String collection = userName + "@" + connect.getHeader().getCollection(); + + if (isValidAuth(userName, connect.getAuthToken())) { + session.getUserProperties().put("authorized", true); + session.getUserProperties().put("collection", collection); + session.getUserProperties().put("replica", replicaId); + + mockRepository.getAuthorizedSessions().add(session); + + List replicas; + if (mockRepository.getCollectionReplicaMap().containsKey(collection)) { + replicas = mockRepository.getCollectionReplicaMap().get(collection); + if (!replicas.contains(replicaId)) { + replicas.add(replicaId); + } + } else { + replicas = new ArrayList<>(); + replicas.add(replicaId); + } + mockRepository.getCollectionReplicaMap().put(collection, replicas); + + if (mockRepository.getUserReplicaMap().containsKey(userName)) { + replicas = mockRepository.getUserReplicaMap().get(userName); + if (!replicas.contains(replicaId)) { + replicas.add(replicaId); + } + } else { + replicas = new ArrayList<>(); + replicas.add(replicaId); + } + mockRepository.getUserReplicaMap().put(userName, replicas); + + if (!mockRepository.getReplicaStore().containsKey(collection)) { + ServerLastWriteWinMap replica = createCrdt(collection); + mockRepository.getReplicaStore().put(collection, replica); + } + + ConnectAck ack = new ConnectAck(); + ack.setHeader(createHeader(MessageType.ConnectAck, + connect.getHeader().getCollection(), userName, + mockRepository.getServerId(), connect.getHeader().getCorrelationId())); + ack.setTombstoneTtl(mockRepository.getGcTtl()); + String message = objectMapper.writeValueAsString(ack); + session.getBasicRemote().sendText(message); + } else { + session.getUserProperties().put("authorized", false); + ErrorMessage errorMessage = new ErrorMessage(); + errorMessage.setError("Unauthorized"); + errorMessage.setHeader(createHeader(MessageType.Error, + connect.getHeader().getCollection(), userName, + mockRepository.getServerId(), connect.getHeader().getCorrelationId())); + String message = objectMapper.writeValueAsString(errorMessage); + session.getBasicRemote().sendText(message); + } + } + + protected void handleBatchChangeStart(Session session, BatchChangeStart batchChangeStart) throws IOException { + log.debug("BatchChangeStart message received " + batchChangeStart); + DataGateFeed feed = new DataGateFeed(); + + String userName = batchChangeStart.getHeader().getUserName(); + String collection = userName + "@" + batchChangeStart.getHeader().getCollection(); + String replicaId = batchChangeStart.getHeader().getOrigin(); + ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); + replica.merge(batchChangeStart.getFeed()); + + feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeStart.getHeader().getCollection(), + userName, replicaId, batchChangeStart.getHeader().getCorrelationId())); + feed.setFeed(batchChangeStart.getFeed()); + + BatchAck ack = new BatchAck(); + ack.setReceipt(feed.calculateReceipt()); + ack.setHeader(createHeader(MessageType.BatchAck, batchChangeStart.getHeader().getCollection(), + userName, mockRepository.getServerId(), batchChangeStart.getHeader().getCorrelationId())); + ack.setStartTime(batchChangeStart.getStartTime()); + ack.setEndTime(batchChangeStart.getEndTime()); + + String message = objectMapper.writeValueAsString(ack); + session.getBasicRemote().sendText(message); + } + + protected void handleBatchChangeContinue(Session session, BatchChangeContinue batchChangeContinue) throws IOException { + DataGateFeed feed = new DataGateFeed(); + + String userName = batchChangeContinue.getHeader().getUserName(); + String collection = userName + "@" + batchChangeContinue.getHeader().getCollection(); + String replicaId = batchChangeContinue.getHeader().getOrigin(); + ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); + replica.merge(batchChangeContinue.getFeed()); + + feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeContinue.getHeader().getCollection(), + userName, replicaId, batchChangeContinue.getHeader().getCorrelationId())); + feed.setFeed(batchChangeContinue.getFeed()); + + BatchAck ack = new BatchAck(); + ack.setReceipt(feed.calculateReceipt()); + ack.setHeader(createHeader(MessageType.BatchAck, batchChangeContinue.getHeader().getCollection(), + userName, mockRepository.getServerId(), batchChangeContinue.getHeader().getCorrelationId())); + ack.setStartTime(batchChangeContinue.getStartTime()); + ack.setEndTime(batchChangeContinue.getEndTime()); + + String message = objectMapper.writeValueAsString(ack); + session.getBasicRemote().sendText(message); + } + + protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeEnd) throws IOException { + Integer batchSize = batchChangeEnd.getBatchSize(); + Integer debounce = batchChangeEnd.getDebounce(); + String userName = batchChangeEnd.getHeader().getUserName(); + String collection = userName + "@" + batchChangeEnd.getHeader().getCollection(); + + BatchEndAck ack = new BatchEndAck(); + ack.setHeader(createHeader(MessageType.BatchEndAck, batchChangeEnd.getHeader().getCollection(), + userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getCorrelationId())); + ack.setStartTime(batchChangeEnd.getStartTime()); + ack.setEndTime(batchChangeEnd.getEndTime()); + + String message = objectMapper.writeValueAsString(ack); + session.getBasicRemote().sendText(message); + + ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); + LastWriteWinState changesSince = replica.getChangesSince(batchChangeEnd.getStartTime(), + batchChangeEnd.getEndTime(), 0, batchSize); + + session.getUserProperties().put("offset", batchSize); + session.getUserProperties().put("batchSize", batchSize); + session.getUserProperties().put("debounce", debounce); + + BatchChangeStart batchChangeStart = new BatchChangeStart(); + batchChangeStart.setHeader(createHeader(MessageType.BatchChangeStart, + collection, userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getCorrelationId())); + batchChangeStart.setStartTime(batchChangeEnd.getStartTime()); + batchChangeStart.setEndTime(batchChangeEnd.getEndTime()); + batchChangeStart.setBatchSize(batchSize); + batchChangeStart.setDebounce(debounce); + batchChangeStart.setFeed(changesSince); + + session.getBasicRemote().sendText(objectMapper.writeValueAsString(batchChangeStart)); + } + + protected void handleBatchAck(Session session, BatchAck batchAck) throws IOException { + String userName = batchAck.getHeader().getUserName(); + String collection = userName + "@" + batchAck.getHeader().getCollection(); + Integer offset = (Integer) session.getUserProperties().get("offset"); + Integer batchSize = (Integer) session.getUserProperties().get("batchSize"); + Integer debounce = (Integer) session.getUserProperties().get("debounce"); + + Receipt receipt = batchAck.getReceipt(); + FeedLedger feedLedger = mockRepository.getFeedLedgerMap().get(collection); + if (feedLedger != null) { + feedLedger.writeOff(receipt); + mockRepository.getFeedLedgerMap().put(collection, feedLedger); + } + + ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); + LastWriteWinState changesSince = replica.getChangesSince(batchAck.getStartTime(), + batchAck.getEndTime(), offset, batchSize); + + boolean hasMore = !(changesSince.getChangeSet().size() == 0 && changesSince.getTombstoneMap().size() == 0); + if (hasMore) { + BatchChangeContinue message = new BatchChangeContinue(); + message.setHeader(createHeader(MessageType.BatchChangeContinue, + collection, userName, mockRepository.getServerId(), "")); + message.setFeed(changesSince); + message.setBatchSize(batchSize); + message.setDebounce(debounce); + message.setStartTime(batchAck.getStartTime()); + message.setEndTime(batchAck.getEndTime()); + + session.getBasicRemote().sendText(objectMapper.writeValueAsString(message)); + + session.getUserProperties().put("offset", offset + batchSize); + } else { + BatchChangeEnd message = new BatchChangeEnd(); + message.setHeader(createHeader(MessageType.BatchChangeEnd, + collection, userName, mockRepository.getServerId(), batchAck.getHeader().getCorrelationId())); + message.setBatchSize(batchSize); + message.setDebounce(debounce); + message.setStartTime(batchAck.getStartTime()); + message.setEndTime(batchAck.getEndTime()); + + session.getBasicRemote().sendText(objectMapper.writeValueAsString(message)); + session.getUserProperties().put("offset", 0); + } + } + + protected void handleDisconnect(Session session, Disconnect connect) { + String replicaId = connect.getHeader().getOrigin(); + String userName = connect.getHeader().getUserName(); + String collection = userName + "@" + connect.getHeader().getCollection(); + + mockRepository.getCollectionReplicaMap().get(collection).remove(replicaId); + mockRepository.getUserReplicaMap().get(userName).remove(replicaId); + mockRepository.getAuthorizedSessions().remove(session); + } + + protected void handleDataGateFeed(Session channel, DataGateFeed feed) { + + } + + protected void handleBatchEndAck(Session session, BatchEndAck batchEndAck) throws IOException { + // send disconnect + Disconnect disconnect = new Disconnect(); + String user = (String) session.getUserProperties().get("user"); + String collection = (String) session.getUserProperties().get("collection"); + + disconnect.setHeader(createHeader(MessageType.Disconnect, collection, user, mockRepository.getServerId(), + batchEndAck.getHeader().getCorrelationId())); + session.getBasicRemote().sendText(objectMapper.writeValueAsString(disconnect)); + + mockRepository.getCollectionReplicaMap().get(collection).remove(batchEndAck.getHeader().getOrigin()); + mockRepository.getUserReplicaMap().get(user).remove(batchEndAck.getHeader().getOrigin()); + mockRepository.getAuthorizedSessions().remove(session); + } + + private ServerLastWriteWinMap createCrdt(String collection) { + NitriteCollection nc = mockRepository.getDb().getCollection(collection); + NitriteMap nitriteMap = + mockRepository.getDb().getConfig().getNitriteStore().openMap(collection + "-replica", + NitriteId.class, Tombstone.class); + return new ServerLastWriteWinMap(nc, nitriteMap); + } + + private void sendErrorMessage(Session session, Throwable error) throws IOException { + ErrorMessage errorMessage = new ErrorMessage(); + String user = (String) session.getUserProperties().get("user"); + String collection = (String) session.getUserProperties().get("collection"); + + errorMessage.setHeader(createHeader(MessageType.Error, collection, user, mockRepository.getServerId(), "")); + errorMessage.setError(error.getMessage()); + session.getBasicRemote().sendText(objectMapper.writeValueAsString(errorMessage)); + } + + + private MessageHeader createHeader(MessageType messageType, String collection, + String userName, String origin, String corrId) { + MessageHeader messageHeader = new MessageHeader(); + messageHeader.setId(UUID.randomUUID().toString()); + messageHeader.setCorrelationId(corrId); + messageHeader.setCollection(collection); + messageHeader.setMessageType(messageType); + messageHeader.setOrigin(origin); + messageHeader.setTimestamp(System.currentTimeMillis()); + messageHeader.setUserName(userName); + return messageHeader; + } + + private boolean isValidAuth(String userName, String authToken) { + if (mockRepository.getUserMap().containsKey(userName)) { + return mockRepository.getUserMap().get(userName).equals(authToken); + } + return false; + } + + private void checkAuthorized(Session session) { + if (!(boolean) session.getUserProperties().get("authorized")) { + throw new SecurityException("session is not authorized"); + } + } +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/server/SimpleDataGateServer.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateServer.java similarity index 74% rename from nitrite-replication/src/test/java/org/dizitart/no2/integration/server/SimpleDataGateServer.java rename to nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateServer.java index 3279f3aec..557c10936 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/server/SimpleDataGateServer.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateServer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.dizitart.no2.integration.server; +package org.dizitart.no2.mock.server; import org.glassfish.tyrus.server.Server; @@ -22,24 +22,24 @@ /** * @author Anindya Chatterjee */ -public class SimpleDataGateServer { +public class MockDataGateServer { private final int port; private Server server; - private final Repository repository; + private final MockRepository mockRepository; - public SimpleDataGateServer(int port) { + public MockDataGateServer(int port) { this.port = port; - this.repository = Repository.getInstance(); + this.mockRepository = MockRepository.getInstance(); } public void start() throws Exception { - server = new Server("127.0.0.1", port, "", null, SimpleDataGateEndpoint.class); + server = new Server("127.0.0.1", port, "", null, MockDataGateEndpoint.class); server.start(); Runtime.getRuntime().addShutdownHook(new Thread(() -> server.stop())); } public void stop() { server.stop(); - repository.reset(); + mockRepository.reset(); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/server/Repository.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java similarity index 76% rename from nitrite-replication/src/test/java/org/dizitart/no2/integration/server/Repository.java rename to nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java index bfc433ce9..80a19bd00 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/server/Repository.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java @@ -14,38 +14,39 @@ * limitations under the License. */ -package org.dizitart.no2.integration.server; +package org.dizitart.no2.mock.server; import jakarta.websocket.Session; import lombok.Data; import org.dizitart.no2.Nitrite; -import org.dizitart.no2.sync.crdt.LastWriteWinMap; +import org.dizitart.no2.sync.FeedLedger; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import static org.dizitart.no2.integration.TestUtils.createDb; +import static org.dizitart.no2.TestUtils.createDb; /** * @author Anindya Chatterjee */ @Data -public class Repository { - private static Repository instance = new Repository(); +public class MockRepository { + private static MockRepository instance = new MockRepository(); private Map> collectionReplicaMap; private Map> userReplicaMap; - private Map replicaStore; + private Map replicaStore; + private Map feedLedgerMap; private Nitrite db; private String serverId; private Set authorizedSessions; private Map userMap; private Long gcTtl; - private Repository() { + private MockRepository() { reset(); } - public static Repository getInstance() { + public static MockRepository getInstance() { return instance; } @@ -55,6 +56,7 @@ public void reset() { replicaStore = new ConcurrentHashMap<>(); authorizedSessions = new HashSet<>(); userMap = new ConcurrentHashMap<>(); + feedLedgerMap = new ConcurrentHashMap<>(); db = createDb(); serverId = UUID.randomUUID().toString(); diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java new file mode 100644 index 000000000..d5cf9bc42 --- /dev/null +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.mock.server; + +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.DocumentCursor; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.tuples.Pair; +import org.dizitart.no2.store.NitriteMap; +import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.Tombstone; + +import java.util.Map; + +import static org.dizitart.no2.collection.FindOptions.skipBy; +import static org.dizitart.no2.common.Constants.*; +import static org.dizitart.no2.filters.Filter.and; +import static org.dizitart.no2.filters.FluentFilter.where; + +/** + * @author Anindya Chatterjee + */ +@Data +@Slf4j +public class ServerLastWriteWinMap { + private NitriteCollection collection; + private NitriteMap tombstoneMap; + + public ServerLastWriteWinMap(NitriteCollection collection, NitriteMap tombstoneMap) { + this.collection = collection; + this.tombstoneMap = tombstoneMap; + } + + public void merge(LastWriteWinState snapshot) { + if (snapshot.getChangeSet() != null) { + for (Document entry : snapshot.getChangeSet()) { + put(entry); + } + } + + if (snapshot.getTombstoneMap() != null) { + for (Map.Entry entry : snapshot.getTombstoneMap().entrySet()) { + remove(NitriteId.createId(entry.getKey()), entry.getValue()); + } + } + + log.debug("Server Collection after merge - " + collection.find().toList()); + log.debug("Server Tombstone after merge - " + tombstoneMap.entries().toList()); + } + + public LastWriteWinState getChangesSince(Long startTime, Long endTime, int offset, int size) { + LastWriteWinState state = new LastWriteWinState(); + + DocumentCursor cursor = collection.find( + and( + where("_synced").gte(startTime), + where("_synced").lte(endTime) + ), skipBy(offset).limit(size)); + + state.getChangeSet().addAll(cursor.toSet()); + + // send tombstone info in first batch only + if (offset == 0) { + // don't repeat for other offsets + for (Pair entry : tombstoneMap.entries()) { + Long syncTimestamp = entry.getSecond().getSyncTimestamp(); + if (syncTimestamp > startTime && syncTimestamp <= endTime) { + state.getTombstoneMap().put(entry.getFirst().getIdValue(), + entry.getSecond().getDeleteTimestamp()); + } + } + } + + return state; + } + + private void put(Document value) { + if (value != null) { + value.put("_synced", System.currentTimeMillis()); + NitriteId key = value.getId(); + + Document entry = collection.getById(key); + if (entry == null) { + if (tombstoneMap.containsKey(key)) { + Long tombstoneTime = tombstoneMap.get(key).getDeleteTimestamp(); + Long docModifiedTime = value.getLastModifiedSinceEpoch(); + + if (docModifiedTime >= tombstoneTime) { + value.put(DOC_SOURCE, REPLICATOR); + collection.insert(value); + tombstoneMap.remove(key); + } + } else { + value.put(DOC_SOURCE, REPLICATOR); + collection.insert(value); + } + } else { + Long oldTime = entry.getLastModifiedSinceEpoch(); + Long newTime = value.getLastModifiedSinceEpoch(); + + if (newTime > oldTime) { + entry.put(DOC_SOURCE, REPLICATOR); + collection.remove(entry); + + value.put(DOC_SOURCE, REPLICATOR); + collection.insert(value); + } + } + } + } + + private void remove(NitriteId key, long timestamp) { + Document entry = collection.getById(key); + if (entry != null) { + entry.put(DOC_SOURCE, REPLICATOR); + + log.debug("Removing {} from collection for tombstone", key.getIdValue()); + collection.remove(entry); + Tombstone tombstone = new Tombstone(); + tombstone.setNitriteId(key); + tombstone.setDeleteTimestamp(timestamp); + tombstone.setSyncTimestamp(System.currentTimeMillis()); + tombstoneMap.put(key, tombstone); + } else { + log.debug("No entry found to remove"); + } + } +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java index f240efc3d..0f436be3a 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java @@ -13,14 +13,14 @@ public void testCanEqual() { @Test public void testEquals() { BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setLastSynced(0L); + batchChangeEnd.setEndTime(0L); assertFalse((new BatchChangeEnd()).equals(batchChangeEnd)); } @Test public void testEquals10() { BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setLastSynced(0L); + batchChangeEnd.setEndTime(0L); assertFalse(batchChangeEnd.equals(new BatchChangeEnd())); } @@ -104,8 +104,8 @@ public void testSetHeader() { @Test public void testSetLastSynced() { BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setLastSynced(1L); - assertEquals(1L, batchChangeEnd.getLastSynced().longValue()); + batchChangeEnd.setEndTime(1L); + assertEquals(1L, batchChangeEnd.getEndTime().longValue()); } @Test diff --git a/nitrite-replication/src/test/resources/log4j2.xml b/nitrite-replication/src/test/resources/log4j2.xml deleted file mode 100644 index c30ab253c..000000000 --- a/nitrite-replication/src/test/resources/log4j2.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/nitrite-replication/src/test/resources/logback.xml b/nitrite-replication/src/test/resources/logback.xml new file mode 100644 index 000000000..db5d92b6c --- /dev/null +++ b/nitrite-replication/src/test/resources/logback.xml @@ -0,0 +1,31 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{15} - %msg%n + + + + + + + + + + + \ No newline at end of file diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java b/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java index 3fa969a28..18512c0b9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java @@ -134,7 +134,7 @@ public Nitrite openOrCreate() { */ public Nitrite openOrCreate(String username, String password) { this.nitriteConfig.autoConfigure(); - Runtime.getRuntime().addShutdownHook(new Thread(ThreadPoolManager::shutdownThreadPools)); + Runtime.getRuntime().addShutdownHook(new Thread(ThreadPoolManager::shutdownAllThreadPools)); return new NitriteDatabase(username, password, nitriteConfig); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/concurrent/ThreadPoolManager.java b/nitrite/src/main/java/org/dizitart/no2/common/concurrent/ThreadPoolManager.java index 8ad7226b1..bf3ace37d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/concurrent/ThreadPoolManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/concurrent/ThreadPoolManager.java @@ -18,12 +18,8 @@ import lombok.extern.slf4j.Slf4j; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import static org.dizitart.no2.common.Constants.DAEMON_THREAD_NAME; @@ -40,7 +36,7 @@ public class ThreadPoolManager { private final static Object lock; static { - threadPools = new ArrayList<>(); + threadPools = new CopyOnWriteArrayList<>(); commonPool = workerPool(); threadPools.add(commonPool); lock = new Object(); @@ -70,6 +66,20 @@ public static ExecutorService getThreadPool(int size, String threadName) { return threadPool; } + /** + * Creates an {@link ScheduledExecutorService} with provided size where + * all {@link Thread}s are daemon threads and uncaught error aware. + * + * @param size the size + * @param threadName the thread name + * @return the {@link ScheduledExecutorService} + */ + public static ScheduledExecutorService getScheduledThreadPool(int size, String threadName) { + ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(size, threadFactory(threadName)); + threadPools.add(threadPool); + return threadPool; + } + /** * Returns a new {@link ErrorAwareThreadFactory} where thread name * will be set to the name specified. @@ -102,29 +112,39 @@ public static Future runAsync(Runnable runnable) { /** * Shuts down all thread pools. */ - public synchronized static void shutdownThreadPools() { + public synchronized static void shutdownAllThreadPools() { for (ExecutorService threadPool : threadPools) { - synchronized (lock) { - if (threadPool != null) { - threadPool.shutdown(); - } - } - try { - if (threadPool != null && !threadPool.awaitTermination(10, TimeUnit.SECONDS)) { - synchronized (lock) { - threadPool.shutdownNow(); - } + shutdownThreadPool(threadPool); + } + } - if (!threadPool.awaitTermination(10, TimeUnit.SECONDS)) { - log.error("Thread pool did not terminate"); - } - } - } catch (InterruptedException e) { + /** + * Shuts down a thread pool. + * + * @param threadPool the thread pool + */ + public synchronized static void shutdownThreadPool(ExecutorService threadPool) { + synchronized (lock) { + if (threadPool != null) { + threadPool.shutdown(); + } + } + try { + if (threadPool != null && !threadPool.awaitTermination(10, TimeUnit.SECONDS)) { synchronized (lock) { threadPool.shutdownNow(); } - Thread.currentThread().interrupt(); + + if (!threadPool.awaitTermination(10, TimeUnit.SECONDS)) { + log.error("Thread pool did not terminate"); + } + } + } catch (InterruptedException e) { + synchronized (lock) { + threadPool.shutdownNow(); } + Thread.currentThread().interrupt(); } + threadPools.remove(threadPool); } } From 29aa022d5e3495414e32cc095420e35da2637d4b Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 30 Jul 2021 00:10:28 +0530 Subject: [PATCH 10/78] replication logic updated --- gradle.properties | 2 + nitrite-android-example/build.gradle | 16 +++---- .../android/ExampleInstrumentedTest.java | 4 +- .../src/main/AndroidManifest.xml | 2 +- .../no2/example/android/MainActivity.java | 2 +- .../dizitart/no2/sync/BatchChangeSender.java | 2 +- .../org/dizitart/no2/sync/DataGateClient.java | 5 +- .../no2/sync/ReplicatedCollection.java | 7 +-- .../no2/sync/crdt/LastWriteWinMap.java | 31 ++++++------- .../sync/handlers/BatchChangeEndHandler.java | 6 ++- .../no2/sync/handlers/DisconnectHandler.java | 2 +- .../no2/sync/message/MessageHeader.java | 2 - .../src/test/java/org/dizitart/no2/Retry.java | 7 ++- .../integration/DataGateIntegrationTest.java | 7 --- .../no2/mock/ReplicaNegativeTest.java | 5 -- .../org/dizitart/no2/mock/ReplicaTest.java | 35 +++++++------- .../no2/mock/server/MockDataGateEndpoint.java | 11 ++--- .../no2/mock/server/MockRepository.java | 17 ++++--- .../mock/server/ServerLastWriteWinMap.java | 46 ++++++++++--------- .../org/dizitart/no2/sync/ConfigTest.java | 18 ++++---- .../no2/sync/crdt/LastWriteWinStateTest.java | 6 +-- .../sync/message/BatchChangeContinueTest.java | 4 +- .../no2/sync/message/BatchChangeEndTest.java | 6 +-- .../sync/message/BatchChangeStartTest.java | 13 ++++-- .../no2/sync/message/DataGateFeedTest.java | 2 +- .../src/test/resources/logback.xml | 1 + .../collection/operation/ReadOperations.java | 2 +- ...DocumentStream.java => BoundedStream.java} | 12 ++--- .../dizitart/no2/common/util/Iterables.java | 11 ----- ...StreamTest.java => BoundedStreamTest.java} | 11 +++-- 30 files changed, 137 insertions(+), 158 deletions(-) rename nitrite/src/main/java/org/dizitart/no2/common/streams/{BoundedDocumentStream.java => BoundedStream.java} (87%) rename nitrite/src/test/java/org/dizitart/no2/common/streams/{BoundedDocumentStreamTest.java => BoundedStreamTest.java} (80%) diff --git a/gradle.properties b/gradle.properties index a478be1cf..0f8e3ec96 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,3 +18,5 @@ org.gradle.parallel=false #android.enableSeparateAnnotationProcessing=true # artifact version nitriteVersion=4.0.1-SNAPSHOT +android.useAndroidX=true +android.enableJetifier=true diff --git a/nitrite-android-example/build.gradle b/nitrite-android-example/build.gradle index d7f9ffbca..06697d787 100644 --- a/nitrite-android-example/build.gradle +++ b/nitrite-android-example/build.gradle @@ -21,7 +21,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.0' + classpath 'com.android.tools.build:gradle:4.2.2' } } @@ -57,7 +57,7 @@ android { // multiDexEnabled true - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { @@ -87,15 +87,15 @@ dependencies { implementation project(path: ':nitrite-mvstore-adapter', configuration: 'default') implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support:multidex:1.0.3' - implementation 'com.android.support.constraint:constraint-layout:2.0.4' - implementation 'com.android.support:design:28.0.0' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'com.google.android.material:material:1.4.0' annotationProcessor "org.projectlombok:lombok:1.18.20" testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.11.2' testAnnotationProcessor "org.projectlombok:lombok:1.18.20" - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } diff --git a/nitrite-android-example/src/androidTest/java/org/dizitart/no2/example/android/ExampleInstrumentedTest.java b/nitrite-android-example/src/androidTest/java/org/dizitart/no2/example/android/ExampleInstrumentedTest.java index d1b5ff2d6..4e6640dae 100644 --- a/nitrite-android-example/src/androidTest/java/org/dizitart/no2/example/android/ExampleInstrumentedTest.java +++ b/nitrite-android-example/src/androidTest/java/org/dizitart/no2/example/android/ExampleInstrumentedTest.java @@ -17,8 +17,8 @@ package org.dizitart.no2.example.android; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/nitrite-android-example/src/main/AndroidManifest.xml b/nitrite-android-example/src/main/AndroidManifest.xml index dbba5aab9..4289a215c 100644 --- a/nitrite-android-example/src/main/AndroidManifest.xml +++ b/nitrite-android-example/src/main/AndroidManifest.xml @@ -19,7 +19,7 @@ xmlns:tools="http://schemas.android.com/tools" package="org.dizitart.no2.example.android"> void sendMessage(WebSocket webSocket, M message) { try { if (replicatedCollection.isConnected()) { - log.debug("Sending message to server " + message); messageTemplate.postMessage(webSocket, message); } else { throw new IllegalStateException("datagate client is not connected"); @@ -126,7 +125,7 @@ public void sendMessage(WebSocket webSocket, M messa } public void closeConnection(WebSocket webSocket, String reason) { - log.debug("Closing connection due to " + reason); + log.debug("Closing connection due to {}", reason); replicatedCollection.setConnected(false); if (webSocket != null) { webSocket.close(WebSocketCode.NORMAL_CLOSE, reason); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java index 83576d747..81b135427 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java @@ -25,13 +25,13 @@ import org.dizitart.no2.collection.meta.Attributes; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.common.util.StringUtils; +import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; import org.dizitart.no2.sync.crdt.LastWriteWinMap; import org.dizitart.no2.sync.crdt.LastWriteWinState; import org.dizitart.no2.sync.event.CollectionChangeListener; -import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; -import org.dizitart.no2.sync.net.DataGateSocket; import org.dizitart.no2.sync.handlers.ReceiptLedgerAware; import org.dizitart.no2.sync.message.Receipt; +import org.dizitart.no2.sync.net.DataGateSocket; import java.util.HashSet; import java.util.Set; @@ -62,7 +62,7 @@ public ReplicatedCollection(Config config) { } public void startReplication() { - log.debug("Starting replication for " + getReplicaId()); + log.debug("Starting replication for {}", getReplicaId()); DataGateSocket dataGateSocket = new DataGateSocket(config); dataGateClient = new DataGateClient(config, this); batchChangeSender = new BatchChangeSender(config, this, dataGateClient); @@ -85,6 +85,7 @@ public void sendAndReceive(WebSocket webSocket, String correlationId, Long check public void collectGarbage(Long ttl) { if (ttl != null && ttl > 0) { long collectTime = System.currentTimeMillis() - ttl; + if (lastWriteWinMap != null && lastWriteWinMap.getTombstoneMap() != null) { Set removeSet = new HashSet<>(); for (Pair entry : lastWriteWinMap.getTombstoneMap().entries()) { diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java index cd02c8ad4..ff020b5fc 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java @@ -22,6 +22,7 @@ import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.streams.BoundedStream; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.store.NitriteMap; @@ -58,34 +59,31 @@ public void merge(LastWriteWinState snapshot) { remove(NitriteId.createId(entry.getKey()), entry.getValue()); } } - - log.debug("Collection after merge - " + collection.find().toList()); - log.debug("Tombstone after merge - " + tombstoneMap.entries().toList()); } public LastWriteWinState getChangesSince(Long startTime, Long endTime, int offset, int size) { LastWriteWinState state = new LastWriteWinState(); + BoundedStream stream + = new BoundedStream<>((long) offset, (long) size, tombstoneMap.entries()); + + for (Pair entry : stream) { + Long syncTimestamp = entry.getSecond(); + if (syncTimestamp > startTime && syncTimestamp <= endTime) { + state.getTombstoneMap().put(entry.getFirst().getIdValue(), + entry.getSecond()); + } + } + DocumentCursor cursor = collection.find( and( - where(DOC_MODIFIED).gte(startTime), + where(DOC_MODIFIED).gt(startTime), where(DOC_MODIFIED).lte(endTime) ), skipBy(offset).limit(size)); state.getChangeSet().addAll(cursor.toSet()); - // send tombstone info in first batch only - if (offset == 0) { - // don't repeat for other offsets - for (Pair entry : tombstoneMap.entries()) { - Long timestamp = entry.getSecond(); - if (timestamp > startTime && timestamp <= endTime) { - state.getTombstoneMap().put(entry.getFirst().getIdValue(), entry.getSecond()); - } - } - } - return state; } @@ -127,9 +125,8 @@ private void remove(NitriteId key, long timestamp) { Document entry = collection.getById(key); if (entry != null) { entry.put(DOC_SOURCE, REPLICATOR); - - log.debug("Removing {} from collection for tombstone", key.getIdValue()); collection.remove(entry); + tombstoneMap.put(key, timestamp); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java index 9ab29cb20..79139fa21 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java @@ -39,14 +39,16 @@ public BatchChangeEndHandler(ReplicatedCollection replicatedCollection) { public void handleMessage(WebSocket webSocket, BatchChangeEnd message) { MessageFactory factory = new MessageFactory(); BatchEndAck batchEndAck = factory.createBatchEndAck(replicatedCollection.getConfig(), - replicatedCollection.getReplicaId(), message.getHeader().getId()); + replicatedCollection.getReplicaId(), message.getHeader().getCorrelationId()); + batchEndAck.setStartTime(message.getStartTime()); + batchEndAck.setEndTime(message.getEndTime()); DataGateClient dataGateClient = replicatedCollection.getDataGateClient(); dataGateClient.sendMessage(webSocket, batchEndAck); Long time = message.getEndTime(); - log.debug("Saving last sync time - " + time); + log.debug("Saving last sync time {}", time); replicatedCollection.saveLastSyncTime(time); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java index d69fef496..9a74564d1 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java @@ -35,6 +35,6 @@ public DisconnectHandler(ReplicatedCollection replicatedCollection) { @Override public void handleMessage(WebSocket webSocket, Disconnect message) { log.debug("Disconnecting from server"); - replicatedCollection.stopReplication(webSocket, "Server disconnect"); + replicatedCollection.stopReplication(webSocket, "Server disconnected"); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java index 9681f8956..0c4136871 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java @@ -17,13 +17,11 @@ package org.dizitart.no2.sync.message; import lombok.Data; -import lombok.ToString; /** * @author Anindya Chatterjee */ @Data -@ToString(onlyExplicitlyIncluded = true) public class MessageHeader { private String id; private String correlationId; diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/Retry.java b/nitrite-replication/src/test/java/org/dizitart/no2/Retry.java index b7e69a7f2..11cd2b109 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/Retry.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/Retry.java @@ -17,6 +17,7 @@ package org.dizitart.no2; +import lombok.extern.slf4j.Slf4j; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; @@ -24,6 +25,7 @@ /** * @author Anindya Chatterjee */ +@Slf4j public class Retry implements TestRule { private final int retryCount; @@ -48,10 +50,11 @@ public void evaluate() throws Throwable { return; } catch (Throwable t) { caughtThrowable = t; - System.err.println(description.getDisplayName() + ": run " + (i + 1) + " failed"); + log.info(description.getDisplayName() + ": run " + (i + 1) + " failed"); } } - System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures"); + log.error(description.getDisplayName() + ": giving up after " + retryCount + " failures", caughtThrowable); + assert caughtThrowable != null; throw caughtThrowable; } }; diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index 538973098..a0a7746a6 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -67,13 +67,6 @@ public static void main(String[] args) { .acceptAllCertificates(true) .create(); -// replica.subscribe(event -> { -// if (event.getEventType() == ReplicationEventType.Stopped) { -// System.out.println("Reconnecting"); -// replica.connect(); -// } -// }); - replica.connect(); Thread.sleep(10000); } catch (Exception e) { diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java index d405c4d02..3205a28f5 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java @@ -103,9 +103,4 @@ public void testServerClose() { server.stop(); await().atMost(5, SECONDS).until(() -> !r1.isConnected()); } - - /* - * 1. Server close and again restarted - * 2. Connectivity check with with atomic counter - * */ } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java index 0539e1fca..192ff11ec 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java @@ -68,7 +68,8 @@ public class ReplicaTest { private Nitrite db; public static String getRandomTempDbFile() { - String dataDir = System.getProperty("java.io.tmpdir") + File.separator + "nitrite" + File.separator + "data"; + String dataDir = System.getProperty("java.io.tmpdir") + File.separator + + "nitrite" + File.separator + "data"; File file = new File(dataDir); if (!file.exists()) { assertTrue(file.mkdirs()); @@ -210,7 +211,6 @@ public void testSingleUserMultiReplica() { e.printStackTrace(); } } - System.out.println("All r1 data inserted"); }); executorService.submit(() -> { @@ -223,15 +223,10 @@ public void testSingleUserMultiReplica() { e.printStackTrace(); } } - System.out.println("All r2 data inserted"); }); - await().atMost(10, SECONDS).until(() -> { - System.out.println("C1 Size - " + c1.size()); - System.out.println(c1.find().toList()); -// FIXME: Always coming as 30, check batch continue logic and scrutinize logs - return c1.size() == 40; - }); + + await().atMost(10, SECONDS).until(() -> c1.size() == 40); assertEquals(c2.size(), 40); r1.disconnect(); @@ -269,7 +264,8 @@ public void testSingleUserMultiReplica() { }); await().atMost(10, SECONDS).until(() -> c2.size() == 0); - await().atMost(5, SECONDS).until(() -> c1.size() == 0); + + await().atMost(10, SECONDS).until(() -> c1.size() == 0); TestUtils.assertEquals(c1, c2); r1.disconnectNow(); @@ -581,7 +577,6 @@ public void testDelayedConnectRemoveAll() { .create(); r1.connect(); - System.out.println("r1 connected"); for (int i = 0; i < 10; i++) { Document document = randomDocument(); @@ -589,13 +584,11 @@ public void testDelayedConnectRemoveAll() { } c1.remove(Filter.ALL); - System.out.println("Removed all"); assertEquals(c1.size(), 0); r1.disconnect(); r1.close(); db.close(); - System.out.println("r1 disconnected"); Nitrite db2 = createDb(); NitriteCollection c2 = db2.getCollection("testDelayedConnect"); @@ -607,7 +600,6 @@ public void testDelayedConnectRemoveAll() { .create(); r2.connect(); - System.out.println("r2 connected"); for (int i = 0; i < 5; i++) { Document document = randomDocument(); @@ -624,7 +616,7 @@ public void testDelayedConnectRemoveAll() { .create(); r1.connect(); - System.out.println("r1 connected again"); + await().atMost(5, SECONDS).until(() -> { List l1 = c3.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); List l2 = c2.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); @@ -653,14 +645,19 @@ public void testGarbageCollect() { c1.insert(document); } await().atMost(5, SECONDS).until(() -> c1.size() == 10); + c1.remove(Filter.ALL); assertEquals(c1.size(), 0); + final LastWriteWinMap lastWriteWinMap = getCrdt(r1); + await().atMost(5, SECONDS).until(() -> c1.size() == 0 + && lastWriteWinMap.getTombstoneMap().size() == 10); + r1.disconnect(); r1.close(); db.close(); - mockRepository.setGcTtl(1L); + mockRepository.setGcTtl(1000L); db = createDb(dbFile); NitriteCollection c2 = db.getCollection("testGarbageCollect"); @@ -668,14 +665,14 @@ public void testGarbageCollect() { .of(c2) .remote("ws://127.0.0.1:9090/datagate/anidotnet/testGarbageCollect") .jwtAuth("anidotnet", "abcd") + .replicaName("r1") .create(); r1.connect(); - LastWriteWinMap lastWriteWinMap = getCrdt(r1); - + final LastWriteWinMap finalLastWriteWinMap = getCrdt(r1); await().atMost(5, SECONDS).until(() -> c2.size() == 0 - && lastWriteWinMap.getTombstoneMap().size() == 0); + && finalLastWriteWinMap.getTombstoneMap().size() == 0); r1.disconnectNow(); } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java index 385a3dfda..5589ba2c6 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java @@ -193,14 +193,13 @@ protected void handleConnect(Session session, Connect connect) throws IOExceptio } protected void handleBatchChangeStart(Session session, BatchChangeStart batchChangeStart) throws IOException { - log.debug("BatchChangeStart message received " + batchChangeStart); DataGateFeed feed = new DataGateFeed(); String userName = batchChangeStart.getHeader().getUserName(); String collection = userName + "@" + batchChangeStart.getHeader().getCollection(); String replicaId = batchChangeStart.getHeader().getOrigin(); ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - replica.merge(batchChangeStart.getFeed()); + replica.merge(batchChangeStart.getFeed(), batchChangeStart.getEndTime()); feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeStart.getHeader().getCollection(), userName, replicaId, batchChangeStart.getHeader().getCorrelationId())); @@ -224,7 +223,7 @@ protected void handleBatchChangeContinue(Session session, BatchChangeContinue ba String collection = userName + "@" + batchChangeContinue.getHeader().getCollection(); String replicaId = batchChangeContinue.getHeader().getOrigin(); ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - replica.merge(batchChangeContinue.getFeed()); + replica.merge(batchChangeContinue.getFeed(), batchChangeContinue.getEndTime()); feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeContinue.getHeader().getCollection(), userName, replicaId, batchChangeContinue.getHeader().getCorrelationId())); @@ -298,7 +297,7 @@ protected void handleBatchAck(Session session, BatchAck batchAck) throws IOExcep if (hasMore) { BatchChangeContinue message = new BatchChangeContinue(); message.setHeader(createHeader(MessageType.BatchChangeContinue, - collection, userName, mockRepository.getServerId(), "")); + collection, userName, mockRepository.getServerId(), batchAck.getHeader().getCorrelationId())); message.setFeed(changesSince); message.setBatchSize(batchSize); message.setDebounce(debounce); @@ -371,10 +370,10 @@ private void sendErrorMessage(Session session, Throwable error) throws IOExcepti private MessageHeader createHeader(MessageType messageType, String collection, - String userName, String origin, String corrId) { + String userName, String origin, String correlationId) { MessageHeader messageHeader = new MessageHeader(); messageHeader.setId(UUID.randomUUID().toString()); - messageHeader.setCorrelationId(corrId); + messageHeader.setCorrelationId(correlationId); messageHeader.setCollection(collection); messageHeader.setMessageType(messageType); messageHeader.setOrigin(origin); diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java index 80a19bd00..c62f774a0 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java @@ -43,23 +43,22 @@ public class MockRepository { private Long gcTtl; private MockRepository() { - reset(); - } - - public static MockRepository getInstance() { - return instance; - } - - public void reset() { collectionReplicaMap = new ConcurrentHashMap<>(); userReplicaMap = new ConcurrentHashMap<>(); replicaStore = new ConcurrentHashMap<>(); authorizedSessions = new HashSet<>(); userMap = new ConcurrentHashMap<>(); feedLedgerMap = new ConcurrentHashMap<>(); - db = createDb(); serverId = UUID.randomUUID().toString(); gcTtl = 0L; } + + public static MockRepository getInstance() { + return instance; + } + + public void reset() { + instance = new MockRepository(); + } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java index d5cf9bc42..2d1ffec3e 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java @@ -23,12 +23,15 @@ import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.streams.BoundedStream; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.sync.crdt.LastWriteWinState; import org.dizitart.no2.sync.crdt.Tombstone; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import static org.dizitart.no2.collection.FindOptions.skipBy; import static org.dizitart.no2.common.Constants.*; @@ -49,7 +52,7 @@ public ServerLastWriteWinMap(NitriteCollection collection, NitriteMap entry : snapshot.getTombstoneMap().entrySet()) { - remove(NitriteId.createId(entry.getKey()), entry.getValue()); + remove(NitriteId.createId(entry.getKey()), entry.getValue(), syncTime); } } - - log.debug("Server Collection after merge - " + collection.find().toList()); - log.debug("Server Tombstone after merge - " + tombstoneMap.entries().toList()); } public LastWriteWinState getChangesSince(Long startTime, Long endTime, int offset, int size) { LastWriteWinState state = new LastWriteWinState(); + // send tombstone info + BoundedStream stream + = new BoundedStream<>((long) offset, (long) size, tombstoneMap.entries()); + + for (Pair entry : stream) { + Long syncTimestamp = entry.getSecond().getSyncTimestamp(); + if (syncTimestamp > startTime && syncTimestamp <= endTime) { + state.getTombstoneMap().put(entry.getFirst().getIdValue(), + entry.getSecond().getDeleteTimestamp()); + } + } + DocumentCursor cursor = collection.find( and( - where("_synced").gte(startTime), + where("_synced").gt(startTime), where("_synced").lte(endTime) ), skipBy(offset).limit(size)); - state.getChangeSet().addAll(cursor.toSet()); + Set documents = cursor.toSet().stream() + .peek(document -> document.remove("_synced")) + .collect(Collectors.toSet()); - // send tombstone info in first batch only - if (offset == 0) { - // don't repeat for other offsets - for (Pair entry : tombstoneMap.entries()) { - Long syncTimestamp = entry.getSecond().getSyncTimestamp(); - if (syncTimestamp > startTime && syncTimestamp <= endTime) { - state.getTombstoneMap().put(entry.getFirst().getIdValue(), - entry.getSecond().getDeleteTimestamp()); - } - } - } + state.getChangeSet().addAll(documents); return state; } @@ -127,17 +131,15 @@ private void put(Document value) { } } - private void remove(NitriteId key, long timestamp) { + private void remove(NitriteId key, long timestamp, long syncTime) { Document entry = collection.getById(key); if (entry != null) { entry.put(DOC_SOURCE, REPLICATOR); - - log.debug("Removing {} from collection for tombstone", key.getIdValue()); collection.remove(entry); Tombstone tombstone = new Tombstone(); tombstone.setNitriteId(key); tombstone.setDeleteTimestamp(timestamp); - tombstone.setSyncTimestamp(System.currentTimeMillis()); + tombstone.setSyncTimestamp(syncTime); tombstoneMap.put(key, tombstone); } else { log.debug("No entry found to remove"); diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/ConfigTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/ConfigTest.java index a138e75fa..94d928b1e 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/ConfigTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/ConfigTest.java @@ -160,9 +160,9 @@ public void testSetProxy() { Config config = new Config(); config.setProxy(proxy); assertEquals( - "Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, timeout=null," - + " requestBuilder=null, proxy=HTTP @ 0.0.0.0/0.0.0.0:1, authToken=null, acceptAllCertificates=false," - + " networkConnectivityChecker=null)", + "Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, " + + "timeout=null, requestBuilder=null, proxy=HTTP @ 0.0.0.0/0.0.0.0:1, authToken=null, " + + "acceptAllCertificates=false, eventListeners=null, replicaName=null)", config.toString()); } @@ -179,9 +179,9 @@ public void testSetTimeout() { TimeSpan timeout = new TimeSpan(10L, TimeUnit.NANOSECONDS); Config config = new Config(); config.setTimeout(timeout); - assertEquals("Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, timeout" - + "=TimeSpan(time=10, timeUnit=NANOSECONDS), requestBuilder=null, proxy=null, authToken=null, acceptAll" - + "Certificates=false, networkConnectivityChecker=null)", config.toString()); + assertEquals("Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, " + + "timeout=TimeSpan(time=10, timeUnit=NANOSECONDS), requestBuilder=null, proxy=null, authToken=null, " + + "acceptAllCertificates=false, eventListeners=null, replicaName=null)", config.toString()); } @Test @@ -194,9 +194,9 @@ public void testSetUserName() { @Test public void testToString() { assertEquals( - "Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, timeout=null," - + " requestBuilder=null, proxy=null, authToken=null, acceptAllCertificates=false, networkConnectivityChecker" - + "=null)", + "Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, " + + "timeout=null, requestBuilder=null, proxy=null, authToken=null, acceptAllCertificates=false, " + + "eventListeners=null, replicaName=null)", (new Config()).toString()); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/LastWriteWinStateTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/LastWriteWinStateTest.java index ed1d986d9..863b37dec 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/LastWriteWinStateTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/LastWriteWinStateTest.java @@ -1,13 +1,13 @@ package org.dizitart.no2.sync.crdt; -import static org.junit.Assert.assertEquals; - import org.junit.Test; +import static org.junit.Assert.assertEquals; + public class LastWriteWinStateTest { @Test public void testConstructor() { - assertEquals("LastWriteWinState(changes=[], tombstones={})", (new LastWriteWinState()).toString()); + assertEquals("LastWriteWinState(changeSet=[], tombstoneMap={})", (new LastWriteWinState()).toString()); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeContinueTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeContinueTest.java index 1d32935ab..6b9c24b5e 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeContinueTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeContinueTest.java @@ -97,8 +97,8 @@ public void testSetDebounce() { public void testSetFeed() { BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); batchChangeContinue.setFeed(new LastWriteWinState()); - assertEquals("BatchChangeContinue(header=null, feed=LastWriteWinState(changes=[], tombstones={}), batchSize=null," - + " debounce=null)", batchChangeContinue.toString()); + assertEquals("BatchChangeContinue(header=null, feed=LastWriteWinState(changeSet=[], tombstoneMap={}), " + + "batchSize=null, debounce=null)", batchChangeContinue.toString()); } @Test diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java index 0f436be3a..0b4209444 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java @@ -96,8 +96,8 @@ public void testSetHeader() { BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); batchChangeEnd.setHeader(new MessageHeader()); assertEquals( - "BatchChangeEnd(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null," - + " timestamp=null, messageType=null, origin=null), lastSynced=null, batchSize=null, debounce=null)", + "BatchChangeEnd(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null, " + + "timestamp=null, messageType=null, origin=null), batchSize=null, debounce=null)", batchChangeEnd.toString()); } @@ -110,7 +110,7 @@ public void testSetLastSynced() { @Test public void testToString() { - assertEquals("BatchChangeEnd(header=null, lastSynced=null, batchSize=null, debounce=null)", + assertEquals("BatchChangeEnd(header=null, batchSize=null, debounce=null)", (new BatchChangeEnd()).toString()); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeStartTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeStartTest.java index f47dfa797..d033fc947 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeStartTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeStartTest.java @@ -96,8 +96,9 @@ public void testSetDebounce() { public void testSetFeed() { BatchChangeStart batchChangeStart = new BatchChangeStart(); batchChangeStart.setFeed(new LastWriteWinState()); - assertEquals("BatchChangeStart(header=null, batchSize=null, debounce=null, feed=LastWriteWinState(changes=[]," - + " tombstones={}))", batchChangeStart.toString()); + assertEquals("BatchChangeStart(super=TimeBoundMessage(startTime=null, endTime=null), header=null, " + + "batchSize=null, debounce=null, feed=LastWriteWinState(changeSet=[], tombstoneMap={}))", + batchChangeStart.toString()); } @Test @@ -105,14 +106,16 @@ public void testSetHeader() { BatchChangeStart batchChangeStart = new BatchChangeStart(); batchChangeStart.setHeader(new MessageHeader()); assertEquals( - "BatchChangeStart(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null," - + " timestamp=null, messageType=null, origin=null), batchSize=null, debounce=null, feed=null)", + "BatchChangeStart(super=TimeBoundMessage(startTime=null, endTime=null), header=MessageHeader" + + "(id=null, correlationId=null, collection=null, userName=null, timestamp=null, messageType=null, " + + "origin=null), batchSize=null, debounce=null, feed=null)", batchChangeStart.toString()); } @Test public void testToString() { - assertEquals("BatchChangeStart(header=null, batchSize=null, debounce=null, feed=null)", + assertEquals("BatchChangeStart(super=TimeBoundMessage(startTime=null, endTime=null), header=null, " + + "batchSize=null, debounce=null, feed=null)", (new BatchChangeStart()).toString()); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedTest.java index 4f096a567..1094bbf4b 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedTest.java @@ -72,7 +72,7 @@ public void testEquals8() { public void testSetFeed() { DataGateFeed dataGateFeed = new DataGateFeed(); dataGateFeed.setFeed(new LastWriteWinState()); - assertEquals("DataGateFeed(header=null, feed=LastWriteWinState(changes=[], tombstones={}))", + assertEquals("DataGateFeed(header=null, feed=LastWriteWinState(changeSet=[], tombstoneMap={}))", dataGateFeed.toString()); } diff --git a/nitrite-replication/src/test/resources/logback.xml b/nitrite-replication/src/test/resources/logback.xml index db5d92b6c..500a2f118 100644 --- a/nitrite-replication/src/test/resources/logback.xml +++ b/nitrite-replication/src/test/resources/logback.xml @@ -27,5 +27,6 @@ + \ No newline at end of file diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java index 4f7411feb..c77e6af3f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java @@ -153,7 +153,7 @@ private RecordStream> findSuitableStream(FindPlan find if (findPlan.getLimit() != null || findPlan.getSkip() != null) { long limit = findPlan.getLimit() == null ? Long.MAX_VALUE : findPlan.getLimit(); long skip = findPlan.getSkip() == null ? 0 : findPlan.getSkip(); - rawStream = new BoundedDocumentStream(skip, limit, rawStream); + rawStream = new BoundedStream<>(skip, limit, rawStream); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedDocumentStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedStream.java similarity index 87% rename from nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedDocumentStream.java rename to nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedStream.java index 41a978163..193039553 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedDocumentStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedStream.java @@ -17,8 +17,6 @@ package org.dizitart.no2.common.streams; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.exceptions.ValidationException; @@ -33,8 +31,8 @@ * @since 1.0 * @author Anindya Chatterjee. */ -public class BoundedDocumentStream implements RecordStream> { - private final RecordStream> recordStream; +public class BoundedStream implements RecordStream> { + private final RecordStream> recordStream; private final long skip; private final long limit; @@ -45,7 +43,7 @@ public class BoundedDocumentStream implements RecordStream> recordStream) { + public BoundedStream(Long skip, Long limit, RecordStream> recordStream) { this.skip = skip; this.limit = limit; @@ -60,8 +58,8 @@ public BoundedDocumentStream(Long skip, Long limit, RecordStream> iterator() { - Iterator> iterator = recordStream == null ? Collections.emptyIterator() + public Iterator> iterator() { + Iterator> iterator = recordStream == null ? Collections.emptyIterator() : recordStream.iterator(); return new BoundedIterator<>(iterator, skip, limit); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/Iterables.java b/nitrite/src/main/java/org/dizitart/no2/common/util/Iterables.java index 55b980d91..27f745b54 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/Iterables.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/Iterables.java @@ -16,8 +16,6 @@ package org.dizitart.no2.common.util; -import org.dizitart.no2.common.UnknownType; - import java.lang.reflect.Array; import java.util.*; @@ -138,15 +136,6 @@ public static long size(Iterable iterable) { return count; } - public static Class getElementType(Iterable iterable) { - if (iterable == null) return UnknownType.class; - Iterator iterator = iterable.iterator(); - if (iterator.hasNext()) { - return iterator.next().getClass(); - } - return UnknownType.class; - } - @SuppressWarnings({"unchecked", "rawtypes"}) static Object[] toArray(Iterable iterable) { if (iterable instanceof Collection) { diff --git a/nitrite/src/test/java/org/dizitart/no2/common/streams/BoundedDocumentStreamTest.java b/nitrite/src/test/java/org/dizitart/no2/common/streams/BoundedStreamTest.java similarity index 80% rename from nitrite/src/test/java/org/dizitart/no2/common/streams/BoundedDocumentStreamTest.java rename to nitrite/src/test/java/org/dizitart/no2/common/streams/BoundedStreamTest.java index 4aa3a7054..6968e25a5 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/streams/BoundedDocumentStreamTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/streams/BoundedStreamTest.java @@ -30,22 +30,22 @@ import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.*; -public class BoundedDocumentStreamTest { +public class BoundedStreamTest { @Test public void testConstructor() { - new BoundedDocumentStream(1L, 1L, (RecordStream>) mock(RecordStream.class)); + new BoundedStream<>(1L, 1L, (RecordStream>) mock(RecordStream.class)); } @Test public void testConstructor2() { assertThrows(ValidationException.class, - () -> new BoundedDocumentStream(-1L, 1L, (RecordStream>) mock(RecordStream.class))); + () -> new BoundedStream<>(-1L, 1L, (RecordStream>) mock(RecordStream.class))); } @Test public void testConstructor3() { assertThrows(ValidationException.class, - () -> new BoundedDocumentStream(1L, -1L, (RecordStream>) mock(RecordStream.class))); + () -> new BoundedStream<>(1L, -1L, (RecordStream>) mock(RecordStream.class))); } @Test @@ -65,7 +65,8 @@ public Pair next() { } }; - BoundedDocumentStream stream = new BoundedDocumentStream(2L, 1L, recordStream); + BoundedStream stream + = new BoundedStream<>(2L, 1L, recordStream); for (Pair pair : stream) { assertEquals(3, (int) pair.getSecond().get("value", Integer.class)); From 56201dc9c60b071a4db126796d1f27fdad165caf Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 30 Jul 2021 01:12:41 +0530 Subject: [PATCH 11/78] Update ReplicaTest.java --- .../src/test/java/org/dizitart/no2/mock/ReplicaTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java index 192ff11ec..49c63ef0d 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java @@ -20,6 +20,7 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.Nitrite; +import org.dizitart.no2.Retry; import org.dizitart.no2.TestUtils; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; @@ -32,6 +33,7 @@ import org.dizitart.no2.sync.crdt.LastWriteWinMap; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import java.io.File; @@ -60,8 +62,10 @@ @Slf4j public class ReplicaTest { private static MockDataGateServer server; -// @Rule -// public Retry retry = new Retry(3); + + @Rule + public Retry retry = new Retry(3); + private String dbFile; private ExecutorService executorService; private MockRepository mockRepository; From f253f8f73012e46e56db91fc9bc8b4be09ca72ee Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 9 Aug 2021 08:45:52 +0530 Subject: [PATCH 12/78] few changes --- potassium-nitrite/build.gradle | 2 -- potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/potassium-nitrite/build.gradle b/potassium-nitrite/build.gradle index 5cb2eef83..bbae5bd9c 100644 --- a/potassium-nitrite/build.gradle +++ b/potassium-nitrite/build.gradle @@ -16,7 +16,6 @@ buildscript { repositories { - jcenter() mavenCentral() } @@ -37,7 +36,6 @@ plugins { } repositories { - jcenter() mavenCentral() } diff --git a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt index b8ccac4cd..e0461e8e5 100644 --- a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt +++ b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/Builder.kt @@ -73,7 +73,7 @@ class Builder internal constructor() { /** * Opens or creates a new database. If it is an in-memory store, then it * will create a new one. If it is a file based store, and if the file does not - * exists, then it will create a new file store and open; otherwise it will + * exist, then it will create a new file store and open; otherwise it will * open the existing file store. * * @param [userId] the user id From a63aaae5bdb1b8b3ef4fe671e395b5dee859d90f Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 9 Aug 2021 15:10:47 +0530 Subject: [PATCH 13/78] remove jcenter --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index f81645e4f..421e476fc 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,6 @@ buildscript { repositories { - jcenter() mavenCentral() mavenLocal() maven { From e460c8ce31e209c948802083c7fe6cd0958c584b Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 17 Aug 2021 20:58:17 +0530 Subject: [PATCH 14/78] checkpoint 1 --- .../{migrate => migration}/MigrationTest.java | 2 +- .../{migrate => migration}/NewClass.java | 2 +- .../{migrate => migration}/OldClass.java | 2 +- .../org/dizitart/no2/sync/MessageFactory.java | 2 +- .../no2/sync/handlers/BatchAckHandler.java | 2 +- .../sync/handlers/BatchChangeEndHandler.java | 2 +- .../no2/sync/handlers/ConnectAckHandler.java | 2 +- .../no2/sync/handlers/ReceiptAckSender.java | 2 +- .../dizitart/no2/sync/message/BatchAck.java | 3 ++- .../no2/sync/message/BatchChangeContinue.java | 3 ++- .../no2/sync/message/BatchChangeEnd.java | 3 ++- .../no2/sync/message/BatchChangeStart.java | 3 ++- .../no2/sync/message/BatchEndAck.java | 3 ++- .../no2/sync/message/BatchMessage.java | 25 +++++++++++++++++++ .../no2/sync/message/MessageHeader.java | 2 ++ .../no2/mock/server/MockDataGateEndpoint.java | 24 +++++++++--------- .../dizitart/no2/sync/MessageFactoryTest.java | 2 +- .../no2/sync/message/MessageHeaderTest.java | 8 +++--- 18 files changed, 62 insertions(+), 30 deletions(-) rename nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/{migrate => migration}/MigrationTest.java (99%) rename nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/{migrate => migration}/NewClass.java (98%) rename nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/{migrate => migration}/OldClass.java (98%) create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java similarity index 99% rename from nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java rename to nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java index 9b4eeab29..0a3fa50ee 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java @@ -15,7 +15,7 @@ * */ -package org.dizitart.no2.integration.migrate; +package org.dizitart.no2.integration.migration; import com.github.javafaker.Faker; import org.dizitart.no2.integration.Retry; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java similarity index 98% rename from nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java rename to nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java index 56ef25c22..3ef9090ea 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java @@ -15,7 +15,7 @@ * */ -package org.dizitart.no2.integration.migrate; +package org.dizitart.no2.integration.migration; import lombok.Data; import org.dizitart.no2.collection.Document; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java similarity index 98% rename from nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java rename to nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java index 3bbb09b4e..0f2e7669c 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java @@ -15,7 +15,7 @@ * */ -package org.dizitart.no2.integration.migrate; +package org.dizitart.no2.integration.migration; import lombok.Data; import org.dizitart.no2.collection.Document; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java index d80cdd8ca..28db06385 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java @@ -108,7 +108,7 @@ public MessageHeader createHeader(MessageType messageType, String collectionName String correlationId, String replicaId, String userName) { MessageHeader messageHeader = new MessageHeader(); messageHeader.setId(UUID.randomUUID().toString()); - messageHeader.setCorrelationId(correlationId); + messageHeader.setTransactionId(correlationId); messageHeader.setCollection(collectionName); messageHeader.setMessageType(messageType); messageHeader.setOrigin(replicaId); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java index d60831110..fe56b6b41 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java @@ -37,7 +37,7 @@ public void handleMessage(WebSocket webSocket, BatchAck message) { Receipt receipt = message.getReceipt(); FeedLedger feedLedger = replicatedCollection.getFeedLedger(); feedLedger.writeOff(receipt); - replicatedCollection.sendAndReceive(webSocket, message.getHeader().getCorrelationId(), + replicatedCollection.sendAndReceive(webSocket, message.getHeader().getTransactionId(), message.getHeader().getTimestamp()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java index 79139fa21..4b8e348e7 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java @@ -39,7 +39,7 @@ public BatchChangeEndHandler(ReplicatedCollection replicatedCollection) { public void handleMessage(WebSocket webSocket, BatchChangeEnd message) { MessageFactory factory = new MessageFactory(); BatchEndAck batchEndAck = factory.createBatchEndAck(replicatedCollection.getConfig(), - replicatedCollection.getReplicaId(), message.getHeader().getCorrelationId()); + replicatedCollection.getReplicaId(), message.getHeader().getTransactionId()); batchEndAck.setStartTime(message.getStartTime()); batchEndAck.setEndTime(message.getEndTime()); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java index 64bb099d6..fbb6b2c5e 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java @@ -36,7 +36,7 @@ public ConnectAckHandler(ReplicatedCollection replicatedCollection) { public void handleMessage(WebSocket webSocket, ConnectAck message) { replicatedCollection.collectGarbage(message.getTombstoneTtl()); replicatedCollection.setConnected(true); - replicatedCollection.sendAndReceive(webSocket, message.getHeader().getCorrelationId(), + replicatedCollection.sendAndReceive(webSocket, message.getHeader().getTransactionId(), message.getHeader().getTimestamp()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java index a3d0abeb4..7c52e8b6f 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java @@ -39,7 +39,7 @@ default void sendAck(WebSocket webSocket, ReceiptAware message) { getReplicatedCollection().getLastWriteWinMap().merge(state); Receipt receipt = message.calculateReceipt(); - Ack ack = createAck(message.getHeader().getCorrelationId(), receipt); + Ack ack = createAck(message.getHeader().getTransactionId(), receipt); if (ack instanceof TimeBoundMessage && message instanceof TimeBoundMessage) { TimeBoundMessage timeBoundMessage = (TimeBoundMessage) message; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java index 28ab91e35..ff80b6d75 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java @@ -24,7 +24,8 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchAck extends TimeBoundMessage implements DataGateMessage { +public class BatchAck extends TimeBoundMessage implements DataGateMessage, BatchMessage { private MessageHeader header; private Receipt receipt; + private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java index 2d79f5fac..7381cee2d 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java @@ -25,9 +25,10 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchChangeContinue extends TimeBoundMessage implements ReceiptAware { +public class BatchChangeContinue extends TimeBoundMessage implements ReceiptAware, BatchMessage { private MessageHeader header; private LastWriteWinState feed; private Integer batchSize; private Integer debounce; + private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java index 28f4dd3a8..93f84bbe0 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java @@ -24,8 +24,9 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchChangeEnd extends TimeBoundMessage implements DataGateMessage { +public class BatchChangeEnd extends TimeBoundMessage implements BatchMessage { private MessageHeader header; private Integer batchSize; private Integer debounce; + private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java index 9af619952..530f28cb4 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java @@ -27,9 +27,10 @@ @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class BatchChangeStart extends TimeBoundMessage implements ReceiptAware { +public class BatchChangeStart extends TimeBoundMessage implements ReceiptAware, BatchMessage { private MessageHeader header; private Integer batchSize; private Integer debounce; private LastWriteWinState feed; + private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java index 42d4c2228..7a250f03f 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java @@ -24,6 +24,7 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchEndAck extends TimeBoundMessage implements DataGateMessage { +public class BatchEndAck extends TimeBoundMessage implements BatchMessage { private MessageHeader header; + private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java new file mode 100644 index 000000000..4aa4a1629 --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.sync.message; + +/** + * @author Anindya Chatterjee + */ +public interface BatchMessage extends DataGateMessage { + int getNextOffset(); +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java index 0c4136871..3c9aaaa1b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/MessageHeader.java @@ -24,7 +24,9 @@ @Data public class MessageHeader { private String id; + private String transactionId; private String correlationId; + private String tenant; private String collection; private String userName; private Long timestamp; diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java index 5589ba2c6..83a3f9a05 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java @@ -176,7 +176,7 @@ protected void handleConnect(Session session, Connect connect) throws IOExceptio ConnectAck ack = new ConnectAck(); ack.setHeader(createHeader(MessageType.ConnectAck, connect.getHeader().getCollection(), userName, - mockRepository.getServerId(), connect.getHeader().getCorrelationId())); + mockRepository.getServerId(), connect.getHeader().getTransactionId())); ack.setTombstoneTtl(mockRepository.getGcTtl()); String message = objectMapper.writeValueAsString(ack); session.getBasicRemote().sendText(message); @@ -186,7 +186,7 @@ protected void handleConnect(Session session, Connect connect) throws IOExceptio errorMessage.setError("Unauthorized"); errorMessage.setHeader(createHeader(MessageType.Error, connect.getHeader().getCollection(), userName, - mockRepository.getServerId(), connect.getHeader().getCorrelationId())); + mockRepository.getServerId(), connect.getHeader().getTransactionId())); String message = objectMapper.writeValueAsString(errorMessage); session.getBasicRemote().sendText(message); } @@ -202,13 +202,13 @@ protected void handleBatchChangeStart(Session session, BatchChangeStart batchCha replica.merge(batchChangeStart.getFeed(), batchChangeStart.getEndTime()); feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeStart.getHeader().getCollection(), - userName, replicaId, batchChangeStart.getHeader().getCorrelationId())); + userName, replicaId, batchChangeStart.getHeader().getTransactionId())); feed.setFeed(batchChangeStart.getFeed()); BatchAck ack = new BatchAck(); ack.setReceipt(feed.calculateReceipt()); ack.setHeader(createHeader(MessageType.BatchAck, batchChangeStart.getHeader().getCollection(), - userName, mockRepository.getServerId(), batchChangeStart.getHeader().getCorrelationId())); + userName, mockRepository.getServerId(), batchChangeStart.getHeader().getTransactionId())); ack.setStartTime(batchChangeStart.getStartTime()); ack.setEndTime(batchChangeStart.getEndTime()); @@ -226,13 +226,13 @@ protected void handleBatchChangeContinue(Session session, BatchChangeContinue ba replica.merge(batchChangeContinue.getFeed(), batchChangeContinue.getEndTime()); feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeContinue.getHeader().getCollection(), - userName, replicaId, batchChangeContinue.getHeader().getCorrelationId())); + userName, replicaId, batchChangeContinue.getHeader().getTransactionId())); feed.setFeed(batchChangeContinue.getFeed()); BatchAck ack = new BatchAck(); ack.setReceipt(feed.calculateReceipt()); ack.setHeader(createHeader(MessageType.BatchAck, batchChangeContinue.getHeader().getCollection(), - userName, mockRepository.getServerId(), batchChangeContinue.getHeader().getCorrelationId())); + userName, mockRepository.getServerId(), batchChangeContinue.getHeader().getTransactionId())); ack.setStartTime(batchChangeContinue.getStartTime()); ack.setEndTime(batchChangeContinue.getEndTime()); @@ -248,7 +248,7 @@ protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeE BatchEndAck ack = new BatchEndAck(); ack.setHeader(createHeader(MessageType.BatchEndAck, batchChangeEnd.getHeader().getCollection(), - userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getCorrelationId())); + userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getTransactionId())); ack.setStartTime(batchChangeEnd.getStartTime()); ack.setEndTime(batchChangeEnd.getEndTime()); @@ -265,7 +265,7 @@ protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeE BatchChangeStart batchChangeStart = new BatchChangeStart(); batchChangeStart.setHeader(createHeader(MessageType.BatchChangeStart, - collection, userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getCorrelationId())); + collection, userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getTransactionId())); batchChangeStart.setStartTime(batchChangeEnd.getStartTime()); batchChangeStart.setEndTime(batchChangeEnd.getEndTime()); batchChangeStart.setBatchSize(batchSize); @@ -297,7 +297,7 @@ protected void handleBatchAck(Session session, BatchAck batchAck) throws IOExcep if (hasMore) { BatchChangeContinue message = new BatchChangeContinue(); message.setHeader(createHeader(MessageType.BatchChangeContinue, - collection, userName, mockRepository.getServerId(), batchAck.getHeader().getCorrelationId())); + collection, userName, mockRepository.getServerId(), batchAck.getHeader().getTransactionId())); message.setFeed(changesSince); message.setBatchSize(batchSize); message.setDebounce(debounce); @@ -310,7 +310,7 @@ protected void handleBatchAck(Session session, BatchAck batchAck) throws IOExcep } else { BatchChangeEnd message = new BatchChangeEnd(); message.setHeader(createHeader(MessageType.BatchChangeEnd, - collection, userName, mockRepository.getServerId(), batchAck.getHeader().getCorrelationId())); + collection, userName, mockRepository.getServerId(), batchAck.getHeader().getTransactionId())); message.setBatchSize(batchSize); message.setDebounce(debounce); message.setStartTime(batchAck.getStartTime()); @@ -342,7 +342,7 @@ protected void handleBatchEndAck(Session session, BatchEndAck batchEndAck) throw String collection = (String) session.getUserProperties().get("collection"); disconnect.setHeader(createHeader(MessageType.Disconnect, collection, user, mockRepository.getServerId(), - batchEndAck.getHeader().getCorrelationId())); + batchEndAck.getHeader().getTransactionId())); session.getBasicRemote().sendText(objectMapper.writeValueAsString(disconnect)); mockRepository.getCollectionReplicaMap().get(collection).remove(batchEndAck.getHeader().getOrigin()); @@ -373,7 +373,7 @@ private MessageHeader createHeader(MessageType messageType, String collection, String userName, String origin, String correlationId) { MessageHeader messageHeader = new MessageHeader(); messageHeader.setId(UUID.randomUUID().toString()); - messageHeader.setCorrelationId(correlationId); + messageHeader.setTransactionId(correlationId); messageHeader.setCollection(collection); messageHeader.setMessageType(messageType); messageHeader.setOrigin(origin); diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/MessageFactoryTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/MessageFactoryTest.java index 1267dddbb..438d94d10 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/MessageFactoryTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/MessageFactoryTest.java @@ -13,7 +13,7 @@ public void testCreateHeader() { "42", "42", "janedoe"); assertEquals("42", actualCreateHeaderResult.getOrigin()); assertEquals("collectionName", actualCreateHeaderResult.getCollection()); - assertEquals("42", actualCreateHeaderResult.getCorrelationId()); + assertEquals("42", actualCreateHeaderResult.getTransactionId()); assertEquals(MessageType.Error, actualCreateHeaderResult.getMessageType()); assertEquals("janedoe", actualCreateHeaderResult.getUserName()); } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageHeaderTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageHeaderTest.java index c25930cd3..75131d28b 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageHeaderTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageHeaderTest.java @@ -28,14 +28,14 @@ public void testEquals10() { @Test public void testEquals11() { MessageHeader messageHeader = new MessageHeader(); - messageHeader.setCorrelationId("42"); + messageHeader.setTransactionId("42"); assertFalse((new MessageHeader()).equals(messageHeader)); } @Test public void testEquals12() { MessageHeader messageHeader = new MessageHeader(); - messageHeader.setCorrelationId("42"); + messageHeader.setTransactionId("42"); assertFalse(messageHeader.equals(new MessageHeader())); } @@ -130,8 +130,8 @@ public void testSetCollection() { @Test public void testSetCorrelationId() { MessageHeader messageHeader = new MessageHeader(); - messageHeader.setCorrelationId("42"); - assertEquals("42", messageHeader.getCorrelationId()); + messageHeader.setTransactionId("42"); + assertEquals("42", messageHeader.getTransactionId()); } @Test From 87e57d8c27126693f35c1b6269415250d36077de Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 17 Aug 2021 23:42:32 +0530 Subject: [PATCH 15/78] offset aware message added --- .../dizitart/no2/sync/BatchChangeSender.java | 30 ++- .../org/dizitart/no2/sync/MessageFactory.java | 40 ++-- .../no2/sync/ReplicatedCollection.java | 5 +- .../no2/sync/handlers/BatchAckHandler.java | 3 +- .../handlers/BatchChangeContinueHandler.java | 4 +- .../sync/handlers/BatchChangeEndHandler.java | 1 + .../handlers/BatchChangeStartHandler.java | 4 +- .../no2/sync/handlers/ConnectAckHandler.java | 3 +- .../sync/handlers/DataGateFeedHandler.java | 4 +- .../no2/sync/handlers/ReceiptAckSender.java | 14 +- .../dizitart/no2/sync/message/BatchAck.java | 2 +- .../no2/sync/message/BatchChangeContinue.java | 2 +- .../no2/sync/message/BatchChangeEnd.java | 2 +- .../no2/sync/message/BatchChangeStart.java | 2 +- .../no2/sync/message/BatchEndAck.java | 2 +- .../dizitart/no2/sync/message/ConnectAck.java | 3 +- .../{BatchMessage.java => OffsetAware.java} | 5 +- .../no2/mock/server/MockDataGateEndpoint.java | 13 +- .../no2/sync/message/BatchAckTest.java | 92 --------- .../sync/message/BatchChangeContinueTest.java | 120 ------------ .../no2/sync/message/BatchChangeEndTest.java | 117 ------------ .../sync/message/BatchChangeStartTest.java | 122 ------------ .../no2/sync/message/BatchEndAckTest.java | 60 ------ .../no2/sync/message/ConnectAckTest.java | 94 --------- .../no2/sync/message/ConnectTest.java | 93 --------- .../no2/sync/message/DataGateFeedAckTest.java | 90 --------- .../no2/sync/message/DataGateFeedTest.java | 92 --------- .../no2/sync/message/DisconnectAckTest.java | 60 ------ .../no2/sync/message/DisconnectTest.java | 62 ------ .../no2/sync/message/ErrorMessageTest.java | 91 --------- .../no2/sync/message/MessageHeaderTest.java | 180 ------------------ .../no2/sync/message/MessageTypeTest.java | 13 -- .../no2/sync/message/ReceiptTest.java | 84 -------- 33 files changed, 73 insertions(+), 1436 deletions(-) rename nitrite-replication/src/main/java/org/dizitart/no2/sync/message/{BatchMessage.java => OffsetAware.java} (85%) delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchAckTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeContinueTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeStartTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchEndAckTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ConnectAckTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ConnectTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedAckTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DisconnectAckTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DisconnectTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ErrorMessageTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageHeaderTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageTypeTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ReceiptTest.java diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java index f8b43cec8..2e376e5c4 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java @@ -31,7 +31,6 @@ public class BatchChangeSender { private final DataGateClient dataGateClient; private boolean hasMore; - private int offset; private State currentState; private FeedLedger feedLedger; private MessageFactory messageFactory; @@ -47,43 +46,43 @@ public BatchChangeSender(Config config, configure(); } - public void sendAndReceive(WebSocket webSocket, String correlationId, Long checkpoint) { + public void sendAndReceive(WebSocket webSocket, OffsetAware offsetAware) { if (endTime == null) { - endTime = checkpoint; + endTime = offsetAware.getHeader().getTimestamp(); } switch (currentState) { case ReadyToSend: - sendStartMessage(webSocket, correlationId); + sendStartMessage(webSocket, offsetAware); break; case StartSent: - sendChanges(webSocket, correlationId); + sendChanges(webSocket, offsetAware); break; case ChangesSent: break; } } - private void sendStartMessage(WebSocket webSocket, String correlationId) { + private void sendStartMessage(WebSocket webSocket, OffsetAware offsetAware) { BatchChangeStart startMessage = messageFactory.createChangeStart(config, - replicatedCollection.getReplicaId(), correlationId); + replicatedCollection.getReplicaId(), offsetAware.getHeader().getTransactionId()); startMessage.setStartTime(lastSyncTime); startMessage.setEndTime(endTime); + startMessage.setNextOffset(offsetAware.getNextOffset() + config.getChunkSize()); LastWriteWinState state = replicatedCollection.getChangesSince(lastSyncTime, endTime, - 0, config.getChunkSize()); + offsetAware.getNextOffset(), config.getChunkSize()); startMessage.setFeed(state); dataGateClient.sendMessage(webSocket, startMessage); feedLedger.writeEntry(startMessage.getFeed()); - offset += config.getChunkSize(); currentState = State.StartSent; } - private void sendChanges(WebSocket webSocket, String correlationId) { + private void sendChanges(WebSocket webSocket, OffsetAware offsetAware) { LastWriteWinState state = replicatedCollection.getChangesSince(lastSyncTime, endTime, - offset, config.getChunkSize()); + offsetAware.getNextOffset(), config.getChunkSize()); if (state.getChangeSet().size() == 0 && state.getTombstoneMap().size() == 0) { hasMore = false; @@ -91,26 +90,26 @@ private void sendChanges(WebSocket webSocket, String correlationId) { if (hasMore) { BatchChangeContinue message = messageFactory.createChangeContinue(config, - replicatedCollection.getReplicaId(), correlationId, state); + replicatedCollection.getReplicaId(), offsetAware.getHeader().getTransactionId(), state); message.setStartTime(lastSyncTime); message.setEndTime(endTime); + message.setNextOffset(offsetAware.getNextOffset() + config.getChunkSize()); dataGateClient.sendMessage(webSocket, message); feedLedger.writeEntry(state); - offset += config.getChunkSize(); } else { Receipt finalReceipt = replicatedCollection.getFeedLedger().getFinalReceipt(); if (replicatedCollection.shouldRetry(finalReceipt)) { state = replicatedCollection.createState(finalReceipt); BatchChangeContinue message = messageFactory.createChangeContinue(config, - replicatedCollection.getReplicaId(), correlationId, state); + replicatedCollection.getReplicaId(), offsetAware.getHeader().getTransactionId(), state); message.setStartTime(lastSyncTime); message.setEndTime(endTime); dataGateClient.sendMessage(webSocket, message); feedLedger.writeEntry(state); } else { - sendEndMessage(webSocket, correlationId); + sendEndMessage(webSocket, offsetAware.getHeader().getTransactionId()); currentState = State.ChangesSent; } } @@ -128,7 +127,6 @@ private void configure() { this.feedLedger = replicatedCollection.getFeedLedger(); this.messageFactory = new MessageFactory(); this.hasMore = true; - this.offset = 0; this.currentState = State.ReadyToSend; this.lastSyncTime = replicatedCollection.getLastSyncTime(); this.endTime = null; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java index 28db06385..0e29d0dde 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java @@ -25,35 +25,35 @@ * @author Anindya Chatterjee */ public class MessageFactory { - public Connect createConnect(Config config, String replicaId, String correlationId) { + public Connect createConnect(Config config, String replicaId, String txId) { Connect message = new Connect(); message.setHeader(createHeader(MessageType.Connect, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); message.setAuthToken(config.getAuthToken()); return message; } - public Disconnect createDisconnect(Config config, String replicaId, String correlationId) { + public Disconnect createDisconnect(Config config, String replicaId, String txId) { Disconnect message = new Disconnect(); message.setHeader(createHeader(MessageType.Disconnect, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); return message; } - public BatchChangeStart createChangeStart(Config config, String replicaId, String correlationId) { + public BatchChangeStart createChangeStart(Config config, String replicaId, String txId) { BatchChangeStart message = new BatchChangeStart(); message.setHeader(createHeader(MessageType.BatchChangeStart, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); message.setBatchSize(config.getChunkSize()); message.setDebounce(config.getDebounce()); return message; } public BatchChangeContinue createChangeContinue(Config config, String replicaId, - String correlationId, LastWriteWinState state) { + String txId, LastWriteWinState state) { BatchChangeContinue message = new BatchChangeContinue(); message.setHeader(createHeader(MessageType.BatchChangeContinue, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); message.setBatchSize(config.getChunkSize()); message.setDebounce(config.getDebounce()); message.setFeed(state); @@ -61,54 +61,54 @@ public BatchChangeContinue createChangeContinue(Config config, String replicaId, } public BatchChangeEnd createChangeEnd(Config config, String replicaId, - String correlationId) { + String txId) { BatchChangeEnd message = new BatchChangeEnd(); message.setHeader(createHeader(MessageType.BatchChangeEnd, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); message.setBatchSize(config.getChunkSize()); message.setDebounce(config.getDebounce()); return message; } public DataGateFeed createFeedMessage(Config config, String replicaId, - String correlationId, LastWriteWinState state) { + String txId, LastWriteWinState state) { DataGateFeed feed = new DataGateFeed(); feed.setHeader(createHeader(MessageType.DataGateFeed, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); feed.setFeed(state); return feed; } public DataGateFeedAck createFeedAck(Config config, String replicaId, - String correlationId, Receipt receipt) { + String txId, Receipt receipt) { DataGateFeedAck ack = new DataGateFeedAck(); ack.setHeader(createHeader(MessageType.DataGateFeedAck, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); ack.setReceipt(receipt); return ack; } public BatchAck createBatchAck(Config config, String replicaId, - String correlationId, Receipt receipt) { + String txId, Receipt receipt) { BatchAck ack = new BatchAck(); ack.setHeader(createHeader(MessageType.BatchAck, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); ack.setReceipt(receipt); return ack; } - public BatchEndAck createBatchEndAck(Config config, String replicaId, String correlationId) { + public BatchEndAck createBatchEndAck(Config config, String replicaId, String txId) { BatchEndAck ack = new BatchEndAck(); ack.setHeader(createHeader(MessageType.BatchEndAck, config.getCollection().getName(), - correlationId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName())); return ack; } public MessageHeader createHeader(MessageType messageType, String collectionName, - String correlationId, String replicaId, String userName) { + String txId, String replicaId, String userName) { MessageHeader messageHeader = new MessageHeader(); messageHeader.setId(UUID.randomUUID().toString()); - messageHeader.setTransactionId(correlationId); + messageHeader.setTransactionId(txId); messageHeader.setCollection(collectionName); messageHeader.setMessageType(messageType); messageHeader.setOrigin(replicaId); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java index 81b135427..83ac3c178 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java @@ -30,6 +30,7 @@ import org.dizitart.no2.sync.crdt.LastWriteWinState; import org.dizitart.no2.sync.event.CollectionChangeListener; import org.dizitart.no2.sync.handlers.ReceiptLedgerAware; +import org.dizitart.no2.sync.message.OffsetAware; import org.dizitart.no2.sync.message.Receipt; import org.dizitart.no2.sync.net.DataGateSocket; @@ -78,8 +79,8 @@ public LastWriteWinState getChangesSince(Long startTime, Long endTime, int start return lastWriteWinMap.getChangesSince(startTime, endTime, start, chunkSize); } - public void sendAndReceive(WebSocket webSocket, String correlationId, Long checkpoint) { - batchChangeSender.sendAndReceive(webSocket, correlationId, checkpoint); + public void sendAndReceive(WebSocket webSocket, OffsetAware offsetAware) { + batchChangeSender.sendAndReceive(webSocket, offsetAware); } public void collectGarbage(Long ttl) { diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java index fe56b6b41..302f5c039 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchAckHandler.java @@ -37,7 +37,6 @@ public void handleMessage(WebSocket webSocket, BatchAck message) { Receipt receipt = message.getReceipt(); FeedLedger feedLedger = replicatedCollection.getFeedLedger(); feedLedger.writeOff(receipt); - replicatedCollection.sendAndReceive(webSocket, message.getHeader().getTransactionId(), - message.getHeader().getTimestamp()); + replicatedCollection.sendAndReceive(webSocket, message); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java index bedc68ba9..81c2fb5c6 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java @@ -40,9 +40,9 @@ public void handleMessage(WebSocket webSocket, BatchChangeContinue message) { } @Override - public BatchAck createAck(String correlationId, Receipt receipt) { + public BatchAck createAck(String transactionId, Receipt receipt) { MessageFactory factory = new MessageFactory(); return factory.createBatchAck(replicatedCollection.getConfig(), - correlationId, replicatedCollection.getReplicaId(), receipt); + transactionId, replicatedCollection.getReplicaId(), receipt); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java index 4b8e348e7..1a4e8e9d7 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java @@ -42,6 +42,7 @@ public void handleMessage(WebSocket webSocket, BatchChangeEnd message) { replicatedCollection.getReplicaId(), message.getHeader().getTransactionId()); batchEndAck.setStartTime(message.getStartTime()); batchEndAck.setEndTime(message.getEndTime()); + batchEndAck.getHeader().setCorrelationId(message.getHeader().getId()); DataGateClient dataGateClient = replicatedCollection.getDataGateClient(); dataGateClient.sendMessage(webSocket, batchEndAck); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeStartHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeStartHandler.java index 94c80cd37..ceb3bf316 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeStartHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeStartHandler.java @@ -40,9 +40,9 @@ public void handleMessage(WebSocket webSocket, BatchChangeStart message) { } @Override - public BatchAck createAck(String correlationId, Receipt receipt) { + public BatchAck createAck(String transactionId, Receipt receipt) { MessageFactory factory = new MessageFactory(); return factory.createBatchAck(replicatedCollection.getConfig(), - replicatedCollection.getReplicaId(), correlationId, receipt); + replicatedCollection.getReplicaId(), transactionId, receipt); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java index fbb6b2c5e..0babe0482 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java @@ -36,7 +36,6 @@ public ConnectAckHandler(ReplicatedCollection replicatedCollection) { public void handleMessage(WebSocket webSocket, ConnectAck message) { replicatedCollection.collectGarbage(message.getTombstoneTtl()); replicatedCollection.setConnected(true); - replicatedCollection.sendAndReceive(webSocket, message.getHeader().getTransactionId(), - message.getHeader().getTimestamp()); + replicatedCollection.sendAndReceive(webSocket, message); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java index d4166ce2a..531bdd22b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java @@ -43,9 +43,9 @@ public void handleMessage(WebSocket webSocket, DataGateFeed message) { } @Override - public DataGateFeedAck createAck(String correlationId, Receipt receipt) { + public DataGateFeedAck createAck(String transactionId, Receipt receipt) { MessageFactory factory = new MessageFactory(); return factory.createFeedAck(replicatedCollection.getConfig(), - replicatedCollection.getReplicaId(), correlationId, receipt); + replicatedCollection.getReplicaId(), transactionId, receipt); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java index 7c52e8b6f..dc1b0f291 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java @@ -20,10 +20,7 @@ import org.dizitart.no2.sync.crdt.LastWriteWinState; import org.dizitart.no2.sync.DataGateClient; import org.dizitart.no2.sync.ReplicatedCollection; -import org.dizitart.no2.sync.message.DataGateMessage; -import org.dizitart.no2.sync.message.Receipt; -import org.dizitart.no2.sync.message.ReceiptAware; -import org.dizitart.no2.sync.message.TimeBoundMessage; +import org.dizitart.no2.sync.message.*; /** * @author Anindya Chatterjee @@ -31,7 +28,7 @@ public interface ReceiptAckSender { ReplicatedCollection getReplicatedCollection(); - Ack createAck(String correlationId, Receipt receipt); + Ack createAck(String transactionId, Receipt receipt); default void sendAck(WebSocket webSocket, ReceiptAware message) { if (message != null) { @@ -40,7 +37,14 @@ default void sendAck(WebSocket webSocket, ReceiptAware message) { Receipt receipt = message.calculateReceipt(); Ack ack = createAck(message.getHeader().getTransactionId(), receipt); + ack.getHeader().setCorrelationId(message.getHeader().getId()); + // set offset + if (message instanceof OffsetAware && ack instanceof OffsetAware) { + ((OffsetAware) ack).setNextOffset(((OffsetAware) message).getNextOffset()); + } + + // set start time and end time if (ack instanceof TimeBoundMessage && message instanceof TimeBoundMessage) { TimeBoundMessage timeBoundMessage = (TimeBoundMessage) message; TimeBoundMessage timeBoundAck = (TimeBoundMessage) ack; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java index ff80b6d75..59e4380eb 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java @@ -24,7 +24,7 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchAck extends TimeBoundMessage implements DataGateMessage, BatchMessage { +public class BatchAck extends TimeBoundMessage implements OffsetAware { private MessageHeader header; private Receipt receipt; private Integer nextOffset; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java index 7381cee2d..e27f638b1 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java @@ -25,7 +25,7 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchChangeContinue extends TimeBoundMessage implements ReceiptAware, BatchMessage { +public class BatchChangeContinue extends TimeBoundMessage implements ReceiptAware, OffsetAware { private MessageHeader header; private LastWriteWinState feed; private Integer batchSize; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java index 93f84bbe0..886cb4096 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java @@ -24,7 +24,7 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchChangeEnd extends TimeBoundMessage implements BatchMessage { +public class BatchChangeEnd extends TimeBoundMessage implements OffsetAware { private MessageHeader header; private Integer batchSize; private Integer debounce; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java index 530f28cb4..ab7ca189b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java @@ -27,7 +27,7 @@ @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class BatchChangeStart extends TimeBoundMessage implements ReceiptAware, BatchMessage { +public class BatchChangeStart extends TimeBoundMessage implements ReceiptAware, OffsetAware { private MessageHeader header; private Integer batchSize; private Integer debounce; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java index 7a250f03f..57187ba46 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java @@ -24,7 +24,7 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchEndAck extends TimeBoundMessage implements BatchMessage { +public class BatchEndAck extends TimeBoundMessage implements OffsetAware { private MessageHeader header; private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java index d974f486f..a0b5be6b1 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java @@ -22,7 +22,8 @@ * @author Anindya Chatterjee */ @Data -public class ConnectAck implements DataGateMessage { +public class ConnectAck implements OffsetAware { private MessageHeader header; private Long tombstoneTtl; + private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java similarity index 85% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java index 4aa4a1629..98d9831fb 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java @@ -20,6 +20,7 @@ /** * @author Anindya Chatterjee */ -public interface BatchMessage extends DataGateMessage { - int getNextOffset(); +public interface OffsetAware extends DataGateMessage { + Integer getNextOffset(); + void setNextOffset(Integer offset); } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java index 83a3f9a05..bd644cd5c 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java @@ -178,6 +178,7 @@ protected void handleConnect(Session session, Connect connect) throws IOExceptio connect.getHeader().getCollection(), userName, mockRepository.getServerId(), connect.getHeader().getTransactionId())); ack.setTombstoneTtl(mockRepository.getGcTtl()); + ack.setNextOffset(0); String message = objectMapper.writeValueAsString(ack); session.getBasicRemote().sendText(message); } else { @@ -211,6 +212,7 @@ protected void handleBatchChangeStart(Session session, BatchChangeStart batchCha userName, mockRepository.getServerId(), batchChangeStart.getHeader().getTransactionId())); ack.setStartTime(batchChangeStart.getStartTime()); ack.setEndTime(batchChangeStart.getEndTime()); + ack.setNextOffset(batchChangeStart.getNextOffset()); String message = objectMapper.writeValueAsString(ack); session.getBasicRemote().sendText(message); @@ -235,6 +237,7 @@ protected void handleBatchChangeContinue(Session session, BatchChangeContinue ba userName, mockRepository.getServerId(), batchChangeContinue.getHeader().getTransactionId())); ack.setStartTime(batchChangeContinue.getStartTime()); ack.setEndTime(batchChangeContinue.getEndTime()); + ack.setNextOffset(batchChangeContinue.getNextOffset()); String message = objectMapper.writeValueAsString(ack); session.getBasicRemote().sendText(message); @@ -259,7 +262,6 @@ protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeE LastWriteWinState changesSince = replica.getChangesSince(batchChangeEnd.getStartTime(), batchChangeEnd.getEndTime(), 0, batchSize); - session.getUserProperties().put("offset", batchSize); session.getUserProperties().put("batchSize", batchSize); session.getUserProperties().put("debounce", debounce); @@ -271,6 +273,7 @@ protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeE batchChangeStart.setBatchSize(batchSize); batchChangeStart.setDebounce(debounce); batchChangeStart.setFeed(changesSince); + batchChangeStart.setNextOffset(0); session.getBasicRemote().sendText(objectMapper.writeValueAsString(batchChangeStart)); } @@ -278,7 +281,7 @@ protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeE protected void handleBatchAck(Session session, BatchAck batchAck) throws IOException { String userName = batchAck.getHeader().getUserName(); String collection = userName + "@" + batchAck.getHeader().getCollection(); - Integer offset = (Integer) session.getUserProperties().get("offset"); + Integer offset = batchAck.getNextOffset(); Integer batchSize = (Integer) session.getUserProperties().get("batchSize"); Integer debounce = (Integer) session.getUserProperties().get("debounce"); @@ -303,10 +306,9 @@ protected void handleBatchAck(Session session, BatchAck batchAck) throws IOExcep message.setDebounce(debounce); message.setStartTime(batchAck.getStartTime()); message.setEndTime(batchAck.getEndTime()); + message.setNextOffset(batchAck.getNextOffset() + batchSize); session.getBasicRemote().sendText(objectMapper.writeValueAsString(message)); - - session.getUserProperties().put("offset", offset + batchSize); } else { BatchChangeEnd message = new BatchChangeEnd(); message.setHeader(createHeader(MessageType.BatchChangeEnd, @@ -315,9 +317,10 @@ protected void handleBatchAck(Session session, BatchAck batchAck) throws IOExcep message.setDebounce(debounce); message.setStartTime(batchAck.getStartTime()); message.setEndTime(batchAck.getEndTime()); + message.setNextOffset(batchAck.getNextOffset()); + message.setNextOffset(0); session.getBasicRemote().sendText(objectMapper.writeValueAsString(message)); - session.getUserProperties().put("offset", 0); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchAckTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchAckTest.java deleted file mode 100644 index e9060c196..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchAckTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class BatchAckTest { - @Test - public void testCanEqual() { - assertFalse((new BatchAck()).canEqual("other")); - } - - @Test - public void testEquals() { - BatchAck batchAck = new BatchAck(); - batchAck.setHeader(new MessageHeader()); - assertFalse((new BatchAck()).equals(batchAck)); - } - - @Test - public void testEquals2() { - BatchAck batchAck = new BatchAck(); - batchAck.setHeader(new MessageHeader()); - assertFalse(batchAck.equals(new BatchAck())); - } - - @Test - public void testEquals3() { - BatchAck batchAck = new BatchAck(); - batchAck.setReceipt(new Receipt()); - BatchAck batchAck1 = new BatchAck(); - batchAck1.setReceipt(new Receipt()); - assertTrue(batchAck.equals(batchAck1)); - } - - @Test - public void testEquals4() { - BatchAck batchAck = new BatchAck(); - batchAck.setReceipt(new Receipt()); - assertFalse((new BatchAck()).equals(batchAck)); - } - - @Test - public void testEquals5() { - BatchAck batchAck = new BatchAck(); - batchAck.setHeader(new MessageHeader()); - BatchAck batchAck1 = new BatchAck(); - batchAck1.setHeader(new MessageHeader()); - assertTrue(batchAck.equals(batchAck1)); - } - - @Test - public void testEquals6() { - BatchAck batchAck = new BatchAck(); - assertTrue(batchAck.equals(new BatchAck())); - } - - @Test - public void testEquals7() { - BatchAck batchAck = new BatchAck(); - batchAck.setReceipt(new Receipt()); - assertFalse(batchAck.equals(new BatchAck())); - } - - @Test - public void testEquals8() { - assertFalse((new BatchAck()).equals("o")); - } - - @Test - public void testSetHeader() { - BatchAck batchAck = new BatchAck(); - batchAck.setHeader(new MessageHeader()); - assertEquals( - "BatchAck(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null, timestamp=null," - + " messageType=null, origin=null), receipt=null)", - batchAck.toString()); - } - - @Test - public void testSetReceipt() { - BatchAck batchAck = new BatchAck(); - batchAck.setReceipt(new Receipt()); - assertEquals("BatchAck(header=null, receipt=Receipt(added=[], removed=[]))", batchAck.toString()); - } - - @Test - public void testToString() { - assertEquals("BatchAck(header=null, receipt=null)", (new BatchAck()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeContinueTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeContinueTest.java deleted file mode 100644 index 6b9c24b5e..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeContinueTest.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.junit.Test; - -import static org.junit.Assert.*; -import static org.junit.Assert.assertEquals; - -public class BatchChangeContinueTest { - @Test - public void testCanEqual() { - assertFalse((new BatchChangeContinue()).canEqual("other")); - } - - @Test - public void testEquals() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setHeader(new MessageHeader()); - assertFalse(batchChangeContinue.equals(new BatchChangeContinue())); - } - - @Test - public void testEquals10() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setDebounce(0); - assertFalse((new BatchChangeContinue()).equals(batchChangeContinue)); - } - - @Test - public void testEquals2() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setDebounce(0); - assertFalse(batchChangeContinue.equals(new BatchChangeContinue())); - } - - @Test - public void testEquals3() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setHeader(new MessageHeader()); - assertFalse((new BatchChangeContinue()).equals(batchChangeContinue)); - } - - @Test - public void testEquals4() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setBatchSize(3); - assertFalse(batchChangeContinue.equals(new BatchChangeContinue())); - } - - @Test - public void testEquals5() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - assertTrue(batchChangeContinue.equals(new BatchChangeContinue())); - } - - @Test - public void testEquals6() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setFeed(new LastWriteWinState()); - assertFalse(batchChangeContinue.equals(new BatchChangeContinue())); - } - - @Test - public void testEquals7() { - assertFalse((new BatchChangeContinue()).equals("o")); - } - - @Test - public void testEquals8() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setFeed(new LastWriteWinState()); - assertFalse((new BatchChangeContinue()).equals(batchChangeContinue)); - } - - @Test - public void testEquals9() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setBatchSize(3); - assertFalse((new BatchChangeContinue()).equals(batchChangeContinue)); - } - - @Test - public void testSetBatchSize() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setBatchSize(3); - assertEquals(3, batchChangeContinue.getBatchSize().intValue()); - } - - @Test - public void testSetDebounce() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setDebounce(1); - assertEquals(1, batchChangeContinue.getDebounce().intValue()); - } - - @Test - public void testSetFeed() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setFeed(new LastWriteWinState()); - assertEquals("BatchChangeContinue(header=null, feed=LastWriteWinState(changeSet=[], tombstoneMap={}), " + - "batchSize=null, debounce=null)", batchChangeContinue.toString()); - } - - @Test - public void testSetHeader() { - BatchChangeContinue batchChangeContinue = new BatchChangeContinue(); - batchChangeContinue.setHeader(new MessageHeader()); - assertEquals( - "BatchChangeContinue(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null," - + " timestamp=null, messageType=null, origin=null), feed=null, batchSize=null, debounce=null)", - batchChangeContinue.toString()); - } - - @Test - public void testToString() { - assertEquals("BatchChangeContinue(header=null, feed=null, batchSize=null, debounce=null)", - (new BatchChangeContinue()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java deleted file mode 100644 index 0b4209444..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeEndTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class BatchChangeEndTest { - @Test - public void testCanEqual() { - assertFalse((new BatchChangeEnd()).canEqual("other")); - } - - @Test - public void testEquals() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setEndTime(0L); - assertFalse((new BatchChangeEnd()).equals(batchChangeEnd)); - } - - @Test - public void testEquals10() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setEndTime(0L); - assertFalse(batchChangeEnd.equals(new BatchChangeEnd())); - } - - @Test - public void testEquals2() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setDebounce(0); - assertFalse((new BatchChangeEnd()).equals(batchChangeEnd)); - } - - @Test - public void testEquals3() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - assertTrue(batchChangeEnd.equals(new BatchChangeEnd())); - } - - @Test - public void testEquals4() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setHeader(new MessageHeader()); - assertFalse(batchChangeEnd.equals(new BatchChangeEnd())); - } - - @Test - public void testEquals5() { - assertFalse((new BatchChangeEnd()).equals("o")); - } - - @Test - public void testEquals6() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setBatchSize(3); - assertFalse(batchChangeEnd.equals(new BatchChangeEnd())); - } - - @Test - public void testEquals7() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setBatchSize(3); - assertFalse((new BatchChangeEnd()).equals(batchChangeEnd)); - } - - @Test - public void testEquals8() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setHeader(new MessageHeader()); - assertFalse((new BatchChangeEnd()).equals(batchChangeEnd)); - } - - @Test - public void testEquals9() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setDebounce(0); - assertFalse(batchChangeEnd.equals(new BatchChangeEnd())); - } - - @Test - public void testSetBatchSize() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setBatchSize(3); - assertEquals(3, batchChangeEnd.getBatchSize().intValue()); - } - - @Test - public void testSetDebounce() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setDebounce(1); - assertEquals(1, batchChangeEnd.getDebounce().intValue()); - } - - @Test - public void testSetHeader() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setHeader(new MessageHeader()); - assertEquals( - "BatchChangeEnd(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null, " + - "timestamp=null, messageType=null, origin=null), batchSize=null, debounce=null)", - batchChangeEnd.toString()); - } - - @Test - public void testSetLastSynced() { - BatchChangeEnd batchChangeEnd = new BatchChangeEnd(); - batchChangeEnd.setEndTime(1L); - assertEquals(1L, batchChangeEnd.getEndTime().longValue()); - } - - @Test - public void testToString() { - assertEquals("BatchChangeEnd(header=null, batchSize=null, debounce=null)", - (new BatchChangeEnd()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeStartTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeStartTest.java deleted file mode 100644 index d033fc947..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchChangeStartTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class BatchChangeStartTest { - @Test - public void testCanEqual() { - assertFalse((new BatchChangeStart()).canEqual("other")); - } - - @Test - public void testEquals() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setDebounce(0); - assertFalse((new BatchChangeStart()).equals(batchChangeStart)); - } - - @Test - public void testEquals10() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setFeed(new LastWriteWinState()); - assertFalse(batchChangeStart.equals(new BatchChangeStart())); - } - - @Test - public void testEquals2() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setHeader(new MessageHeader()); - assertFalse(batchChangeStart.equals(new BatchChangeStart())); - } - - @Test - public void testEquals3() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setFeed(new LastWriteWinState()); - assertFalse((new BatchChangeStart()).equals(batchChangeStart)); - } - - @Test - public void testEquals4() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setBatchSize(3); - assertFalse(batchChangeStart.equals(new BatchChangeStart())); - } - - @Test - public void testEquals5() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setHeader(new MessageHeader()); - assertFalse((new BatchChangeStart()).equals(batchChangeStart)); - } - - @Test - public void testEquals6() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setBatchSize(3); - assertFalse((new BatchChangeStart()).equals(batchChangeStart)); - } - - @Test - public void testEquals7() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setDebounce(0); - assertFalse(batchChangeStart.equals(new BatchChangeStart())); - } - - @Test - public void testEquals8() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - assertTrue(batchChangeStart.equals(new BatchChangeStart())); - } - - @Test - public void testEquals9() { - assertFalse((new BatchChangeStart()).equals("o")); - } - - @Test - public void testSetBatchSize() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setBatchSize(3); - assertEquals(3, batchChangeStart.getBatchSize().intValue()); - } - - @Test - public void testSetDebounce() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setDebounce(1); - assertEquals(1, batchChangeStart.getDebounce().intValue()); - } - - @Test - public void testSetFeed() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setFeed(new LastWriteWinState()); - assertEquals("BatchChangeStart(super=TimeBoundMessage(startTime=null, endTime=null), header=null, " + - "batchSize=null, debounce=null, feed=LastWriteWinState(changeSet=[], tombstoneMap={}))", - batchChangeStart.toString()); - } - - @Test - public void testSetHeader() { - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setHeader(new MessageHeader()); - assertEquals( - "BatchChangeStart(super=TimeBoundMessage(startTime=null, endTime=null), header=MessageHeader" + - "(id=null, correlationId=null, collection=null, userName=null, timestamp=null, messageType=null, " + - "origin=null), batchSize=null, debounce=null, feed=null)", - batchChangeStart.toString()); - } - - @Test - public void testToString() { - assertEquals("BatchChangeStart(super=TimeBoundMessage(startTime=null, endTime=null), header=null, " + - "batchSize=null, debounce=null, feed=null)", - (new BatchChangeStart()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchEndAckTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchEndAckTest.java deleted file mode 100644 index 14c279aa9..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/BatchEndAckTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class BatchEndAckTest { - @Test - public void testCanEqual() { - assertFalse((new BatchEndAck()).canEqual("other")); - } - - @Test - public void testEquals() { - BatchEndAck batchEndAck = new BatchEndAck(); - batchEndAck.setHeader(new MessageHeader()); - assertFalse(batchEndAck.equals(new BatchEndAck())); - } - - @Test - public void testEquals2() { - BatchEndAck batchEndAck = new BatchEndAck(); - batchEndAck.setHeader(new MessageHeader()); - assertFalse((new BatchEndAck()).equals(batchEndAck)); - } - - @Test - public void testEquals3() { - BatchEndAck batchEndAck = new BatchEndAck(); - assertTrue(batchEndAck.equals(new BatchEndAck())); - } - - @Test - public void testEquals4() { - BatchEndAck batchEndAck = new BatchEndAck(); - batchEndAck.setHeader(new MessageHeader()); - BatchEndAck batchEndAck1 = new BatchEndAck(); - batchEndAck1.setHeader(new MessageHeader()); - assertTrue(batchEndAck.equals(batchEndAck1)); - } - - @Test - public void testEquals5() { - assertFalse((new BatchEndAck()).equals("o")); - } - - @Test - public void testSetHeader() { - BatchEndAck batchEndAck = new BatchEndAck(); - batchEndAck.setHeader(new MessageHeader()); - assertEquals("BatchEndAck(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null," - + " timestamp=null, messageType=null, origin=null))", batchEndAck.toString()); - } - - @Test - public void testToString() { - assertEquals("BatchEndAck(header=null)", (new BatchEndAck()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ConnectAckTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ConnectAckTest.java deleted file mode 100644 index 94873cf1c..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ConnectAckTest.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class ConnectAckTest { - @Test - public void testCanEqual() { - assertFalse((new ConnectAck()).canEqual("other")); - } - - @Test - public void testEquals() { - ConnectAck connectAck = new ConnectAck(); - connectAck.setTombstoneTtl(0L); - assertFalse(connectAck.equals(new ConnectAck())); - } - - @Test - public void testEquals2() { - ConnectAck connectAck = new ConnectAck(); - connectAck.setTombstoneTtl(0L); - assertFalse((new ConnectAck()).equals(connectAck)); - } - - @Test - public void testEquals3() { - ConnectAck connectAck = new ConnectAck(); - connectAck.setHeader(new MessageHeader()); - ConnectAck connectAck1 = new ConnectAck(); - connectAck1.setHeader(new MessageHeader()); - assertTrue(connectAck.equals(connectAck1)); - } - - @Test - public void testEquals4() { - ConnectAck connectAck = new ConnectAck(); - assertTrue(connectAck.equals(new ConnectAck())); - } - - @Test - public void testEquals5() { - ConnectAck connectAck = new ConnectAck(); - connectAck.setTombstoneTtl(1L); - ConnectAck connectAck1 = new ConnectAck(); - connectAck1.setTombstoneTtl(1L); - assertTrue(connectAck.equals(connectAck1)); - } - - @Test - public void testEquals6() { - ConnectAck connectAck = new ConnectAck(); - connectAck.setHeader(new MessageHeader()); - assertFalse((new ConnectAck()).equals(connectAck)); - } - - @Test - public void testEquals7() { - ConnectAck connectAck = new ConnectAck(); - connectAck.setHeader(new MessageHeader()); - assertFalse(connectAck.equals(new ConnectAck())); - } - - @Test - public void testEquals8() { - assertFalse((new ConnectAck()).equals("o")); - } - - @Test - public void testSetHeader() { - ConnectAck connectAck = new ConnectAck(); - connectAck.setHeader(new MessageHeader()); - assertEquals( - "ConnectAck(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null, timestamp=null," - + " messageType=null, origin=null), tombstoneTtl=null)", - connectAck.toString()); - } - - @Test - public void testSetTombstoneTtl() { - ConnectAck connectAck = new ConnectAck(); - connectAck.setTombstoneTtl(1L); - assertEquals(1L, connectAck.getTombstoneTtl().longValue()); - } - - @Test - public void testToString() { - assertEquals("ConnectAck(header=null, tombstoneTtl=null)", (new ConnectAck()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ConnectTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ConnectTest.java deleted file mode 100644 index d885e16c2..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ConnectTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class ConnectTest { - @Test - public void testCanEqual() { - assertFalse((new Connect()).canEqual("other")); - } - - @Test - public void testEquals() { - Connect connect = new Connect(); - connect.setAuthToken("ABC123"); - assertFalse(connect.equals(new Connect())); - } - - @Test - public void testEquals2() { - Connect connect = new Connect(); - assertTrue(connect.equals(new Connect())); - } - - @Test - public void testEquals3() { - Connect connect = new Connect(); - connect.setAuthToken("ABC123"); - assertFalse((new Connect()).equals(connect)); - } - - @Test - public void testEquals4() { - Connect connect = new Connect(); - connect.setHeader(new MessageHeader()); - assertFalse(connect.equals(new Connect())); - } - - @Test - public void testEquals5() { - Connect connect = new Connect(); - connect.setHeader(new MessageHeader()); - assertFalse((new Connect()).equals(connect)); - } - - @Test - public void testEquals6() { - Connect connect = new Connect(); - connect.setHeader(new MessageHeader()); - Connect connect1 = new Connect(); - connect1.setHeader(new MessageHeader()); - assertTrue(connect.equals(connect1)); - } - - @Test - public void testEquals7() { - Connect connect = new Connect(); - connect.setAuthToken("ABC123"); - Connect connect1 = new Connect(); - connect1.setAuthToken("ABC123"); - connect1.setHeader(null); - assertTrue(connect.equals(connect1)); - } - - @Test - public void testEquals8() { - assertFalse((new Connect()).equals("o")); - } - - @Test - public void testSetAuthToken() { - Connect connect = new Connect(); - connect.setAuthToken("ABC123"); - assertEquals("ABC123", connect.getAuthToken()); - } - - @Test - public void testSetHeader() { - Connect connect = new Connect(); - connect.setHeader(new MessageHeader()); - assertEquals( - "Connect(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null, timestamp=null," - + " messageType=null, origin=null), authToken=null)", - connect.toString()); - } - - @Test - public void testToString() { - assertEquals("Connect(header=null, authToken=null)", (new Connect()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedAckTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedAckTest.java deleted file mode 100644 index 9d5e8a770..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedAckTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class DataGateFeedAckTest { - @Test - public void testCanEqual() { - assertFalse((new DataGateFeedAck()).canEqual("other")); - } - - @Test - public void testEquals() { - assertFalse((new DataGateFeedAck()).equals("o")); - } - - @Test - public void testEquals2() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - assertTrue(dataGateFeedAck.equals(new DataGateFeedAck())); - } - - @Test - public void testEquals3() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - dataGateFeedAck.setHeader(new MessageHeader()); - assertFalse((new DataGateFeedAck()).equals(dataGateFeedAck)); - } - - @Test - public void testEquals4() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - dataGateFeedAck.setReceipt(new Receipt()); - assertFalse((new DataGateFeedAck()).equals(dataGateFeedAck)); - } - - @Test - public void testEquals5() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - dataGateFeedAck.setHeader(new MessageHeader()); - DataGateFeedAck dataGateFeedAck1 = new DataGateFeedAck(); - dataGateFeedAck1.setHeader(new MessageHeader()); - assertTrue(dataGateFeedAck.equals(dataGateFeedAck1)); - } - - @Test - public void testEquals6() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - dataGateFeedAck.setHeader(new MessageHeader()); - assertFalse(dataGateFeedAck.equals(new DataGateFeedAck())); - } - - @Test - public void testEquals7() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - dataGateFeedAck.setReceipt(new Receipt()); - DataGateFeedAck dataGateFeedAck1 = new DataGateFeedAck(); - dataGateFeedAck1.setReceipt(new Receipt()); - assertTrue(dataGateFeedAck.equals(dataGateFeedAck1)); - } - - @Test - public void testEquals8() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - dataGateFeedAck.setReceipt(new Receipt()); - assertFalse(dataGateFeedAck.equals(new DataGateFeedAck())); - } - - @Test - public void testSetHeader() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - dataGateFeedAck.setHeader(new MessageHeader()); - assertEquals("DataGateFeedAck(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null," - + " timestamp=null, messageType=null, origin=null), receipt=null)", dataGateFeedAck.toString()); - } - - @Test - public void testSetReceipt() { - DataGateFeedAck dataGateFeedAck = new DataGateFeedAck(); - dataGateFeedAck.setReceipt(new Receipt()); - assertEquals("DataGateFeedAck(header=null, receipt=Receipt(added=[], removed=[]))", dataGateFeedAck.toString()); - } - - @Test - public void testToString() { - assertEquals("DataGateFeedAck(header=null, receipt=null)", (new DataGateFeedAck()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedTest.java deleted file mode 100644 index 1094bbf4b..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DataGateFeedTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class DataGateFeedTest { - @Test - public void testCanEqual() { - assertFalse((new DataGateFeed()).canEqual("other")); - } - - @Test - public void testEquals() { - DataGateFeed dataGateFeed = new DataGateFeed(); - dataGateFeed.setHeader(new MessageHeader()); - DataGateFeed dataGateFeed1 = new DataGateFeed(); - dataGateFeed1.setHeader(new MessageHeader()); - assertTrue(dataGateFeed.equals(dataGateFeed1)); - } - - @Test - public void testEquals2() { - DataGateFeed dataGateFeed = new DataGateFeed(); - assertTrue(dataGateFeed.equals(new DataGateFeed())); - } - - @Test - public void testEquals3() { - DataGateFeed dataGateFeed = new DataGateFeed(); - dataGateFeed.setFeed(new LastWriteWinState()); - DataGateFeed dataGateFeed1 = new DataGateFeed(); - dataGateFeed1.setFeed(new LastWriteWinState()); - assertTrue(dataGateFeed.equals(dataGateFeed1)); - } - - @Test - public void testEquals4() { - assertFalse((new DataGateFeed()).equals("o")); - } - - @Test - public void testEquals5() { - DataGateFeed dataGateFeed = new DataGateFeed(); - dataGateFeed.setFeed(new LastWriteWinState()); - assertFalse((new DataGateFeed()).equals(dataGateFeed)); - } - - @Test - public void testEquals6() { - DataGateFeed dataGateFeed = new DataGateFeed(); - dataGateFeed.setFeed(new LastWriteWinState()); - assertFalse(dataGateFeed.equals(new DataGateFeed())); - } - - @Test - public void testEquals7() { - DataGateFeed dataGateFeed = new DataGateFeed(); - dataGateFeed.setHeader(new MessageHeader()); - assertFalse((new DataGateFeed()).equals(dataGateFeed)); - } - - @Test - public void testEquals8() { - DataGateFeed dataGateFeed = new DataGateFeed(); - dataGateFeed.setHeader(new MessageHeader()); - assertFalse(dataGateFeed.equals(new DataGateFeed())); - } - - @Test - public void testSetFeed() { - DataGateFeed dataGateFeed = new DataGateFeed(); - dataGateFeed.setFeed(new LastWriteWinState()); - assertEquals("DataGateFeed(header=null, feed=LastWriteWinState(changeSet=[], tombstoneMap={}))", - dataGateFeed.toString()); - } - - @Test - public void testSetHeader() { - DataGateFeed dataGateFeed = new DataGateFeed(); - dataGateFeed.setHeader(new MessageHeader()); - assertEquals("DataGateFeed(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null," - + " timestamp=null, messageType=null, origin=null), feed=null)", dataGateFeed.toString()); - } - - @Test - public void testToString() { - assertEquals("DataGateFeed(header=null, feed=null)", (new DataGateFeed()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DisconnectAckTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DisconnectAckTest.java deleted file mode 100644 index ef41de26d..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DisconnectAckTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class DisconnectAckTest { - @Test - public void testCanEqual() { - assertFalse((new DisconnectAck()).canEqual("other")); - } - - @Test - public void testEquals() { - DisconnectAck disconnectAck = new DisconnectAck(); - disconnectAck.setHeader(new MessageHeader()); - DisconnectAck disconnectAck1 = new DisconnectAck(); - disconnectAck1.setHeader(new MessageHeader()); - assertTrue(disconnectAck.equals(disconnectAck1)); - } - - @Test - public void testEquals2() { - assertFalse((new DisconnectAck()).equals("o")); - } - - @Test - public void testEquals3() { - DisconnectAck disconnectAck = new DisconnectAck(); - disconnectAck.setHeader(new MessageHeader()); - assertFalse((new DisconnectAck()).equals(disconnectAck)); - } - - @Test - public void testEquals4() { - DisconnectAck disconnectAck = new DisconnectAck(); - disconnectAck.setHeader(new MessageHeader()); - assertFalse(disconnectAck.equals(new DisconnectAck())); - } - - @Test - public void testEquals5() { - DisconnectAck disconnectAck = new DisconnectAck(); - assertTrue(disconnectAck.equals(new DisconnectAck())); - } - - @Test - public void testSetHeader() { - DisconnectAck disconnectAck = new DisconnectAck(); - disconnectAck.setHeader(new MessageHeader()); - assertEquals("DisconnectAck(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null," - + " timestamp=null, messageType=null, origin=null))", disconnectAck.toString()); - } - - @Test - public void testToString() { - assertEquals("DisconnectAck(header=null)", (new DisconnectAck()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DisconnectTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DisconnectTest.java deleted file mode 100644 index 515313eca..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/DisconnectTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class DisconnectTest { - @Test - public void testCanEqual() { - assertFalse((new Disconnect()).canEqual("other")); - } - - @Test - public void testEquals() { - Disconnect disconnect = new Disconnect(); - disconnect.setHeader(new MessageHeader()); - assertFalse(disconnect.equals(new Disconnect())); - } - - @Test - public void testEquals2() { - Disconnect disconnect = new Disconnect(); - disconnect.setHeader(new MessageHeader()); - Disconnect disconnect1 = new Disconnect(); - disconnect1.setHeader(new MessageHeader()); - assertTrue(disconnect.equals(disconnect1)); - } - - @Test - public void testEquals3() { - Disconnect disconnect = new Disconnect(); - assertTrue(disconnect.equals(new Disconnect())); - } - - @Test - public void testEquals4() { - Disconnect disconnect = new Disconnect(); - disconnect.setHeader(new MessageHeader()); - assertFalse((new Disconnect()).equals(disconnect)); - } - - @Test - public void testEquals5() { - assertFalse((new Disconnect()).equals("o")); - } - - @Test - public void testSetHeader() { - Disconnect disconnect = new Disconnect(); - disconnect.setHeader(new MessageHeader()); - assertEquals( - "Disconnect(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null, timestamp=null," - + " messageType=null, origin=null))", - disconnect.toString()); - } - - @Test - public void testToString() { - assertEquals("Disconnect(header=null)", (new Disconnect()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ErrorMessageTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ErrorMessageTest.java deleted file mode 100644 index e2159667a..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ErrorMessageTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class ErrorMessageTest { - @Test - public void testCanEqual() { - assertFalse((new ErrorMessage()).canEqual("other")); - } - - @Test - public void testEquals() { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setHeader(new MessageHeader()); - assertFalse((new ErrorMessage()).equals(errorMessage)); - } - - @Test - public void testEquals2() { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setHeader(new MessageHeader()); - ErrorMessage errorMessage1 = new ErrorMessage(); - errorMessage1.setHeader(new MessageHeader()); - assertTrue(errorMessage.equals(errorMessage1)); - } - - @Test - public void testEquals3() { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setError("An error occurred"); - assertFalse(errorMessage.equals(new ErrorMessage())); - } - - @Test - public void testEquals4() { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setError("An error occurred"); - ErrorMessage errorMessage1 = new ErrorMessage(); - errorMessage1.setError("An error occurred"); - errorMessage1.setHeader(null); - assertTrue(errorMessage.equals(errorMessage1)); - } - - @Test - public void testEquals5() { - ErrorMessage errorMessage = new ErrorMessage(); - assertTrue(errorMessage.equals(new ErrorMessage())); - } - - @Test - public void testEquals6() { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setError("An error occurred"); - assertFalse((new ErrorMessage()).equals(errorMessage)); - } - - @Test - public void testEquals7() { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setHeader(new MessageHeader()); - assertFalse(errorMessage.equals(new ErrorMessage())); - } - - @Test - public void testEquals8() { - assertFalse((new ErrorMessage()).equals("o")); - } - - @Test - public void testSetError() { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setError("An error occurred"); - assertEquals("An error occurred", errorMessage.getError()); - } - - @Test - public void testSetHeader() { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setHeader(new MessageHeader()); - assertEquals("ErrorMessage(header=MessageHeader(id=null, correlationId=null, collection=null, userName=null," - + " timestamp=null, messageType=null, origin=null), error=null)", errorMessage.toString()); - } - - @Test - public void testToString() { - assertEquals("ErrorMessage(header=null, error=null)", (new ErrorMessage()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageHeaderTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageHeaderTest.java deleted file mode 100644 index 75131d28b..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageHeaderTest.java +++ /dev/null @@ -1,180 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import static org.junit.Assert.*; -import static org.junit.Assert.assertEquals; - -public class MessageHeaderTest { - @Test - public void testCanEqual() { - assertFalse((new MessageHeader()).canEqual("other")); - } - - @Test - public void testEquals() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setId("42"); - assertFalse(messageHeader.equals(new MessageHeader())); - } - - @Test - public void testEquals10() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setUserName("janedoe"); - assertFalse(messageHeader.equals(new MessageHeader())); - } - - @Test - public void testEquals11() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setTransactionId("42"); - assertFalse((new MessageHeader()).equals(messageHeader)); - } - - @Test - public void testEquals12() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setTransactionId("42"); - assertFalse(messageHeader.equals(new MessageHeader())); - } - - @Test - public void testEquals13() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setCollection("collection"); - assertFalse(messageHeader.equals(new MessageHeader())); - } - - @Test - public void testEquals14() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setOrigin("origin"); - assertFalse((new MessageHeader()).equals(messageHeader)); - } - - @Test - public void testEquals15() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setCollection("collection"); - assertFalse((new MessageHeader()).equals(messageHeader)); - } - - @Test - public void testEquals16() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setId("42"); - assertFalse((new MessageHeader()).equals(messageHeader)); - } - - @Test - public void testEquals2() { - MessageHeader messageHeader = new MessageHeader(); - assertTrue(messageHeader.equals(new MessageHeader())); - } - - @Test - public void testEquals3() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setUserName("janedoe"); - assertFalse((new MessageHeader()).equals(messageHeader)); - } - - @Test - public void testEquals4() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setMessageType(MessageType.Error); - assertFalse(messageHeader.equals(new MessageHeader())); - } - - @Test - public void testEquals5() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setMessageType(MessageType.Error); - assertFalse((new MessageHeader()).equals(messageHeader)); - } - - @Test - public void testEquals6() { - assertFalse((new MessageHeader()).equals("o")); - } - - @Test - public void testEquals7() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setOrigin("origin"); - assertFalse(messageHeader.equals(new MessageHeader())); - } - - @Test - public void testEquals8() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setTimestamp(10L); - assertFalse(messageHeader.equals(new MessageHeader())); - } - - @Test - public void testEquals9() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setTimestamp(10L); - assertFalse((new MessageHeader()).equals(messageHeader)); - } - - @Test - public void testSetCollection() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setCollection("collection"); - assertEquals("collection", messageHeader.getCollection()); - } - - @Test - public void testSetCorrelationId() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setTransactionId("42"); - assertEquals("42", messageHeader.getTransactionId()); - } - - @Test - public void testSetId() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setId("42"); - assertEquals("42", messageHeader.getId()); - } - - @Test - public void testSetMessageType() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setMessageType(MessageType.Error); - assertEquals(MessageType.Error, messageHeader.getMessageType()); - } - - @Test - public void testSetOrigin() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setOrigin("origin"); - assertEquals("origin", messageHeader.getOrigin()); - } - - @Test - public void testSetTimestamp() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setTimestamp(10L); - assertEquals(10L, messageHeader.getTimestamp().longValue()); - } - - @Test - public void testSetUserName() { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setUserName("janedoe"); - assertEquals("janedoe", messageHeader.getUserName()); - } - - @Test - public void testToString() { - assertEquals( - "MessageHeader(id=null, correlationId=null, collection=null, userName=null, timestamp=null, messageType=null," - + " origin=null)", - (new MessageHeader()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageTypeTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageTypeTest.java deleted file mode 100644 index 25a5d5173..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/MessageTypeTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.dizitart.no2.sync.message; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class MessageTypeTest { - @Test - public void testCode() { - assertEquals("no2.sync.error", MessageType.Error.code()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ReceiptTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ReceiptTest.java deleted file mode 100644 index 832a5052a..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/message/ReceiptTest.java +++ /dev/null @@ -1,84 +0,0 @@ -package org.dizitart.no2.sync.message; - -import org.junit.Test; - -import java.util.HashSet; - -import static org.junit.Assert.*; - -public class ReceiptTest { - @Test - public void testCanEqual() { - assertFalse((new Receipt()).canEqual("other")); - } - - @Test - public void testConstructor() { - HashSet added = new HashSet(); - assertEquals("Receipt(added=[], removed=[])", (new Receipt(added, new HashSet())).toString()); - } - - @Test - public void testConstructor2() { - assertEquals("Receipt(added=[], removed=[])", (new Receipt()).toString()); - } - - @Test - public void testEquals() { - Receipt receipt = new Receipt(); - receipt.setRemoved(null); - assertFalse(receipt.equals(new Receipt())); - } - - @Test - public void testEquals2() { - assertFalse((new Receipt()).equals("o")); - } - - @Test - public void testEquals3() { - Receipt receipt = new Receipt(); - receipt.setAdded(null); - assertFalse(receipt.equals(new Receipt())); - } - - @Test - public void testEquals4() { - Receipt receipt = new Receipt(); - receipt.setRemoved(null); - assertFalse((new Receipt()).equals(receipt)); - } - - @Test - public void testEquals5() { - Receipt receipt = new Receipt(); - assertTrue(receipt.equals(new Receipt())); - } - - @Test - public void testEquals6() { - Receipt receipt = new Receipt(); - receipt.setAdded(null); - assertFalse((new Receipt()).equals(receipt)); - } - - @Test - public void testSetAdded() { - Receipt receipt = new Receipt(); - receipt.setAdded(new HashSet()); - assertEquals("Receipt(added=[], removed=[])", receipt.toString()); - } - - @Test - public void testSetRemoved() { - Receipt receipt = new Receipt(); - receipt.setRemoved(new HashSet()); - assertEquals("Receipt(added=[], removed=[])", receipt.toString()); - } - - @Test - public void testToString() { - assertEquals("Receipt(added=[], removed=[])", (new Receipt()).toString()); - } -} - From 8cd8d2045a6f0a453ac7e92f490eca1d8090f102 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 18 Aug 2021 18:04:08 +0530 Subject: [PATCH 16/78] chunk size --- .../dizitart/no2/sync/BatchChangeSender.java | 3 + .../java/org/dizitart/no2/sync/Config.java | 3 +- .../org/dizitart/no2/sync/MessageFactory.java | 25 ++- .../java/org/dizitart/no2/sync/Replica.java | 2 +- .../org/dizitart/no2/sync/ReplicaBuilder.java | 97 +++++++-- .../no2/sync/ReplicationException.java | 5 + .../no2/sync/handlers/ReceiptAckSender.java | 3 +- .../dizitart/no2/sync/message/BatchAck.java | 1 + .../no2/sync/message/BatchChangeContinue.java | 1 - .../no2/sync/message/BatchChangeEnd.java | 1 - .../no2/sync/message/BatchChangeStart.java | 3 +- .../no2/sync/message/BatchEndAck.java | 1 + .../dizitart/no2/sync/message/ConnectAck.java | 1 + .../no2/sync/message/OffsetAware.java | 2 + .../integration/DataGateIntegrationTest.java | 4 +- .../no2/mock/ReplicaNegativeTest.java | 82 ++++++- .../org/dizitart/no2/mock/ReplicaTest.java | 75 +++++-- .../no2/mock/server/MockDataGateEndpoint.java | 61 +++--- .../org/dizitart/no2/sync/ConfigTest.java | 203 ------------------ .../dizitart/no2/sync/MessageFactoryTest.java | 5 +- 20 files changed, 280 insertions(+), 298 deletions(-) delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/sync/ConfigTest.java diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java index 2e376e5c4..41a046b1c 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java @@ -69,6 +69,7 @@ private void sendStartMessage(WebSocket webSocket, OffsetAware offsetAware) { startMessage.setStartTime(lastSyncTime); startMessage.setEndTime(endTime); startMessage.setNextOffset(offsetAware.getNextOffset() + config.getChunkSize()); + startMessage.setBatchSize(config.getChunkSize()); LastWriteWinState state = replicatedCollection.getChangesSince(lastSyncTime, endTime, offsetAware.getNextOffset(), config.getChunkSize()); @@ -94,6 +95,7 @@ private void sendChanges(WebSocket webSocket, OffsetAware offsetAware) { message.setStartTime(lastSyncTime); message.setEndTime(endTime); message.setNextOffset(offsetAware.getNextOffset() + config.getChunkSize()); + message.setBatchSize(config.getChunkSize()); dataGateClient.sendMessage(webSocket, message); feedLedger.writeEntry(state); @@ -120,6 +122,7 @@ private void sendEndMessage(WebSocket webSocket, String correlationId) { replicatedCollection.getReplicaId(), correlationId); endMessage.setStartTime(lastSyncTime); endMessage.setEndTime(endTime); + endMessage.setBatchSize(config.getChunkSize()); dataGateClient.sendMessage(webSocket, endMessage); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java index c1ce25b12..a85bc864e 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java @@ -36,7 +36,8 @@ public class Config { private NitriteCollection collection; private Integer chunkSize; private String userName; - private Integer debounce; + private String tenant; + private Integer pollingRate; private ObjectMapper objectMapper; private TimeSpan timeout; private Request.Builder requestBuilder; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java index 0e29d0dde..9e3ec7dd7 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java @@ -28,7 +28,7 @@ public class MessageFactory { public Connect createConnect(Config config, String replicaId, String txId) { Connect message = new Connect(); message.setHeader(createHeader(MessageType.Connect, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); message.setAuthToken(config.getAuthToken()); return message; } @@ -36,16 +36,15 @@ public Connect createConnect(Config config, String replicaId, String txId) { public Disconnect createDisconnect(Config config, String replicaId, String txId) { Disconnect message = new Disconnect(); message.setHeader(createHeader(MessageType.Disconnect, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); return message; } public BatchChangeStart createChangeStart(Config config, String replicaId, String txId) { BatchChangeStart message = new BatchChangeStart(); message.setHeader(createHeader(MessageType.BatchChangeStart, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); message.setBatchSize(config.getChunkSize()); - message.setDebounce(config.getDebounce()); return message; } @@ -53,9 +52,8 @@ public BatchChangeContinue createChangeContinue(Config config, String replicaId, String txId, LastWriteWinState state) { BatchChangeContinue message = new BatchChangeContinue(); message.setHeader(createHeader(MessageType.BatchChangeContinue, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); message.setBatchSize(config.getChunkSize()); - message.setDebounce(config.getDebounce()); message.setFeed(state); return message; } @@ -64,9 +62,8 @@ public BatchChangeEnd createChangeEnd(Config config, String replicaId, String txId) { BatchChangeEnd message = new BatchChangeEnd(); message.setHeader(createHeader(MessageType.BatchChangeEnd, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); message.setBatchSize(config.getChunkSize()); - message.setDebounce(config.getDebounce()); return message; } @@ -74,7 +71,7 @@ public DataGateFeed createFeedMessage(Config config, String replicaId, String txId, LastWriteWinState state) { DataGateFeed feed = new DataGateFeed(); feed.setHeader(createHeader(MessageType.DataGateFeed, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); feed.setFeed(state); return feed; } @@ -83,7 +80,7 @@ public DataGateFeedAck createFeedAck(Config config, String replicaId, String txId, Receipt receipt) { DataGateFeedAck ack = new DataGateFeedAck(); ack.setHeader(createHeader(MessageType.DataGateFeedAck, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); ack.setReceipt(receipt); return ack; } @@ -92,7 +89,7 @@ public BatchAck createBatchAck(Config config, String replicaId, String txId, Receipt receipt) { BatchAck ack = new BatchAck(); ack.setHeader(createHeader(MessageType.BatchAck, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); ack.setReceipt(receipt); return ack; } @@ -100,12 +97,13 @@ public BatchAck createBatchAck(Config config, String replicaId, public BatchEndAck createBatchEndAck(Config config, String replicaId, String txId) { BatchEndAck ack = new BatchEndAck(); ack.setHeader(createHeader(MessageType.BatchEndAck, config.getCollection().getName(), - txId, replicaId, config.getUserName())); + txId, replicaId, config.getUserName(), config.getTenant())); return ack; } public MessageHeader createHeader(MessageType messageType, String collectionName, - String txId, String replicaId, String userName) { + String txId, String replicaId, + String userName, String tenant) { MessageHeader messageHeader = new MessageHeader(); messageHeader.setId(UUID.randomUUID().toString()); messageHeader.setTransactionId(txId); @@ -114,6 +112,7 @@ public MessageHeader createHeader(MessageType messageType, String collectionName messageHeader.setOrigin(replicaId); messageHeader.setTimestamp(System.currentTimeMillis()); messageHeader.setUserName(userName); + messageHeader.setTenant(tenant); return messageHeader; } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java index 24c21813b..e55e2f9a0 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java @@ -59,7 +59,7 @@ public void connect() { if (!isConnected()) { replicatedCollection.startReplication(); } - }, 0, config.getDebounce(), TimeUnit.MILLISECONDS); + }, 0, config.getPollingRate(), TimeUnit.MILLISECONDS); } } else { throw new ReplicationException("replica is not configured properly", true); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java index 325631cd4..2494fd7f5 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java @@ -17,6 +17,7 @@ package org.dizitart.no2.sync; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; import okhttp3.Request; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.repository.ObjectRepository; @@ -28,21 +29,27 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; +import static org.dizitart.no2.common.util.StringUtils.isNullOrEmpty; + /** * A builder api for creating a nitrite {@link Replica}. * * @author Anindya Chatterjee. * @since 4.0.0 */ +@Slf4j public class ReplicaBuilder { private NitriteCollection collection; - private String datagateServerUrl; + private String remoteHost; + private Integer remotePort; private String authToken; private TimeSpan timeout; - private TimeSpan debounce; + private TimeSpan pollingRate; private Integer chunkSize; + private String tenant; private String userName; private ObjectMapper objectMapper; private Proxy proxy; @@ -55,8 +62,9 @@ public class ReplicaBuilder { */ ReplicaBuilder() { chunkSize = 10; + remotePort = 46005; // nitrite molar mass timeout = new TimeSpan(5, TimeUnit.SECONDS); - debounce = new TimeSpan(1, TimeUnit.SECONDS); + pollingRate = new TimeSpan(1, TimeUnit.SECONDS); objectMapper = new ObjectMapper(); objectMapper.registerModule(new DocumentModule()); eventListeners = new ArrayList<>(); @@ -84,20 +92,42 @@ public ReplicaBuilder of(ObjectRepository repository) { } /** - * Sets the remote datagate server url. + * Sets the remote datagate server host. + * + * @param remoteHost the replication server host + * @return the replica builder + */ + public ReplicaBuilder remoteHost(String remoteHost) { + this.remoteHost = remoteHost; + return this; + } + + /** + * Sets the remote datagate server port. * - * @param datagateServerUrl the replication server + * @param remotePort the replication server port * @return the replica builder */ - public ReplicaBuilder remote(String datagateServerUrl) { - this.datagateServerUrl = datagateServerUrl; + public ReplicaBuilder remotePort(Integer remotePort) { + this.remotePort = remotePort; return this; } /** - * Sets the JWT auth token and user name. + * Sets the remote datagate server tenant id. * - * @param userName the user name + * @param tenantId the replication server tenant id + * @return the replica builder + */ + public ReplicaBuilder tenant(String tenantId) { + this.tenant = tenantId; + return this; + } + + /** + * Sets the JWT auth token and username. + * + * @param userName the username * @param authToken the auth token * @return the replica builder */ @@ -110,7 +140,7 @@ public ReplicaBuilder jwtAuth(String userName, String authToken) { /** * Sets the basic auth token. * - * @param userName the user name + * @param userName the username * @param password the password * @return the replica builder */ @@ -143,13 +173,13 @@ public ReplicaBuilder chunkSize(Integer size) { } /** - * Sets the debounce value. + * Sets the polling rate value. * * @param timeSpan the time span * @return the replica builder */ - public ReplicaBuilder debounce(TimeSpan timeSpan) { - this.debounce = timeSpan; + public ReplicaBuilder pollingRate(TimeSpan timeSpan) { + this.pollingRate = timeSpan; return this; } @@ -178,7 +208,7 @@ public ReplicaBuilder proxy(Proxy proxy) { /** * Sets a flag to accept all certificates. * - * @param accept the accept + * @param accept to accept * @return the replica builder */ public ReplicaBuilder acceptAllCertificates(boolean accept) { @@ -197,12 +227,17 @@ public ReplicaBuilder addReplicationEventListener(ReplicationEventListener liste return this; } + /** + * Sets an optional name for the replica. + * + * @param name the name of the replica + * @return the replica builder + */ public ReplicaBuilder replicaName(String name) { this.replicaName = name; return this; } - /** * Creates a {@link Replica}. * @@ -216,7 +251,8 @@ public Replica create() { config.setCollection(collection); config.setChunkSize(chunkSize); config.setUserName(userName); - config.setDebounce(getTimeoutInMillis(debounce)); + config.setTenant(tenant); + config.setPollingRate(getTimeoutInMillis(pollingRate)); config.setObjectMapper(objectMapper); config.setTimeout(timeout); config.setRequestBuilder(builder); @@ -234,8 +270,13 @@ public Replica create() { } private Request.Builder createRequestBuilder() { + validateBuilder(); + String remoteUrl = String.format(Locale.getDefault(), "ws://%s:%d/ws/datagate/%s/%s/%s", + remoteHost, remotePort, tenant, collection.getName(), userName); + + log.debug("Using remote datagate url " + remoteUrl); Request.Builder builder = new Request.Builder(); - builder.url(datagateServerUrl); + builder.url(remoteUrl); return builder; } @@ -246,4 +287,26 @@ private String toHex(String arg) { private int getTimeoutInMillis(TimeSpan connectTimeout) { return Math.toIntExact(connectTimeout.getTimeUnit().toMillis(connectTimeout.getTime())); } + + private void validateBuilder() { + if (isNullOrEmpty(remoteHost)) { + throw new ReplicationException("remote host is a mandatory field"); + } + + if (remotePort == null) { + throw new ReplicationException("remote port is a mandatory field"); + } + + if (isNullOrEmpty(tenant)) { + throw new ReplicationException("tenant id is a mandatory field"); + } + + if (collection == null || isNullOrEmpty(collection.getName())) { + throw new ReplicationException("collection is a mandatory field"); + } + + if (isNullOrEmpty(userName)) { + throw new ReplicationException("username is a mandatory field"); + } + } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationException.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationException.java index 788b84751..e0d541675 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationException.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicationException.java @@ -24,6 +24,11 @@ public class ReplicationException extends NitriteException { private final boolean fatal; + public ReplicationException(String errorMessage) { + super(errorMessage); + this.fatal = false; + } + public ReplicationException(String errorMessage, boolean fatal) { super(errorMessage); this.fatal = fatal; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java index dc1b0f291..3fb0e4b34 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java @@ -39,9 +39,10 @@ default void sendAck(WebSocket webSocket, ReceiptAware message) { Ack ack = createAck(message.getHeader().getTransactionId(), receipt); ack.getHeader().setCorrelationId(message.getHeader().getId()); - // set offset + // set offset and batch size if (message instanceof OffsetAware && ack instanceof OffsetAware) { ((OffsetAware) ack).setNextOffset(((OffsetAware) message).getNextOffset()); + ((OffsetAware) ack).setBatchSize(((OffsetAware) message).getBatchSize()); } // set start time and end time diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java index 59e4380eb..f6ba34da0 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java @@ -28,4 +28,5 @@ public class BatchAck extends TimeBoundMessage implements OffsetAware { private MessageHeader header; private Receipt receipt; private Integer nextOffset; + private Integer batchSize; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java index e27f638b1..01bd33ff1 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java @@ -29,6 +29,5 @@ public class BatchChangeContinue extends TimeBoundMessage implements ReceiptAwar private MessageHeader header; private LastWriteWinState feed; private Integer batchSize; - private Integer debounce; private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java index 886cb4096..3bd984980 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java @@ -27,6 +27,5 @@ public class BatchChangeEnd extends TimeBoundMessage implements OffsetAware { private MessageHeader header; private Integer batchSize; - private Integer debounce; private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java index ab7ca189b..972e1d021 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java @@ -29,8 +29,7 @@ @ToString(callSuper = true) public class BatchChangeStart extends TimeBoundMessage implements ReceiptAware, OffsetAware { private MessageHeader header; - private Integer batchSize; - private Integer debounce; private LastWriteWinState feed; + private Integer batchSize; private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java index 57187ba46..b2559b7e7 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java @@ -26,5 +26,6 @@ @EqualsAndHashCode(callSuper = true) public class BatchEndAck extends TimeBoundMessage implements OffsetAware { private MessageHeader header; + private Integer batchSize; private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java index a0b5be6b1..c9b0afccd 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java @@ -26,4 +26,5 @@ public class ConnectAck implements OffsetAware { private MessageHeader header; private Long tombstoneTtl; private Integer nextOffset; + private Integer batchSize; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java index 98d9831fb..d95c6adde 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java @@ -23,4 +23,6 @@ public interface OffsetAware extends DataGateMessage { Integer getNextOffset(); void setNextOffset(Integer offset); + Integer getBatchSize(); + void setBatchSize(Integer batchSize); } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index a0a7746a6..a17af9283 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -62,7 +62,9 @@ public static void main(String[] args) { log.info("Token - " + jwt); Replica replica = Replica.builder() .of(collection) - .remote("wss://127.0.0.1:3030/ws/datagate/abcd@gmail.com/datagateIntegration") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("abcd@gmail.com", jwt) .acceptAllCertificates(true) .create(); diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java index 3205a28f5..605f69179 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java @@ -26,6 +26,7 @@ import org.dizitart.no2.mock.server.MockRepository; import org.dizitart.no2.mock.server.ServerLastWriteWinMap; import org.dizitart.no2.sync.Replica; +import org.dizitart.no2.sync.ReplicationException; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -38,8 +39,7 @@ import static org.dizitart.no2.TestUtils.createDb; import static org.dizitart.no2.TestUtils.randomDocument; import static org.dizitart.no2.mock.ReplicaTest.getRandomTempDbFile; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * @author Anindya Chatterjee @@ -56,7 +56,7 @@ public class ReplicaNegativeTest { @Before public void setUp() throws Exception { dbFile = getRandomTempDbFile(); - server = new MockDataGateServer(9090); + server = new MockDataGateServer(46005); executorService = ThreadPoolManager.getThreadPool(2, "ReplicaNegativeTest"); server.start(); mockRepository = MockRepository.getInstance(); @@ -80,7 +80,9 @@ public void testServerClose() { Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testServerClose") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .create(); @@ -103,4 +105,76 @@ public void testServerClose() { server.stop(); await().atMost(5, SECONDS).until(() -> !r1.isConnected()); } + + @Test(expected = ReplicationException.class) + public void testRemoteHostValidation() { + Nitrite db1 = createDb(dbFile); + NitriteCollection c1 = db1.getCollection("testServerClose"); + + + Replica r1 = Replica.builder() + .of(c1) + .remotePort(46005) + .tenant("junit-test") + .jwtAuth("anidotnet", "abcd") + .create(); + } + + @Test(expected = ReplicationException.class) + public void testRemotePortValidation() { + Nitrite db1 = createDb(dbFile); + NitriteCollection c1 = db1.getCollection("testServerClose"); + + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost("127.0.0.1") + .remotePort(null) + .tenant("junit-test") + .jwtAuth("anidotnet", "abcd") + .create(); + } + + @Test(expected = ReplicationException.class) + public void testTenantValidation() { + Nitrite db1 = createDb(dbFile); + NitriteCollection c1 = db1.getCollection("testServerClose"); + + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost("127.0.0.1") + .remotePort(46005) + .jwtAuth("anidotnet", "abcd") + .create(); + } + + @Test(expected = ReplicationException.class) + public void testCollectionValidation() { + Nitrite db1 = createDb(dbFile); + NitriteCollection c1 = db1.getCollection("testServerClose"); + + + Replica r1 = Replica.builder() + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") + .jwtAuth("anidotnet", "abcd") + .create(); + } + + @Test(expected = ReplicationException.class) + public void testUserValidation() { + Nitrite db1 = createDb(dbFile); + NitriteCollection c1 = db1.getCollection("testServerClose"); + + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") + .jwtAuth("", "abcd") + .create(); + } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java index 49c63ef0d..9bb593628 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java @@ -83,7 +83,7 @@ public static String getRandomTempDbFile() { @Before public void setUp() throws Exception { - server = new MockDataGateServer(9090); + server = new MockDataGateServer(46005); server.start(); dbFile = getRandomTempDbFile(); executorService = Executors.newCachedThreadPool(); @@ -122,7 +122,9 @@ public void testSingleUserSingleReplica() { Replica replica = Replica.builder() .of(collection) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testSingleUserSingleReplica") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .create(); @@ -177,14 +179,18 @@ public void testSingleUserMultiReplica() { Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testSingleUserMultiReplica") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .replicaName("r1") .create(); Replica r2 = Replica.builder() .of(c2) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testSingleUserMultiReplica") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .replicaName("r2") .create(); @@ -293,21 +299,27 @@ public void testMultiUserSingleReplica() { Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/user1/testSingleUserSingleReplica") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("user1", "abcd") .create(); r1.connect(); Replica r2 = Replica.builder() .of(c2) - .remote("ws://127.0.0.1:9090/datagate/user2/testSingleUserSingleReplica") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("user2", "abcd") .create(); r2.connect(); Replica r3 = Replica.builder() .of(c3) - .remote("ws://127.0.0.1:9090/datagate/user3/testSingleUserSingleReplica") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("user3", "abcd") .create(); r3.connect(); @@ -357,14 +369,16 @@ public void testMultiUserMultiReplica() { Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/user1/testMultiUserSingleReplica1") + .remoteHost("127.0.0.1") + .tenant("junit-test") .jwtAuth("user1", "abcd") .create(); r1.connect(); Replica r2 = Replica.builder() .of(c2) - .remote("ws://127.0.0.1:9090/datagate/user2/testMultiUserSingleReplica2") + .remoteHost("127.0.0.1") + .tenant("junit-test") .jwtAuth("user2", "abcd") .create(); r2.connect(); @@ -399,7 +413,8 @@ public void testSecurityInCorrectCredentials() { Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/user/testSecurity") + .remoteHost("127.0.0.1") + .tenant("junit-test") .jwtAuth("user", "wrong_token") .create(); r1.connect(); @@ -427,13 +442,17 @@ public void testCloseDbAndReconnect() { Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testCloseDbAndReconnect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .create(); Replica r2 = Replica.builder() .of(c2) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testCloseDbAndReconnect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .create(); @@ -486,7 +505,9 @@ public void testCloseDbAndReconnect() { c1 = db.getCollection("testCloseDbAndReconnect"); r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testCloseDbAndReconnect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .create(); @@ -537,7 +558,9 @@ public void testDelayedConnect() { NitriteCollection c1 = db1.getCollection("testDelayedConnect"); Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testDelayedConnect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .create(); @@ -557,7 +580,9 @@ public void testDelayedConnect() { NitriteCollection c2 = db2.getCollection("testDelayedConnect"); Replica r2 = Replica.builder() .of(c2) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testDelayedConnect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .create(); r2.connect(); @@ -575,7 +600,9 @@ public void testDelayedConnectRemoveAll() { NitriteCollection c1 = db.getCollection("testDelayedConnect"); Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testDelayedConnect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .replicaName("r1") .create(); @@ -598,7 +625,9 @@ public void testDelayedConnectRemoveAll() { NitriteCollection c2 = db2.getCollection("testDelayedConnect"); Replica r2 = Replica.builder() .of(c2) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testDelayedConnect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .replicaName("r2") .create(); @@ -614,7 +643,9 @@ public void testDelayedConnectRemoveAll() { NitriteCollection c3 = db.getCollection("testDelayedConnect"); r1 = Replica.builder() .of(c3) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testDelayedConnect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .replicaName("r1") .create(); @@ -638,7 +669,9 @@ public void testGarbageCollect() { NitriteCollection c1 = db.getCollection("testGarbageCollect"); Replica r1 = Replica.builder() .of(c1) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testGarbageCollect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .create(); @@ -667,7 +700,9 @@ public void testGarbageCollect() { NitriteCollection c2 = db.getCollection("testGarbageCollect"); r1 = Replica.builder() .of(c2) - .remote("ws://127.0.0.1:9090/datagate/anidotnet/testGarbageCollect") + .remoteHost("127.0.0.1") + .remotePort(46005) + .tenant("junit-test") .jwtAuth("anidotnet", "abcd") .replicaName("r1") .create(); diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java index bd644cd5c..220d6374d 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java @@ -43,7 +43,7 @@ */ @Slf4j @Data -@ServerEndpoint(value = "/datagate/{user}/{collection}") +@ServerEndpoint(value = "/ws/datagate/{tenant}/{collection}/{user}") public class MockDataGateEndpoint { private ObjectMapper objectMapper; private MockRepository mockRepository; @@ -59,10 +59,12 @@ public MockDataGateEndpoint() { @OnOpen public void onOpen(@PathParam("user") String user, + @PathParam("tenant") String tenant, @PathParam("collection") String collection, Session session) { log.info("DataGate server connection established"); session.getUserProperties().put("user", user); + session.getUserProperties().put("tenant", tenant); session.getUserProperties().put("collection", user + "@" + collection); session.getUserProperties().put("authorized", false); } @@ -123,7 +125,7 @@ public void onError(Session session, Throwable ex) { try { ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setHeader(createHeader(MessageType.Error, null, null, + errorMessage.setHeader(createHeader(session, MessageType.Error, null, null, mockRepository.getServerId(), "")); errorMessage.setError(ex.getMessage()); String message = objectMapper.writeValueAsString(errorMessage); @@ -174,18 +176,20 @@ protected void handleConnect(Session session, Connect connect) throws IOExceptio } ConnectAck ack = new ConnectAck(); - ack.setHeader(createHeader(MessageType.ConnectAck, + ack.setHeader(createHeader(session, MessageType.ConnectAck, connect.getHeader().getCollection(), userName, mockRepository.getServerId(), connect.getHeader().getTransactionId())); ack.setTombstoneTtl(mockRepository.getGcTtl()); ack.setNextOffset(0); + ack.setBatchSize(0); + String message = objectMapper.writeValueAsString(ack); session.getBasicRemote().sendText(message); } else { session.getUserProperties().put("authorized", false); ErrorMessage errorMessage = new ErrorMessage(); errorMessage.setError("Unauthorized"); - errorMessage.setHeader(createHeader(MessageType.Error, + errorMessage.setHeader(createHeader(session, MessageType.Error, connect.getHeader().getCollection(), userName, mockRepository.getServerId(), connect.getHeader().getTransactionId())); String message = objectMapper.writeValueAsString(errorMessage); @@ -202,17 +206,18 @@ protected void handleBatchChangeStart(Session session, BatchChangeStart batchCha ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); replica.merge(batchChangeStart.getFeed(), batchChangeStart.getEndTime()); - feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeStart.getHeader().getCollection(), + feed.setHeader(createHeader(session, MessageType.DataGateFeed, batchChangeStart.getHeader().getCollection(), userName, replicaId, batchChangeStart.getHeader().getTransactionId())); feed.setFeed(batchChangeStart.getFeed()); BatchAck ack = new BatchAck(); ack.setReceipt(feed.calculateReceipt()); - ack.setHeader(createHeader(MessageType.BatchAck, batchChangeStart.getHeader().getCollection(), + ack.setHeader(createHeader(session, MessageType.BatchAck, batchChangeStart.getHeader().getCollection(), userName, mockRepository.getServerId(), batchChangeStart.getHeader().getTransactionId())); ack.setStartTime(batchChangeStart.getStartTime()); ack.setEndTime(batchChangeStart.getEndTime()); ack.setNextOffset(batchChangeStart.getNextOffset()); + ack.setBatchSize(batchChangeStart.getBatchSize()); String message = objectMapper.writeValueAsString(ack); session.getBasicRemote().sendText(message); @@ -227,17 +232,18 @@ protected void handleBatchChangeContinue(Session session, BatchChangeContinue ba ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); replica.merge(batchChangeContinue.getFeed(), batchChangeContinue.getEndTime()); - feed.setHeader(createHeader(MessageType.DataGateFeed, batchChangeContinue.getHeader().getCollection(), + feed.setHeader(createHeader(session, MessageType.DataGateFeed, batchChangeContinue.getHeader().getCollection(), userName, replicaId, batchChangeContinue.getHeader().getTransactionId())); feed.setFeed(batchChangeContinue.getFeed()); BatchAck ack = new BatchAck(); ack.setReceipt(feed.calculateReceipt()); - ack.setHeader(createHeader(MessageType.BatchAck, batchChangeContinue.getHeader().getCollection(), + ack.setHeader(createHeader(session, MessageType.BatchAck, batchChangeContinue.getHeader().getCollection(), userName, mockRepository.getServerId(), batchChangeContinue.getHeader().getTransactionId())); ack.setStartTime(batchChangeContinue.getStartTime()); ack.setEndTime(batchChangeContinue.getEndTime()); ack.setNextOffset(batchChangeContinue.getNextOffset()); + ack.setBatchSize(batchChangeContinue.getBatchSize()); String message = objectMapper.writeValueAsString(ack); session.getBasicRemote().sendText(message); @@ -245,12 +251,11 @@ protected void handleBatchChangeContinue(Session session, BatchChangeContinue ba protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeEnd) throws IOException { Integer batchSize = batchChangeEnd.getBatchSize(); - Integer debounce = batchChangeEnd.getDebounce(); String userName = batchChangeEnd.getHeader().getUserName(); String collection = userName + "@" + batchChangeEnd.getHeader().getCollection(); BatchEndAck ack = new BatchEndAck(); - ack.setHeader(createHeader(MessageType.BatchEndAck, batchChangeEnd.getHeader().getCollection(), + ack.setHeader(createHeader(session, MessageType.BatchEndAck, batchChangeEnd.getHeader().getCollection(), userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getTransactionId())); ack.setStartTime(batchChangeEnd.getStartTime()); ack.setEndTime(batchChangeEnd.getEndTime()); @@ -262,18 +267,15 @@ protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeE LastWriteWinState changesSince = replica.getChangesSince(batchChangeEnd.getStartTime(), batchChangeEnd.getEndTime(), 0, batchSize); - session.getUserProperties().put("batchSize", batchSize); - session.getUserProperties().put("debounce", debounce); BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setHeader(createHeader(MessageType.BatchChangeStart, + batchChangeStart.setHeader(createHeader(session, MessageType.BatchChangeStart, collection, userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getTransactionId())); batchChangeStart.setStartTime(batchChangeEnd.getStartTime()); batchChangeStart.setEndTime(batchChangeEnd.getEndTime()); - batchChangeStart.setBatchSize(batchSize); - batchChangeStart.setDebounce(debounce); batchChangeStart.setFeed(changesSince); - batchChangeStart.setNextOffset(0); + batchChangeStart.setNextOffset(batchSize); + batchChangeStart.setBatchSize(batchSize); session.getBasicRemote().sendText(objectMapper.writeValueAsString(batchChangeStart)); } @@ -282,8 +284,6 @@ protected void handleBatchAck(Session session, BatchAck batchAck) throws IOExcep String userName = batchAck.getHeader().getUserName(); String collection = userName + "@" + batchAck.getHeader().getCollection(); Integer offset = batchAck.getNextOffset(); - Integer batchSize = (Integer) session.getUserProperties().get("batchSize"); - Integer debounce = (Integer) session.getUserProperties().get("debounce"); Receipt receipt = batchAck.getReceipt(); FeedLedger feedLedger = mockRepository.getFeedLedgerMap().get(collection); @@ -294,31 +294,28 @@ protected void handleBatchAck(Session session, BatchAck batchAck) throws IOExcep ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); LastWriteWinState changesSince = replica.getChangesSince(batchAck.getStartTime(), - batchAck.getEndTime(), offset, batchSize); + batchAck.getEndTime(), offset, batchAck.getBatchSize()); boolean hasMore = !(changesSince.getChangeSet().size() == 0 && changesSince.getTombstoneMap().size() == 0); if (hasMore) { BatchChangeContinue message = new BatchChangeContinue(); - message.setHeader(createHeader(MessageType.BatchChangeContinue, + message.setHeader(createHeader(session, MessageType.BatchChangeContinue, collection, userName, mockRepository.getServerId(), batchAck.getHeader().getTransactionId())); message.setFeed(changesSince); - message.setBatchSize(batchSize); - message.setDebounce(debounce); message.setStartTime(batchAck.getStartTime()); message.setEndTime(batchAck.getEndTime()); - message.setNextOffset(batchAck.getNextOffset() + batchSize); + message.setNextOffset(batchAck.getNextOffset() + batchAck.getBatchSize()); + message.setBatchSize(batchAck.getBatchSize()); session.getBasicRemote().sendText(objectMapper.writeValueAsString(message)); } else { BatchChangeEnd message = new BatchChangeEnd(); - message.setHeader(createHeader(MessageType.BatchChangeEnd, + message.setHeader(createHeader(session, MessageType.BatchChangeEnd, collection, userName, mockRepository.getServerId(), batchAck.getHeader().getTransactionId())); - message.setBatchSize(batchSize); - message.setDebounce(debounce); message.setStartTime(batchAck.getStartTime()); message.setEndTime(batchAck.getEndTime()); message.setNextOffset(batchAck.getNextOffset()); - message.setNextOffset(0); + message.setBatchSize(batchAck.getBatchSize()); session.getBasicRemote().sendText(objectMapper.writeValueAsString(message)); } @@ -344,8 +341,8 @@ protected void handleBatchEndAck(Session session, BatchEndAck batchEndAck) throw String user = (String) session.getUserProperties().get("user"); String collection = (String) session.getUserProperties().get("collection"); - disconnect.setHeader(createHeader(MessageType.Disconnect, collection, user, mockRepository.getServerId(), - batchEndAck.getHeader().getTransactionId())); + disconnect.setHeader(createHeader(session, MessageType.Disconnect, collection, user, + mockRepository.getServerId(), batchEndAck.getHeader().getTransactionId())); session.getBasicRemote().sendText(objectMapper.writeValueAsString(disconnect)); mockRepository.getCollectionReplicaMap().get(collection).remove(batchEndAck.getHeader().getOrigin()); @@ -366,13 +363,14 @@ private void sendErrorMessage(Session session, Throwable error) throws IOExcepti String user = (String) session.getUserProperties().get("user"); String collection = (String) session.getUserProperties().get("collection"); - errorMessage.setHeader(createHeader(MessageType.Error, collection, user, mockRepository.getServerId(), "")); + errorMessage.setHeader(createHeader(session, MessageType.Error, collection, user, + mockRepository.getServerId(), "")); errorMessage.setError(error.getMessage()); session.getBasicRemote().sendText(objectMapper.writeValueAsString(errorMessage)); } - private MessageHeader createHeader(MessageType messageType, String collection, + private MessageHeader createHeader(Session session, MessageType messageType, String collection, String userName, String origin, String correlationId) { MessageHeader messageHeader = new MessageHeader(); messageHeader.setId(UUID.randomUUID().toString()); @@ -382,6 +380,7 @@ private MessageHeader createHeader(MessageType messageType, String collection, messageHeader.setOrigin(origin); messageHeader.setTimestamp(System.currentTimeMillis()); messageHeader.setUserName(userName); + messageHeader.setTenant((String) session.getUserProperties().get("tenant")); return messageHeader; } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/ConfigTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/ConfigTest.java deleted file mode 100644 index 94d928b1e..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/ConfigTest.java +++ /dev/null @@ -1,203 +0,0 @@ -package org.dizitart.no2.sync; - -import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.Request; -import org.junit.Test; - -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.*; - -public class ConfigTest { - @Test - public void testCanEqual() { - assertFalse((new Config()).canEqual("other")); - } - - @Test - public void testEquals() { - Config config = new Config(); - config.setChunkSize(3); - assertFalse((new Config()).equals(config)); - } - - @Test - public void testEquals10() { - Config config = new Config(); - config.setChunkSize(3); - assertFalse(config.equals(new Config())); - } - - @Test - public void testEquals11() { - Config config = new Config(); - config.setUserName("janedoe"); - assertFalse((new Config()).equals(config)); - } - - @Test - public void testEquals12() { - Config config = new Config(); - config.setDebounce(0); - assertFalse(config.equals(new Config())); - } - - @Test - public void testEquals13() { - Config config = new Config(); - config.setDebounce(0); - assertFalse((new Config()).equals(config)); - } - - @Test - public void testEquals14() { - Config config = new Config(); - config.setObjectMapper(new ObjectMapper()); - assertFalse(config.equals(new Config())); - } - - @Test - public void testEquals15() { - Config config = new Config(); - config.setUserName("janedoe"); - assertFalse(config.equals(new Config())); - } - - @Test - public void testEquals2() { - Config config = new Config(); - config.setAuthToken("ABC123"); - assertFalse((new Config()).equals(config)); - } - - @Test - public void testEquals3() { - Config config = new Config(); - config.setObjectMapper(new ObjectMapper()); - assertFalse((new Config()).equals(config)); - } - - @Test - public void testEquals4() { - Config config = new Config(); - config.setAuthToken("ABC123"); - assertFalse(config.equals(new Config())); - } - - @Test - public void testEquals5() { - assertFalse((new Config()).equals("o")); - } - - @Test - public void testEquals6() { - Config config = new Config(); - assertTrue(config.equals(new Config())); - } - - @Test - public void testEquals7() { - Config config = new Config(); - config.setAcceptAllCertificates(true); - assertFalse(config.equals(new Config())); - } - - @Test - public void testEquals8() { - TimeSpan timeout = new TimeSpan(10L, TimeUnit.NANOSECONDS); - Config config = new Config(); - config.setTimeout(timeout); - assertFalse(config.equals(new Config())); - } - - @Test - public void testEquals9() { - Config config = new Config(); - config.setRequestBuilder(new Request.Builder()); - assertFalse(config.equals(new Config())); - } - - @Test - public void testSetAcceptAllCertificates() { - Config config = new Config(); - config.setAcceptAllCertificates(true); - assertTrue(config.isAcceptAllCertificates()); - } - - @Test - public void testSetAuthToken() { - Config config = new Config(); - config.setAuthToken("ABC123"); - assertEquals("ABC123", config.getAuthToken()); - } - - @Test - public void testSetChunkSize() { - Config config = new Config(); - config.setChunkSize(3); - assertEquals(3, config.getChunkSize().intValue()); - } - - @Test - public void testSetCollection() { - Config config = new Config(); - config.setCollection(null); - assertNull(config.getCollection()); - } - - @Test - public void testSetDebounce() { - Config config = new Config(); - config.setDebounce(1); - assertEquals(1, config.getDebounce().intValue()); - } - - @Test - public void testSetProxy() { - Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(1)); - Config config = new Config(); - config.setProxy(proxy); - assertEquals( - "Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, " + - "timeout=null, requestBuilder=null, proxy=HTTP @ 0.0.0.0/0.0.0.0:1, authToken=null, " + - "acceptAllCertificates=false, eventListeners=null, replicaName=null)", - config.toString()); - } - - @Test - public void testSetRequestBuilder() { - Config config = new Config(); - Request.Builder builder = new Request.Builder(); - config.setRequestBuilder(builder); - assertSame(builder, config.getRequestBuilder()); - } - - @Test - public void testSetTimeout() { - TimeSpan timeout = new TimeSpan(10L, TimeUnit.NANOSECONDS); - Config config = new Config(); - config.setTimeout(timeout); - assertEquals("Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, " + - "timeout=TimeSpan(time=10, timeUnit=NANOSECONDS), requestBuilder=null, proxy=null, authToken=null, " + - "acceptAllCertificates=false, eventListeners=null, replicaName=null)", config.toString()); - } - - @Test - public void testSetUserName() { - Config config = new Config(); - config.setUserName("janedoe"); - assertEquals("janedoe", config.getUserName()); - } - - @Test - public void testToString() { - assertEquals( - "Config(collection=null, chunkSize=null, userName=null, debounce=null, objectMapper=null, " + - "timeout=null, requestBuilder=null, proxy=null, authToken=null, acceptAllCertificates=false, " + - "eventListeners=null, replicaName=null)", - (new Config()).toString()); - } -} - diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/MessageFactoryTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/MessageFactoryTest.java index 438d94d10..dca081245 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/MessageFactoryTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/MessageFactoryTest.java @@ -9,13 +9,14 @@ public class MessageFactoryTest { @Test public void testCreateHeader() { - MessageHeader actualCreateHeaderResult = (new MessageFactory()).createHeader(MessageType.Error, "collectionName", - "42", "42", "janedoe"); + MessageHeader actualCreateHeaderResult = (new MessageFactory()).createHeader(MessageType.Error, + "collectionName", "42", "42", "janedoe", "junit-test"); assertEquals("42", actualCreateHeaderResult.getOrigin()); assertEquals("collectionName", actualCreateHeaderResult.getCollection()); assertEquals("42", actualCreateHeaderResult.getTransactionId()); assertEquals(MessageType.Error, actualCreateHeaderResult.getMessageType()); assertEquals("janedoe", actualCreateHeaderResult.getUserName()); + assertEquals("junit-test", actualCreateHeaderResult.getTenant()); } } From 9de2a7647188fa32d31adab7b1883bac8ed8bc75 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 19 Aug 2021 16:27:36 +0530 Subject: [PATCH 17/78] close reason formalized --- .../org/dizitart/no2/sync/DataGateClient.java | 26 ++++++++++---- .../java/org/dizitart/no2/sync/Replica.java | 3 +- .../no2/sync/ReplicatedCollection.java | 3 +- .../no2/sync/handlers/DisconnectHandler.java | 3 +- .../dizitart/no2/sync/net/CloseReason.java | 35 +++++++++++++++++++ .../org/dizitart/no2/mock/ReplicaTest.java | 4 +-- 6 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/net/CloseReason.java diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java index e45ca2ff6..448192dfc 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java @@ -28,6 +28,8 @@ import org.dizitart.no2.sync.event.ReplicationEventType; import org.dizitart.no2.sync.message.Connect; import org.dizitart.no2.sync.message.DataGateMessage; +import org.dizitart.no2.sync.message.Disconnect; +import org.dizitart.no2.sync.net.CloseReason; import org.dizitart.no2.sync.net.WebSocketCode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -73,7 +75,7 @@ public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { } catch (Exception e) { log.error("Opening websocket failed", e); eventBus.post(new ReplicationEvent(ReplicationEventType.Error, e)); - closeConnection(webSocket, "Error - " + e.getMessage()); + closeConnection(webSocket, new CloseReason("Client Error - " + e.getMessage())); } } @@ -90,7 +92,7 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { if (e instanceof ReplicationException) { if (((ReplicationException) e).isFatal()) { - closeConnection(webSocket, "Error - " + e.getMessage()); + closeConnection(webSocket, new CloseReason("Client Error - " + e.getMessage())); } } } @@ -100,7 +102,7 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { log.error("Communication failure", t); eventBus.post(new ReplicationEvent(ReplicationEventType.Error, t)); - closeConnection(webSocket,"Error - " + t.getMessage()); + closeConnection(webSocket, new CloseReason("Client Error - " + t.getMessage())); } @Override @@ -120,18 +122,28 @@ public void sendMessage(WebSocket webSocket, M messa } catch (Exception e) { log.error("Failed to send message", e); eventBus.post(new ReplicationEvent(ReplicationEventType.Error, e)); - closeConnection(webSocket, "Error - " + e.getMessage()); + closeConnection(webSocket, new CloseReason("Client Error - " + e.getMessage())); } } - public void closeConnection(WebSocket webSocket, String reason) { + public void closeConnection(WebSocket webSocket, CloseReason reason) { log.debug("Closing connection due to {}", reason); + + if (reason == CloseReason.ClientClose) { + Disconnect disconnect = messageFactory.createDisconnect(config, + replicatedCollection.getReplicaId(), UUID.randomUUID().toString()); + if (connectedWebsocket != null) { + messageTemplate.postMessage(connectedWebsocket, disconnect); + } + } + replicatedCollection.setConnected(false); + if (webSocket != null) { - webSocket.close(WebSocketCode.NORMAL_CLOSE, reason); + webSocket.close(WebSocketCode.NORMAL_CLOSE, reason.getReason()); } else { if (connectedWebsocket != null) { - connectedWebsocket.close(WebSocketCode.NORMAL_CLOSE, reason); + connectedWebsocket.close(WebSocketCode.NORMAL_CLOSE, reason.getReason()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java index e55e2f9a0..993899fab 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java @@ -18,6 +18,7 @@ package org.dizitart.no2.sync; import org.dizitart.no2.common.concurrent.ThreadPoolManager; +import org.dizitart.no2.sync.net.CloseReason; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -79,7 +80,7 @@ public boolean isConnected() { } public void close() { - replicatedCollection.stopReplication(null, "User close"); + replicatedCollection.stopReplication(null, CloseReason.ClientClose); ThreadPoolManager.shutdownThreadPool(scheduledExecutorService); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java index 83ac3c178..5b429d45a 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java @@ -32,6 +32,7 @@ import org.dizitart.no2.sync.handlers.ReceiptLedgerAware; import org.dizitart.no2.sync.message.OffsetAware; import org.dizitart.no2.sync.message.Receipt; +import org.dizitart.no2.sync.net.CloseReason; import org.dizitart.no2.sync.net.DataGateSocket; import java.util.HashSet; @@ -70,7 +71,7 @@ public void startReplication() { dataGateSocket.setListener(dataGateClient); } - public void stopReplication(WebSocket webSocket, String reason) { + public void stopReplication(WebSocket webSocket, CloseReason reason) { dataGateClient.closeConnection(webSocket, reason); reset(); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java index 9a74564d1..89399e61b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DisconnectHandler.java @@ -20,6 +20,7 @@ import okhttp3.WebSocket; import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.Disconnect; +import org.dizitart.no2.sync.net.CloseReason; /** * @author Anindya Chatterjee @@ -35,6 +36,6 @@ public DisconnectHandler(ReplicatedCollection replicatedCollection) { @Override public void handleMessage(WebSocket webSocket, Disconnect message) { log.debug("Disconnecting from server"); - replicatedCollection.stopReplication(webSocket, "Server disconnected"); + replicatedCollection.stopReplication(webSocket, CloseReason.ServerClose); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/CloseReason.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/CloseReason.java new file mode 100644 index 000000000..6911ba905 --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/CloseReason.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.sync.net; + +import lombok.Data; + +/** + * @author Anindya Chatterjee + */ +@Data +public class CloseReason { + public static final CloseReason ServerClose = new CloseReason("Server Disconnected"); + public static final CloseReason ClientClose = new CloseReason("User Disconnected"); + + private final String reason; + + public CloseReason(String reason) { + this.reason = reason; + } +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java index 9bb593628..41b2c5bb7 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java @@ -84,7 +84,7 @@ public static String getRandomTempDbFile() { @Before public void setUp() throws Exception { server = new MockDataGateServer(46005); - server.start(); +// server.start(); dbFile = getRandomTempDbFile(); executorService = Executors.newCachedThreadPool(); mockRepository = MockRepository.getInstance(); @@ -104,7 +104,7 @@ public void cleanUp() throws Exception { if (Files.exists(Paths.get(dbFile))) { Files.delete(Paths.get(dbFile)); } - server.stop(); +// server.stop(); } @Test From d5167ee79c6c1cf3a377bae71ca7b14c2caf305d Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 19 Aug 2021 17:08:14 +0530 Subject: [PATCH 18/78] enable test server --- .../src/test/java/org/dizitart/no2/mock/ReplicaTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java index 41b2c5bb7..9bb593628 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java @@ -84,7 +84,7 @@ public static String getRandomTempDbFile() { @Before public void setUp() throws Exception { server = new MockDataGateServer(46005); -// server.start(); + server.start(); dbFile = getRandomTempDbFile(); executorService = Executors.newCachedThreadPool(); mockRepository = MockRepository.getInstance(); @@ -104,7 +104,7 @@ public void cleanUp() throws Exception { if (Files.exists(Paths.get(dbFile))) { Files.delete(Paths.get(dbFile)); } -// server.stop(); + server.stop(); } @Test From 427f24c30138b0a17fe5cf20ed6c021d69ae3bbc Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 20 Aug 2021 23:38:15 +0530 Subject: [PATCH 19/78] update offset --- .../main/java/org/dizitart/no2/sync/BatchChangeSender.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java index 41a046b1c..44ddf9365 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java @@ -68,11 +68,11 @@ private void sendStartMessage(WebSocket webSocket, OffsetAware offsetAware) { replicatedCollection.getReplicaId(), offsetAware.getHeader().getTransactionId()); startMessage.setStartTime(lastSyncTime); startMessage.setEndTime(endTime); - startMessage.setNextOffset(offsetAware.getNextOffset() + config.getChunkSize()); + startMessage.setNextOffset(config.getChunkSize()); startMessage.setBatchSize(config.getChunkSize()); LastWriteWinState state = replicatedCollection.getChangesSince(lastSyncTime, endTime, - offsetAware.getNextOffset(), config.getChunkSize()); + 0, config.getChunkSize()); startMessage.setFeed(state); dataGateClient.sendMessage(webSocket, startMessage); From bdffa53aa5536eeeaea6dc9405150e4d8facefe6 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 24 Sep 2021 23:17:46 +0530 Subject: [PATCH 20/78] checkpoint 2 --- .../no2/mvstore/compat/v3/MigrationUtil.java | 2 +- nitrite-replication/build.gradle | 15 + .../dizitart/no2/sync/BatchChangeSender.java | 81 +- ...lient.java => DataGateSocketListener.java} | 22 +- .../org/dizitart/no2/sync/FeedLedger.java | 17 +- .../org/dizitart/no2/sync/MessageFactory.java | 6 +- .../java/org/dizitart/no2/sync/Replica.java | 14 +- .../no2/sync/ReplicatedCollection.java | 111 ++- .../crdt/ConflictFreeReplicatedDataType.java | 167 +++- ...astWriteWinState.java => DeltaStates.java} | 4 +- .../no2/sync/crdt/LastWriteWinMap.java | 145 +++- .../Timestamps.java} | 8 +- .../sync/event/CollectionChangeListener.java | 13 +- .../handlers/BatchChangeContinueHandler.java | 2 +- .../sync/handlers/BatchChangeEndHandler.java | 11 +- .../no2/sync/handlers/BatchEndAckHandler.java | 2 +- .../no2/sync/handlers/ConnectAckHandler.java | 2 +- .../sync/handlers/DataGateFeedHandler.java | 3 - .../no2/sync/handlers/ReceiptAckSender.java | 31 +- .../no2/sync/handlers/ReceiptLedgerAware.java | 26 +- .../dizitart/no2/sync/message/BatchAck.java | 4 +- .../no2/sync/message/BatchChangeContinue.java | 8 +- .../no2/sync/message/BatchChangeEnd.java | 4 +- .../no2/sync/message/BatchChangeStart.java | 8 +- .../no2/sync/message/BatchEndAck.java | 4 +- .../no2/sync/message/BatchMessage.java | 34 + .../dizitart/no2/sync/message/ConnectAck.java | 6 +- .../no2/sync/message/DataGateFeed.java | 4 +- .../no2/sync/message/ReceiptAware.java | 4 +- .../dizitart/no2/sync/net/CloseReason.java | 27 + ...ataGateSocket.java => DataGateClient.java} | 4 +- .../org/dizitart/no2/IntegrationTest.java} | 8 +- .../test/java/org/dizitart/no2/TestUtils.java | 37 +- .../integration/AnotherIntegrationTest.java | 681 ++++++++++++++++ .../integration/DataGateIntegrationTest.java | 751 ++++++++++++++---- .../org/dizitart/no2/integration/Retry.java | 59 ++ .../dizitart/no2/integration/UserClient.java | 153 ++++ .../no2/mock/ReplicaNegativeTest.java | 2 +- .../org/dizitart/no2/mock/ReplicaTest.java | 26 +- .../no2/mock/server/MockDataGateEndpoint.java | 18 +- .../mock/server/ServerLastWriteWinMap.java | 8 +- ...WinStateTest.java => DeltaStatesTest.java} | 4 +- .../src/test/resources/docker-compose.yml | 38 + .../src/test/resources/logback.xml | 11 +- .../src/test/resources/mongo-seed/Dockerfile | 5 + .../test/resources/mongo-seed/appConfig.json | 46 ++ .../org/dizitart/no2/rocksdb/EntrySet.java | 23 +- .../no2/collection/CollectionFactory.java | 2 +- .../org/dizitart/no2/collection/Document.java | 10 +- .../no2/collection/NitriteDocument.java | 12 +- .../dizitart/no2/collection/NitriteId.java | 6 +- .../no2/collection/meta/Attributes.java | 49 +- .../no2/collection/meta/MetadataAware.java | 4 +- .../collection/operation/IndexOperations.java | 2 +- .../dizitart/no2/index/IndexDescriptor.java | 2 +- .../org/dizitart/no2/store/NitriteMap.java | 8 +- 56 files changed, 2234 insertions(+), 520 deletions(-) rename nitrite-replication/src/main/java/org/dizitart/no2/sync/{DataGateClient.java => DataGateSocketListener.java} (93%) rename nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/{LastWriteWinState.java => DeltaStates.java} (94%) rename nitrite-replication/src/main/java/org/dizitart/no2/sync/{message/TimeBoundMessage.java => crdt/Timestamps.java} (83%) create mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java rename nitrite-replication/src/main/java/org/dizitart/no2/sync/net/{DataGateSocket.java => DataGateClient.java} (98%) rename nitrite-replication/src/{main/java/org/dizitart/no2/sync/message/OffsetAware.java => test/java/org/dizitart/no2/IntegrationTest.java} (74%) create mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/AnotherIntegrationTest.java create mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java create mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java rename nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/{LastWriteWinStateTest.java => DeltaStatesTest.java} (72%) create mode 100644 nitrite-replication/src/test/resources/docker-compose.yml create mode 100644 nitrite-replication/src/test/resources/mongo-seed/Dockerfile create mode 100644 nitrite-replication/src/test/resources/mongo-seed/appConfig.json diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java index 6c7119a21..85779f8f7 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java @@ -167,7 +167,7 @@ private static Attributes attributes(Compat.Attributes value) { Attributes attributes = new Attributes(); attributes.set(Attributes.CREATED_TIME, Long.toString(value.getCreatedTime())); attributes.set(Attributes.LAST_MODIFIED_TIME, Long.toString(value.getLastModifiedTime())); - attributes.set(Attributes.LAST_SYNCED, Long.toString(value.getLastSynced())); + attributes.set(Attributes.LOCAL_COLLECTION_SYNCED_TIME, Long.toString(value.getLastSynced())); attributes.set(Attributes.SYNC_LOCK, Long.toString(value.getSyncLock())); attributes.set(Attributes.EXPIRY_WAIT, Long.toString(value.getExpiryWait())); if (value.getCollection() != null) { diff --git a/nitrite-replication/build.gradle b/nitrite-replication/build.gradle index 260ce041e..cddfc843a 100644 --- a/nitrite-replication/build.gradle +++ b/nitrite-replication/build.gradle @@ -59,11 +59,26 @@ dependencies { testImplementation "ch.qos.logback:logback-core:1.2.4" testImplementation "ch.qos.logback:logback-classic:1.2.4" testImplementation "junit:junit:4.13.2" + testImplementation "org.testcontainers:testcontainers:1.16.0" + testImplementation 'org.testcontainers:mongodb:1.16.0' + testImplementation 'org.mongodb:mongo-java-driver:3.12.10' + testImplementation 'commons-lang:commons-lang:1.0.1' + testImplementation 'com.github.javafaker:javafaker:1.0.2' + testImplementation "com.palantir.docker.compose:docker-compose-rule-junit4:1.7.0" } test { testLogging.showStandardStreams = false testLogging.exceptionFormat = 'full' + useJUnit { + excludeCategories 'org.dizitart.no2.IntegrationTest' + } +} + +task integrationTest(type: Test) { + useJUnit { + includeCategories 'org.dizitart.no2.IntegrationTest' + } } jacocoTestReport { diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java index 44ddf9365..98effe2fd 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java @@ -18,7 +18,9 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.WebSocket; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; +import org.dizitart.no2.sync.crdt.DeltaStates; +import org.dizitart.no2.sync.crdt.Timestamps; import org.dizitart.no2.sync.message.*; /** @@ -28,62 +30,69 @@ public class BatchChangeSender { private final Config config; private final ReplicatedCollection replicatedCollection; - private final DataGateClient dataGateClient; + private final String replicaId; + private final ConflictFreeReplicatedDataType replicatedDataType; + private final DataGateSocketListener dataGateSocketListener; + private final FeedLedger feedLedger; private boolean hasMore; private State currentState; - private FeedLedger feedLedger; private MessageFactory messageFactory; - private Long lastSyncTime; - private Long endTime; + private Timestamps startTime; + private Timestamps endTime; public BatchChangeSender(Config config, ReplicatedCollection replicatedCollection, - DataGateClient dataGateClient) { + DataGateSocketListener dataGateSocketListener) { this.config = config; this.replicatedCollection = replicatedCollection; - this.dataGateClient = dataGateClient; + this.replicaId = replicatedCollection.getReplicaId(); + this.feedLedger = replicatedCollection.getFeedLedger(); + this.replicatedDataType = replicatedCollection.getReplicatedDataType(); + this.dataGateSocketListener = dataGateSocketListener; configure(); } - public void sendAndReceive(WebSocket webSocket, OffsetAware offsetAware) { + public void sendAndReceive(WebSocket webSocket, BatchMessage batchMessage) { + if (startTime == null) { + startTime = replicatedDataType.getLocalSyncedTime(); + } + if (endTime == null) { - endTime = offsetAware.getHeader().getTimestamp(); + endTime = replicatedDataType.getLastModifiedTime(); } switch (currentState) { case ReadyToSend: - sendStartMessage(webSocket, offsetAware); + sendStartMessage(webSocket, batchMessage); break; case StartSent: - sendChanges(webSocket, offsetAware); + sendChanges(webSocket, batchMessage); break; case ChangesSent: break; } } - private void sendStartMessage(WebSocket webSocket, OffsetAware offsetAware) { + private void sendStartMessage(WebSocket webSocket, BatchMessage batchMessage) { BatchChangeStart startMessage = messageFactory.createChangeStart(config, - replicatedCollection.getReplicaId(), offsetAware.getHeader().getTransactionId()); - startMessage.setStartTime(lastSyncTime); - startMessage.setEndTime(endTime); + replicaId, batchMessage.getHeader().getTransactionId()); startMessage.setNextOffset(config.getChunkSize()); startMessage.setBatchSize(config.getChunkSize()); - LastWriteWinState state = replicatedCollection.getChangesSince(lastSyncTime, endTime, + DeltaStates state = replicatedDataType.delta(startTime, endTime, 0, config.getChunkSize()); startMessage.setFeed(state); - dataGateClient.sendMessage(webSocket, startMessage); + dataGateSocketListener.sendMessage(webSocket, startMessage); feedLedger.writeEntry(startMessage.getFeed()); currentState = State.StartSent; } - private void sendChanges(WebSocket webSocket, OffsetAware offsetAware) { - LastWriteWinState state = replicatedCollection.getChangesSince(lastSyncTime, endTime, - offsetAware.getNextOffset(), config.getChunkSize()); + private void sendChanges(WebSocket webSocket, BatchMessage batchMessage) { + DeltaStates state = replicatedDataType.delta(startTime, endTime, + batchMessage.getNextOffset(), config.getChunkSize()); if (state.getChangeSet().size() == 0 && state.getTombstoneMap().size() == 0) { hasMore = false; @@ -91,27 +100,23 @@ private void sendChanges(WebSocket webSocket, OffsetAware offsetAware) { if (hasMore) { BatchChangeContinue message = messageFactory.createChangeContinue(config, - replicatedCollection.getReplicaId(), offsetAware.getHeader().getTransactionId(), state); - message.setStartTime(lastSyncTime); - message.setEndTime(endTime); - message.setNextOffset(offsetAware.getNextOffset() + config.getChunkSize()); + replicaId, batchMessage.getHeader().getTransactionId(), state); + message.setNextOffset(batchMessage.getNextOffset() + config.getChunkSize()); message.setBatchSize(config.getChunkSize()); - dataGateClient.sendMessage(webSocket, message); + dataGateSocketListener.sendMessage(webSocket, message); feedLedger.writeEntry(state); } else { - Receipt finalReceipt = replicatedCollection.getFeedLedger().getFinalReceipt(); + Receipt finalReceipt = feedLedger.getFinalReceipt(); if (replicatedCollection.shouldRetry(finalReceipt)) { state = replicatedCollection.createState(finalReceipt); BatchChangeContinue message = messageFactory.createChangeContinue(config, - replicatedCollection.getReplicaId(), offsetAware.getHeader().getTransactionId(), state); + replicaId, batchMessage.getHeader().getTransactionId(), state); - message.setStartTime(lastSyncTime); - message.setEndTime(endTime); - dataGateClient.sendMessage(webSocket, message); + dataGateSocketListener.sendMessage(webSocket, message); feedLedger.writeEntry(state); } else { - sendEndMessage(webSocket, offsetAware.getHeader().getTransactionId()); + sendEndMessage(webSocket, batchMessage.getHeader().getTransactionId()); currentState = State.ChangesSent; } } @@ -120,19 +125,21 @@ private void sendChanges(WebSocket webSocket, OffsetAware offsetAware) { private void sendEndMessage(WebSocket webSocket, String correlationId) { BatchChangeEnd endMessage = messageFactory.createChangeEnd(config, replicatedCollection.getReplicaId(), correlationId); - endMessage.setStartTime(lastSyncTime); - endMessage.setEndTime(endTime); + + Timestamps serverStartTime = replicatedDataType.getRemoteSyncedTime(); endMessage.setBatchSize(config.getChunkSize()); - dataGateClient.sendMessage(webSocket, endMessage); + endMessage.setStartTime(serverStartTime); + + // end time will be passed back in batch end ack message + endMessage.setEndTime(endTime); + + dataGateSocketListener.sendMessage(webSocket, endMessage); } private void configure() { - this.feedLedger = replicatedCollection.getFeedLedger(); this.messageFactory = new MessageFactory(); this.hasMore = true; this.currentState = State.ReadyToSend; - this.lastSyncTime = replicatedCollection.getLastSyncTime(); - this.endTime = null; } private enum State { diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateSocketListener.java similarity index 93% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateSocketListener.java index 448192dfc..495d5b5d7 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateClient.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateSocketListener.java @@ -43,7 +43,7 @@ * @author Anindya Chatterjee */ @Slf4j -public class DataGateClient extends WebSocketListener { +public class DataGateSocketListener extends WebSocketListener { private final Config config; private final ReplicatedCollection replicatedCollection; @@ -53,7 +53,7 @@ public class DataGateClient extends WebSocketListener { private MessageTransformer transformer; private WebSocket connectedWebsocket; - public DataGateClient(Config config, ReplicatedCollection replicatedCollection) { + public DataGateSocketListener(Config config, ReplicatedCollection replicatedCollection) { this.config = config; this.replicatedCollection = replicatedCollection; configure(); @@ -75,7 +75,7 @@ public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { } catch (Exception e) { log.error("Opening websocket failed", e); eventBus.post(new ReplicationEvent(ReplicationEventType.Error, e)); - closeConnection(webSocket, new CloseReason("Client Error - " + e.getMessage())); + closeConnection(webSocket, new CloseReason("Client Error", e)); } } @@ -92,7 +92,7 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { if (e instanceof ReplicationException) { if (((ReplicationException) e).isFatal()) { - closeConnection(webSocket, new CloseReason("Client Error - " + e.getMessage())); + closeConnection(webSocket, new CloseReason("Client Error", e)); } } } @@ -102,19 +102,19 @@ public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { log.error("Communication failure", t); eventBus.post(new ReplicationEvent(ReplicationEventType.Error, t)); - closeConnection(webSocket, new CloseReason("Client Error - " + t.getMessage())); + closeConnection(webSocket, new CloseReason("Client Error", t)); } @Override public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { log.warn("Connection to server is closed due to {}", reason); eventBus.post(new ReplicationEvent(Stopped)); - replicatedCollection.setConnected(false); + replicatedCollection.setStopped(true); } public void sendMessage(WebSocket webSocket, M message) { try { - if (replicatedCollection.isConnected()) { + if (!replicatedCollection.isStopped()) { messageTemplate.postMessage(webSocket, message); } else { throw new IllegalStateException("datagate client is not connected"); @@ -122,7 +122,7 @@ public void sendMessage(WebSocket webSocket, M messa } catch (Exception e) { log.error("Failed to send message", e); eventBus.post(new ReplicationEvent(ReplicationEventType.Error, e)); - closeConnection(webSocket, new CloseReason("Client Error - " + e.getMessage())); + closeConnection(webSocket, new CloseReason("Client Error", e)); } } @@ -137,13 +137,13 @@ public void closeConnection(WebSocket webSocket, CloseReason reason) { } } - replicatedCollection.setConnected(false); + replicatedCollection.setStopped(true); if (webSocket != null) { - webSocket.close(WebSocketCode.NORMAL_CLOSE, reason.getReason()); + webSocket.close(WebSocketCode.NORMAL_CLOSE, reason.getReasonMessage()); } else { if (connectedWebsocket != null) { - connectedWebsocket.close(WebSocketCode.NORMAL_CLOSE, reason.getReason()); + connectedWebsocket.close(WebSocketCode.NORMAL_CLOSE, reason.getReasonMessage()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java index 22936c949..d9a9aa9bd 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java @@ -21,8 +21,9 @@ import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.collection.meta.MetadataAware; import org.dizitart.no2.common.util.StringUtils; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.DeltaStates; import org.dizitart.no2.sync.message.Receipt; import java.util.HashSet; @@ -37,11 +38,11 @@ public class FeedLedger { private static final String JOURNAL = "no2_feed_ledger"; private final Config config; - private final ReplicatedCollection replicatedCollection; + private final MetadataAware metadataAware; - public FeedLedger(Config config, ReplicatedCollection replicatedCollection) { + public FeedLedger(Config config, MetadataAware metadataAware) { this.config = config; - this.replicatedCollection = replicatedCollection; + this.metadataAware = metadataAware; } public void writeOff(Receipt receipt) { @@ -58,7 +59,7 @@ public void writeOff(Receipt receipt) { setCurrent(current); } - public void writeEntry(LastWriteWinState state) { + public void writeEntry(DeltaStates state) { if (state != null) { Receipt receipt = getCurrent(); @@ -86,7 +87,7 @@ public Receipt getFinalReceipt() { private Receipt getCurrent() { try { - Attributes attributes = replicatedCollection.getAttributes(); + Attributes attributes = metadataAware.getAttributes(); String json = attributes.get(JOURNAL); if (StringUtils.isNullOrEmpty(json)) { return new Receipt(new HashSet<>(), new HashSet<>()); @@ -104,10 +105,10 @@ private void setCurrent(Receipt receipt) { try { ObjectMapper objectMapper = config.getObjectMapper(); String json = objectMapper.writeValueAsString(receipt); - Attributes attributes = replicatedCollection.getAttributes(); + Attributes attributes = metadataAware.getAttributes(); attributes.set(JOURNAL, json); - replicatedCollection.saveAttributes(attributes); + metadataAware.setAttributes(attributes); } catch (JsonProcessingException e) { log.error("Error while writing replica ledger", e); throw new ReplicationException("failed to write replica ledger", e, false); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java index 9e3ec7dd7..2180c6c5b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageFactory.java @@ -16,7 +16,7 @@ package org.dizitart.no2.sync; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.DeltaStates; import org.dizitart.no2.sync.message.*; import java.util.UUID; @@ -49,7 +49,7 @@ public BatchChangeStart createChangeStart(Config config, String replicaId, Strin } public BatchChangeContinue createChangeContinue(Config config, String replicaId, - String txId, LastWriteWinState state) { + String txId, DeltaStates state) { BatchChangeContinue message = new BatchChangeContinue(); message.setHeader(createHeader(MessageType.BatchChangeContinue, config.getCollection().getName(), txId, replicaId, config.getUserName(), config.getTenant())); @@ -68,7 +68,7 @@ public BatchChangeEnd createChangeEnd(Config config, String replicaId, } public DataGateFeed createFeedMessage(Config config, String replicaId, - String txId, LastWriteWinState state) { + String txId, DeltaStates state) { DataGateFeed feed = new DataGateFeed(); feed.setHeader(createHeader(MessageType.DataGateFeed, config.getCollection().getName(), txId, replicaId, config.getUserName(), config.getTenant())); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java index 993899fab..ca045d7da 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java @@ -23,6 +23,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import static org.dizitart.no2.common.Constants.SYNC_THREAD_NAME; @@ -36,6 +37,7 @@ public class Replica implements AutoCloseable { private final Config config; private final ReplicatedCollection replicatedCollection; + private AtomicBoolean disconnected; private ScheduledExecutorService scheduledExecutorService; private ScheduledFuture replicationTask; @@ -55,9 +57,11 @@ public void connect() { scheduledExecutorService = getSyncThreadPool(); } + disconnected.compareAndSet(true, false); + if (replicationTask == null || replicationTask.isCancelled()) { replicationTask = scheduledExecutorService.scheduleAtFixedRate(() -> { - if (!isConnected()) { + if (replicatedCollection.isStopped() && !disconnected.get()) { replicatedCollection.startReplication(); } }, 0, config.getPollingRate(), TimeUnit.MILLISECONDS); @@ -75,11 +79,12 @@ public void disconnectNow() { disconnectInternal(true); } - public boolean isConnected() { - return replicatedCollection.isConnected(); + public boolean isDisconnected() { + return disconnected.get() || replicatedCollection.isStopped(); } public void close() { + disconnected.compareAndSet(false, true); replicatedCollection.stopReplication(null, CloseReason.ClientClose); ThreadPoolManager.shutdownThreadPool(scheduledExecutorService); } @@ -88,7 +93,7 @@ private void disconnectInternal(boolean mayInterruptIfRunning) { if (scheduledExecutorService != null) { if (!scheduledExecutorService.isShutdown() && !scheduledExecutorService.isTerminated()) { replicationTask.cancel(mayInterruptIfRunning); - ThreadPoolManager.shutdownThreadPool(scheduledExecutorService); + disconnected.compareAndSet(false, true); } } else { throw new ReplicationException("replica is not configured properly", true); @@ -97,6 +102,7 @@ private void disconnectInternal(boolean mayInterruptIfRunning) { private void configure() { this.scheduledExecutorService = getSyncThreadPool(); + this.disconnected = new AtomicBoolean(true); } private static ScheduledExecutorService getSyncThreadPool() { diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java index 5b429d45a..1c704a324 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java @@ -21,22 +21,18 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.WebSocket; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.collection.meta.Attributes; -import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; import org.dizitart.no2.sync.crdt.LastWriteWinMap; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.Timestamps; import org.dizitart.no2.sync.event.CollectionChangeListener; import org.dizitart.no2.sync.handlers.ReceiptLedgerAware; -import org.dizitart.no2.sync.message.OffsetAware; +import org.dizitart.no2.sync.message.BatchMessage; import org.dizitart.no2.sync.message.Receipt; import org.dizitart.no2.sync.net.CloseReason; -import org.dizitart.no2.sync.net.DataGateSocket; +import org.dizitart.no2.sync.net.DataGateClient; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; @@ -46,78 +42,69 @@ * @author Anindya Chatterjee */ @Slf4j -public class ReplicatedCollection implements ConflictFreeReplicatedDataType, ReceiptLedgerAware { +public class ReplicatedCollection implements ReceiptLedgerAware { private String replicaId; - private AtomicBoolean connectedIndicator; - private CollectionChangeListener changeListener; + private AtomicBoolean stopped; @Getter private final Config config; + @Getter private final NitriteCollection collection; @Getter private FeedLedger feedLedger; - @Getter private NitriteCollection collection; - @Getter private DataGateClient dataGateClient; - @Getter private LastWriteWinMap lastWriteWinMap; + @Getter private DataGateSocketListener dataGateSocketListener; + @Getter private ConflictFreeReplicatedDataType replicatedDataType; @Getter private BatchChangeSender batchChangeSender; public ReplicatedCollection(Config config) { this.config = config; - configure(); + this.collection = config.getCollection(); + initialize(); } public void startReplication() { log.debug("Starting replication for {}", getReplicaId()); - DataGateSocket dataGateSocket = new DataGateSocket(config); - dataGateClient = new DataGateClient(config, this); - batchChangeSender = new BatchChangeSender(config, this, dataGateClient); - dataGateSocket.setListener(dataGateClient); + DataGateClient dataGateClient = new DataGateClient(config); + dataGateSocketListener = new DataGateSocketListener(config, this); + batchChangeSender = new BatchChangeSender(config, this, dataGateSocketListener); + dataGateClient.setListener(dataGateSocketListener); } public void stopReplication(WebSocket webSocket, CloseReason reason) { - dataGateClient.closeConnection(webSocket, reason); - reset(); + dataGateSocketListener.closeConnection(webSocket, reason); + setStopped(true); } - public LastWriteWinState getChangesSince(Long startTime, Long endTime, int start, Integer chunkSize) { - return lastWriteWinMap.getChangesSince(startTime, endTime, start, chunkSize); + public void sendAndReceive(WebSocket webSocket, BatchMessage batchMessage) { + batchChangeSender.sendAndReceive(webSocket, batchMessage); } - public void sendAndReceive(WebSocket webSocket, OffsetAware offsetAware) { - batchChangeSender.sendAndReceive(webSocket, offsetAware); - } + public void collectGarbage(Long dtl) { + if (dtl != null && dtl > 0) { + long collectTime = System.currentTimeMillis() - dtl * 24 * 60 * 60 * 1000; - public void collectGarbage(Long ttl) { - if (ttl != null && ttl > 0) { - long collectTime = System.currentTimeMillis() - ttl; - - if (lastWriteWinMap != null && lastWriteWinMap.getTombstoneMap() != null) { - Set removeSet = new HashSet<>(); - for (Pair entry : lastWriteWinMap.getTombstoneMap().entries()) { - if (entry.getSecond() < collectTime) { - removeSet.add(entry.getFirst()); - } - } - - Receipt garbage = new Receipt(); - for (NitriteId nitriteId : removeSet) { - lastWriteWinMap.getTombstoneMap().remove(nitriteId); - garbage.getRemoved().add(nitriteId.getIdValue()); - } - - feedLedger.writeOff(garbage); - } + Receipt garbage = replicatedDataType.collectGarbage(collectTime); + feedLedger.writeOff(garbage); } } - public void setConnected(boolean connected) { - this.connectedIndicator.set(connected); + public void setStopped(boolean stopped) { + if (this.stopped == null) { + this.stopped = new AtomicBoolean(); + } + this.stopped.set(stopped); } - public boolean isConnected() { - return connectedIndicator.get(); + public boolean isStopped() { + return stopped != null && stopped.get(); } public String getReplicaId() { if (StringUtils.isNullOrEmpty(replicaId)) { - Attributes attributes = getAttributes(); + Attributes attributes = collection.getAttributes(); + + if (attributes == null) { + attributes = new Attributes(); + collection.setAttributes(attributes); + } + if (!attributes.hasKey(Attributes.REPLICA)) { String name = StringUtils.isNullOrEmpty(config.getReplicaName()) ? UUID.randomUUID().toString() @@ -129,23 +116,19 @@ public String getReplicaId() { return replicaId; } - private void configure() { - connectedIndicator = new AtomicBoolean(false); - collection = config.getCollection(); - lastWriteWinMap = createConflictFreeReplicatedDataType(); - feedLedger = new FeedLedger(config, this); - changeListener = new CollectionChangeListener(lastWriteWinMap); - getCollection().subscribe(changeListener); + public void setLocalSyncedTime(Timestamps syncedTime) { + replicatedDataType.setLocalSyncedTime(syncedTime); } - private void reset() { - connectedIndicator = new AtomicBoolean(false); - collection = config.getCollection(); - lastWriteWinMap = createConflictFreeReplicatedDataType(); - feedLedger = new FeedLedger(config, this); + public void setRemoteSyncedTime(Timestamps syncedTime) { + replicatedDataType.setRemoteSyncedTime(syncedTime); + } - getCollection().unsubscribe(changeListener); - changeListener = new CollectionChangeListener(lastWriteWinMap); + private void initialize() { + stopped = new AtomicBoolean(true); + replicatedDataType = new LastWriteWinMap(collection); + feedLedger = new FeedLedger(config, collection); + CollectionChangeListener changeListener = new CollectionChangeListener(replicatedDataType); getCollection().subscribe(changeListener); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java index ecb81484e..52e147fab 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java @@ -17,71 +17,172 @@ package org.dizitart.no2.sync.crdt; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.SortOrder; +import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; +import org.dizitart.no2.sync.message.Receipt; -import static org.dizitart.no2.collection.meta.Attributes.LAST_SYNCED; -import static org.dizitart.no2.collection.meta.Attributes.TOMBSTONE; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import static org.dizitart.no2.collection.meta.Attributes.*; +import static org.dizitart.no2.common.Constants.DOC_MODIFIED; import static org.dizitart.no2.common.Constants.INTERNAL_NAME_SEPARATOR; /** * @author Anindya Chatterjee */ -public interface ConflictFreeReplicatedDataType { - NitriteCollection getCollection(); +public abstract class ConflictFreeReplicatedDataType implements AutoCloseable { + protected final NitriteCollection collection; + protected NitriteMap tombstoneMap; + + public abstract void createTombstone(NitriteId nitriteId, Long deleteTime); + public abstract void merge(DeltaStates deltaStates); + public abstract DeltaStates delta(Timestamps startMarker, Timestamps endMarker, + int offset, int size); + + protected ConflictFreeReplicatedDataType(NitriteCollection collection) { + this.collection = collection; + createTombstones(); + } + + public Long getTombstoneTime(NitriteId id) { + return tombstoneMap.get(id); + } - String getReplicaId(); + public Document getDocument(NitriteId id) { + return collection.getById(id); + } + + public Timestamps getLastModifiedTime() { + Timestamps lastModifiedTime = new Timestamps(); + + // get last updated document from DOC_MODIFIED index and get its modified time + Document latest = collection.find(FindOptions.orderBy(DOC_MODIFIED, SortOrder.Descending)).firstOrNull(); + lastModifiedTime.setCollectionTime(latest == null ? 0 : latest.getLastModifiedSinceEpoch()); + + Attributes attributes = getTombstoneAttributes(); + lastModifiedTime.setTombstoneTime(Long.parseLong(attributes.get(LAST_MODIFIED_TIME))); + + return lastModifiedTime; + } + + public Receipt collectGarbage(long collectTime) { + Set removeSet = new HashSet<>(); + for (Pair entry : tombstoneMap.entries()) { + if (entry.getSecond() < collectTime) { + removeSet.add(entry.getFirst()); + } + } + + Receipt garbage = new Receipt(); + for (NitriteId nitriteId : removeSet) { + tombstoneMap.remove(nitriteId); + garbage.getRemoved().add(nitriteId.getIdValue()); + } + + return garbage; + } + + @Override + public void close() { + // collection should not be closed as it may be used outside of replication + // but as tombstone is only used in replication, so close tombstone. + if (tombstoneMap != null) { + tombstoneMap.close(); + } + } + + public Timestamps getLocalSyncedTime() { + return getSyncedTime(LOCAL_COLLECTION_SYNCED_TIME, LOCAL_TOMBSTONE_SYNCED_TIME); + } + + public Timestamps getRemoteSyncedTime() { + return getSyncedTime(REMOTE_COLLECTION_SYNCED_TIME, REMOTE_TOMBSTONE_SYNCED_TIME); + } + + public void setLocalSyncedTime(Timestamps timestamps) { + Attributes collectionAttributes = getCollectionAttributes(); + collectionAttributes.set(LOCAL_COLLECTION_SYNCED_TIME, Objects.toString(timestamps.getCollectionTime())); + collection.setAttributes(collectionAttributes); + + Attributes tombstoneAttributes = getTombstoneAttributes(); + tombstoneAttributes.set(LOCAL_TOMBSTONE_SYNCED_TIME, Objects.toString(timestamps.getTombstoneTime())); + tombstoneMap.setAttributes(tombstoneAttributes); + } + + public void setRemoteSyncedTime(Timestamps timestamps) { + Attributes collectionAttributes = getCollectionAttributes(); + collectionAttributes.set(REMOTE_COLLECTION_SYNCED_TIME, Objects.toString(timestamps.getCollectionTime())); + collection.setAttributes(collectionAttributes); + + Attributes tombstoneAttributes = getTombstoneAttributes(); + tombstoneAttributes.set(REMOTE_TOMBSTONE_SYNCED_TIME, Objects.toString(timestamps.getTombstoneTime())); + tombstoneMap.setAttributes(tombstoneAttributes); + } + + public Attributes getCollectionAttributes() { + Attributes attributes = collection.getAttributes(); - default Attributes getAttributes() { - Attributes attributes = getCollection().getAttributes(); if (attributes == null) { attributes = new Attributes(); - saveAttributes(attributes); + collection.setAttributes(attributes); } return attributes; } - default Long getLastSyncTime() { - Attributes attributes = getAttributes(); - String syncTimeStr = attributes.get(LAST_SYNCED); - if (StringUtils.isNullOrEmpty(syncTimeStr)) { - return 0L; - } else { - return Long.parseLong(syncTimeStr); + public Attributes getTombstoneAttributes() { + Attributes attributes = tombstoneMap.getAttributes(); + + if (attributes == null) { + attributes = new Attributes(); + tombstoneMap.setAttributes(attributes); } + return attributes; } - default LastWriteWinMap createConflictFreeReplicatedDataType() { - Attributes attributes = getAttributes(); - String tombstoneName = getTombstoneName(attributes); - saveAttributes(attributes); - - NitriteStore store = getCollection().getStore(); - NitriteMap tombstone = store.openMap(tombstoneName, NitriteId.class, Long.class); - return new LastWriteWinMap(getCollection(), tombstone); + private void createTombstones() { + NitriteStore store = collection.getStore(); + Attributes collectionAttributes = getCollectionAttributes(); + String tombstoneName = getTombstoneName(collectionAttributes); + this.tombstoneMap = store.openMap(tombstoneName, NitriteId.class, Long.class); + collection.setAttributes(collectionAttributes); } - default String getTombstoneName(Attributes attributes) { + private String getTombstoneName(Attributes attributes) { String tombstoneName = attributes.get(TOMBSTONE); if (StringUtils.isNullOrEmpty(tombstoneName)) { - tombstoneName = getCollection().getName() - + INTERNAL_NAME_SEPARATOR + TOMBSTONE; + tombstoneName = collection.getName() + INTERNAL_NAME_SEPARATOR + TOMBSTONE; attributes.set(TOMBSTONE, tombstoneName); } return tombstoneName; } - default void saveLastSyncTime(Long lastSyncTime) { - Attributes attributes = getAttributes(); - attributes.set(LAST_SYNCED, Long.toString(lastSyncTime)); - saveAttributes(attributes); - } + private Timestamps getSyncedTime(String collectionKey, String tombstoneKey) { + Timestamps remoteSyncedTime = new Timestamps(); + + String remoteCollectionSyncedTime = getCollectionAttributes().get(collectionKey); + if (StringUtils.isNullOrEmpty(remoteCollectionSyncedTime)) { + remoteSyncedTime.setCollectionTime(0L); + } else { + remoteSyncedTime.setCollectionTime(Long.parseLong(remoteCollectionSyncedTime)); + } + + String remoteTombstoneSyncedTime = getTombstoneAttributes().get(tombstoneKey); + if (StringUtils.isNullOrEmpty(remoteTombstoneSyncedTime)) { + remoteSyncedTime.setTombstoneTime(0L); + } else { + remoteSyncedTime.setTombstoneTime(Long.parseLong(remoteTombstoneSyncedTime)); + } - default void saveAttributes(Attributes attributes) { - getCollection().setAttributes(attributes); + return remoteSyncedTime; } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinState.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/DeltaStates.java similarity index 94% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinState.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/DeltaStates.java index 8937f55f1..084dd1de9 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinState.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/DeltaStates.java @@ -30,12 +30,12 @@ * @author Anindya Chatterjee */ @Data -public class LastWriteWinState { +public class DeltaStates { @JsonDeserialize(contentUsing = DocumentDeserializer.class) private Set changeSet; private Map tombstoneMap; - public LastWriteWinState() { + public DeltaStates() { changeSet = new LinkedHashSet<>(); tombstoneMap = new LinkedHashMap<>(); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java index ff020b5fc..2c229bf93 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java @@ -16,75 +16,102 @@ package org.dizitart.no2.sync.crdt; -import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.collection.meta.Attributes; import org.dizitart.no2.common.streams.BoundedStream; import org.dizitart.no2.common.tuples.Pair; -import org.dizitart.no2.store.NitriteMap; +import org.dizitart.no2.common.util.Iterables; +import org.dizitart.no2.index.IndexType; -import java.util.Map; +import java.util.*; import static org.dizitart.no2.collection.FindOptions.skipBy; +import static org.dizitart.no2.collection.meta.Attributes.LAST_MODIFIED_TIME; import static org.dizitart.no2.common.Constants.*; import static org.dizitart.no2.filters.Filter.and; import static org.dizitart.no2.filters.FluentFilter.where; +import static org.dizitart.no2.index.IndexOptions.indexOptions; /** * @author Anindya Chatterjee. */ @Slf4j -@Data -public class LastWriteWinMap { - private NitriteCollection collection; - private NitriteMap tombstoneMap; - - public LastWriteWinMap(NitriteCollection collection, NitriteMap tombstoneMap) { - this.collection = collection; - this.tombstoneMap = tombstoneMap; +public class LastWriteWinMap extends ConflictFreeReplicatedDataType { + + public LastWriteWinMap(NitriteCollection collection) { + super(collection); + ensureIndices(); } - public void merge(LastWriteWinState snapshot) { - if (snapshot.getChangeSet() != null) { - for (Document entry : snapshot.getChangeSet()) { + @Override + public void createTombstone(NitriteId nitriteId, Long deleteTime) { + if (tombstoneMap != null) { + if (tombstoneMap.containsKey(nitriteId)) { + Long time = tombstoneMap.get(nitriteId); + if (deleteTime > time) { + writeTombstoneEntry(nitriteId, deleteTime); + } + } else { + writeTombstoneEntry(nitriteId, deleteTime); + } + } + } + + @Override + public void merge(DeltaStates deltaStates) { + if (deltaStates.getChangeSet() != null) { + for (Document entry : deltaStates.getChangeSet()) { put(entry); } } - if (snapshot.getTombstoneMap() != null) { - for (Map.Entry entry : snapshot.getTombstoneMap().entrySet()) { + if (deltaStates.getTombstoneMap() != null) { + for (Map.Entry entry : deltaStates.getTombstoneMap().entrySet()) { remove(NitriteId.createId(entry.getKey()), entry.getValue()); } } } - public LastWriteWinState getChangesSince(Long startTime, Long endTime, + @Override + public DeltaStates delta(Timestamps startMarker, Timestamps endMarker, int offset, int size) { + DeltaStates deltaStates = new DeltaStates(); + deltaStates.setChangeSet(getDocumentChanges(startMarker.getCollectionTime(), endMarker.getCollectionTime(), + offset, size)); + deltaStates.setTombstoneMap(getTombstoneChanges(startMarker.getTombstoneTime(), endMarker.getTombstoneTime(), + offset, size)); + + return deltaStates; + } + + private Set getDocumentChanges(Long startTime, Long endTime, int offset, int size) { - LastWriteWinState state = new LastWriteWinState(); + DocumentCursor cursor = collection.find( + and( + where(DOC_MODIFIED).gt(startTime), + where(DOC_MODIFIED).lte(endTime) + ), skipBy(offset).limit(size)); + + return cursor.toSet(); + } + private Map getTombstoneChanges(Long startTime, Long endTime, + int offset, int size) { BoundedStream stream = new BoundedStream<>((long) offset, (long) size, tombstoneMap.entries()); + Map tombstoneMap = new HashMap<>(); for (Pair entry : stream) { Long syncTimestamp = entry.getSecond(); if (syncTimestamp > startTime && syncTimestamp <= endTime) { - state.getTombstoneMap().put(entry.getFirst().getIdValue(), + tombstoneMap.put(entry.getFirst().getIdValue(), entry.getSecond()); } } - - DocumentCursor cursor = collection.find( - and( - where(DOC_MODIFIED).gt(startTime), - where(DOC_MODIFIED).lte(endTime) - ), skipBy(offset).limit(size)); - - state.getChangeSet().addAll(cursor.toSet()); - - return state; + return tombstoneMap; } private void put(Document value) { @@ -100,22 +127,37 @@ private void put(Document value) { if (docModifiedTime >= tombstoneTime) { value.put(DOC_SOURCE, REPLICATOR); collection.insert(value); - tombstoneMap.remove(key); + + destroyTombstone(key); } } else { value.put(DOC_SOURCE, REPLICATOR); collection.insert(value); } } else { - Long oldTime = entry.getLastModifiedSinceEpoch(); - Long newTime = value.getLastModifiedSinceEpoch(); + Integer entryRevision = entry.getRevision(); + Integer valueRevision = value.getRevision(); - if (newTime > oldTime) { + if (valueRevision > entryRevision) { + // if the document revision is higher update it entry.put(DOC_SOURCE, REPLICATOR); collection.remove(entry); value.put(DOC_SOURCE, REPLICATOR); collection.insert(value); + } else if (valueRevision.equals(entryRevision)) { + // in case for same revision number, check the last modified time + // if the new document is latest, update it + Long oldTime = entry.getLastModifiedSinceEpoch(); + Long newTime = value.getLastModifiedSinceEpoch(); + + if (newTime > oldTime) { + entry.put(DOC_SOURCE, REPLICATOR); + collection.remove(entry); + + value.put(DOC_SOURCE, REPLICATOR); + collection.insert(value); + } } } } @@ -127,7 +169,42 @@ private void remove(NitriteId key, long timestamp) { entry.put(DOC_SOURCE, REPLICATOR); collection.remove(entry); - tombstoneMap.put(key, timestamp); + createTombstone(key, timestamp); + } + } + + private void writeTombstoneEntry(NitriteId nitriteId, Long deleteTime) { + // if current deleted time is greater than previous deleted time, + // update the deleted time + tombstoneMap.put(nitriteId, deleteTime); + + // update last modified date in tombstone attributes + Attributes attributes = getTombstoneAttributes(); + long lastModifiedTime = Long.parseLong(attributes.get(LAST_MODIFIED_TIME)); + if (deleteTime > lastModifiedTime) { + // if deleted date is higher than the already saved last modified time + // then only update it, otherwise ignore + attributes.set(LAST_MODIFIED_TIME, Long.toString(deleteTime)); + tombstoneMap.setAttributes(attributes); + } + } + + private void destroyTombstone(NitriteId nitriteId) { + tombstoneMap.remove(nitriteId); + + // update last modified date in tombstone attributes + Attributes attributes = getTombstoneAttributes(); + List deleteTimes = Iterables.toList(tombstoneMap.values()); + Collections.sort(deleteTimes, Collections.reverseOrder()); + + long lastModifiedTime = deleteTimes.get(0); + attributes.set(LAST_MODIFIED_TIME, Long.toString(lastModifiedTime)); + tombstoneMap.setAttributes(attributes); + } + + private void ensureIndices() { + if (!collection.hasIndex(DOC_MODIFIED)) { + collection.createIndex(indexOptions(IndexType.NON_UNIQUE), DOC_MODIFIED); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/TimeBoundMessage.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Timestamps.java similarity index 83% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/message/TimeBoundMessage.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Timestamps.java index 26364208a..e23d9537b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/TimeBoundMessage.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Timestamps.java @@ -15,7 +15,7 @@ * */ -package org.dizitart.no2.sync.message; +package org.dizitart.no2.sync.crdt; import lombok.Data; @@ -23,7 +23,7 @@ * @author Anindya Chatterjee */ @Data -public abstract class TimeBoundMessage { - private Long startTime; - private Long endTime; +public class Timestamps { + private Long collectionTime; + private Long tombstoneTime; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/event/CollectionChangeListener.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/event/CollectionChangeListener.java index 66a567ad7..2cb8bfed3 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/event/CollectionChangeListener.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/event/CollectionChangeListener.java @@ -22,7 +22,7 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.collection.events.CollectionEventInfo; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.sync.crdt.LastWriteWinMap; +import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; import static org.dizitart.no2.common.Constants.REPLICATOR; @@ -31,16 +31,17 @@ */ @Slf4j public class CollectionChangeListener implements CollectionEventListener { - private final LastWriteWinMap lastWriteWinMap; + private final ConflictFreeReplicatedDataType replicatedDataType; - public CollectionChangeListener(LastWriteWinMap lastWriteWinMap) { - this.lastWriteWinMap = lastWriteWinMap; + public CollectionChangeListener(ConflictFreeReplicatedDataType replicatedDataType) { + this.replicatedDataType = replicatedDataType; } @Override public void onEvent(CollectionEventInfo eventInfo) { if (eventInfo != null) { if (!REPLICATOR.equals(eventInfo.getOriginator())) { + // discard the removes coming from replicator crdt switch (eventInfo.getEventType()) { case Remove: Document document = (Document) eventInfo.getItem(); @@ -60,8 +61,8 @@ private void handleRemoveEvent(Document document) { NitriteId nitriteId = document.getId(); Long deleteTime = document.getLastModifiedSinceEpoch(); - if (lastWriteWinMap != null) { - lastWriteWinMap.getTombstoneMap().put(nitriteId, deleteTime); + if (replicatedDataType != null) { + replicatedDataType.createTombstone(nitriteId, deleteTime); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java index 81c2fb5c6..2825bc829 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeContinueHandler.java @@ -43,6 +43,6 @@ public void handleMessage(WebSocket webSocket, BatchChangeContinue message) { public BatchAck createAck(String transactionId, Receipt receipt) { MessageFactory factory = new MessageFactory(); return factory.createBatchAck(replicatedCollection.getConfig(), - transactionId, replicatedCollection.getReplicaId(), receipt); + replicatedCollection.getReplicaId(), transactionId, receipt); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java index 1a4e8e9d7..b29c81576 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java @@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.WebSocket; import org.dizitart.no2.sync.MessageFactory; -import org.dizitart.no2.sync.DataGateClient; +import org.dizitart.no2.sync.DataGateSocketListener; import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.BatchChangeEnd; import org.dizitart.no2.sync.message.BatchEndAck; @@ -44,12 +44,9 @@ public void handleMessage(WebSocket webSocket, BatchChangeEnd message) { batchEndAck.setEndTime(message.getEndTime()); batchEndAck.getHeader().setCorrelationId(message.getHeader().getId()); - DataGateClient dataGateClient = replicatedCollection.getDataGateClient(); - dataGateClient.sendMessage(webSocket, batchEndAck); + DataGateSocketListener dataGateSocketListener = replicatedCollection.getDataGateSocketListener(); + dataGateSocketListener.sendMessage(webSocket, batchEndAck); - Long time = message.getEndTime(); - - log.debug("Saving last sync time {}", time); - replicatedCollection.saveLastSyncTime(time); + replicatedCollection.setRemoteSyncedTime(message.getEndTime()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java index a3faa456b..ff2d00fa1 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java @@ -34,6 +34,6 @@ public BatchEndAckHandler(ReplicatedCollection replicatedCollection) { @Override public void handleMessage(WebSocket webSocket, BatchEndAck message) { - // nothing to be done here + replicatedCollection.setLocalSyncedTime(message.getEndTime()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java index 0babe0482..57638bd14 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ConnectAckHandler.java @@ -35,7 +35,7 @@ public ConnectAckHandler(ReplicatedCollection replicatedCollection) { @Override public void handleMessage(WebSocket webSocket, ConnectAck message) { replicatedCollection.collectGarbage(message.getTombstoneTtl()); - replicatedCollection.setConnected(true); + replicatedCollection.setStopped(false); replicatedCollection.sendAndReceive(webSocket, message); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java index 531bdd22b..992cd2f44 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/DataGateFeedHandler.java @@ -37,9 +37,6 @@ public DataGateFeedHandler(ReplicatedCollection replicatedCollection) { @Override public void handleMessage(WebSocket webSocket, DataGateFeed message) { sendAck(webSocket, message); - - Long time = message.getHeader().getTimestamp(); - replicatedCollection.saveLastSyncTime(time); } @Override diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java index 3fb0e4b34..ee3adf8df 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java @@ -17,8 +17,8 @@ package org.dizitart.no2.sync.handlers; import okhttp3.WebSocket; -import org.dizitart.no2.sync.crdt.LastWriteWinState; -import org.dizitart.no2.sync.DataGateClient; +import org.dizitart.no2.sync.crdt.DeltaStates; +import org.dizitart.no2.sync.DataGateSocketListener; import org.dizitart.no2.sync.ReplicatedCollection; import org.dizitart.no2.sync.message.*; @@ -32,30 +32,27 @@ public interface ReceiptAckSender { default void sendAck(WebSocket webSocket, ReceiptAware message) { if (message != null) { - LastWriteWinState state = message.getFeed(); - getReplicatedCollection().getLastWriteWinMap().merge(state); + DeltaStates state = message.getFeed(); + getReplicatedCollection().getReplicatedDataType().merge(state); Receipt receipt = message.calculateReceipt(); Ack ack = createAck(message.getHeader().getTransactionId(), receipt); ack.getHeader().setCorrelationId(message.getHeader().getId()); + BatchMessage batchMessage = (BatchMessage) message; + BatchMessage batchAck = (BatchMessage) ack; + // set offset and batch size - if (message instanceof OffsetAware && ack instanceof OffsetAware) { - ((OffsetAware) ack).setNextOffset(((OffsetAware) message).getNextOffset()); - ((OffsetAware) ack).setBatchSize(((OffsetAware) message).getBatchSize()); - } + batchAck.setNextOffset(batchMessage.getNextOffset()); + batchAck.setBatchSize(batchMessage.getBatchSize()); // set start time and end time - if (ack instanceof TimeBoundMessage && message instanceof TimeBoundMessage) { - TimeBoundMessage timeBoundMessage = (TimeBoundMessage) message; - TimeBoundMessage timeBoundAck = (TimeBoundMessage) ack; - - timeBoundAck.setStartTime(timeBoundMessage.getStartTime()); - timeBoundAck.setEndTime(timeBoundMessage.getEndTime()); - } + batchAck.setStartTime(batchMessage.getStartTime()); + batchAck.setEndTime(batchMessage.getEndTime()); - DataGateClient dataGateClient = getReplicatedCollection().getDataGateClient(); - dataGateClient.sendMessage(webSocket, ack); + DataGateSocketListener dataGateSocketListener + = getReplicatedCollection().getDataGateSocketListener(); + dataGateSocketListener.sendMessage(webSocket, ack); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java index 0060147d6..195f5c957 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java @@ -17,12 +17,9 @@ package org.dizitart.no2.sync.handlers; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.sync.Config; -import org.dizitart.no2.sync.DataGateClient; -import org.dizitart.no2.sync.crdt.LastWriteWinMap; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; +import org.dizitart.no2.sync.crdt.DeltaStates; import org.dizitart.no2.sync.message.Receipt; import java.util.HashMap; @@ -32,27 +29,18 @@ * @author Anindya Chatterjee */ public interface ReceiptLedgerAware { - NitriteCollection getCollection(); - String getReplicaId(); + ConflictFreeReplicatedDataType getReplicatedDataType(); - LastWriteWinMap getLastWriteWinMap(); - - Config getConfig(); - - DataGateClient getDataGateClient(); - - default LastWriteWinState createState(Receipt receipt) { - LastWriteWinState state = new LastWriteWinState(); + default DeltaStates createState(Receipt receipt) { + DeltaStates state = new DeltaStates(); state.setTombstoneMap(new HashMap<>()); state.setChangeSet(new HashSet<>()); - NitriteCollection collection = getCollection(); - if (receipt != null) { if (receipt.getAdded() != null) { for (String id : receipt.getAdded()) { - Document document = collection.getById(NitriteId.createId(id)); + Document document = getReplicatedDataType().getDocument(NitriteId.createId(id)); if (document != null) { state.getChangeSet().add(document); } @@ -61,7 +49,7 @@ default LastWriteWinState createState(Receipt receipt) { if (receipt.getRemoved() != null) { for (String id : receipt.getRemoved()) { - Long timestamp = getLastWriteWinMap().getTombstoneMap().get(NitriteId.createId(id)); + Long timestamp = getReplicatedDataType().getTombstoneTime(NitriteId.createId(id)); if (timestamp != null) { state.getTombstoneMap().put(id, timestamp); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java index f6ba34da0..b32f9d067 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchAck.java @@ -24,9 +24,7 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchAck extends TimeBoundMessage implements OffsetAware { +public class BatchAck extends BatchMessage { private MessageHeader header; private Receipt receipt; - private Integer nextOffset; - private Integer batchSize; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java index 01bd33ff1..ab7af2ebf 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeContinue.java @@ -18,16 +18,14 @@ import lombok.Data; import lombok.EqualsAndHashCode; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.DeltaStates; /** * @author Anindya Chatterjee */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchChangeContinue extends TimeBoundMessage implements ReceiptAware, OffsetAware { +public class BatchChangeContinue extends BatchMessage implements ReceiptAware { private MessageHeader header; - private LastWriteWinState feed; - private Integer batchSize; - private Integer nextOffset; + private DeltaStates feed; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java index 3bd984980..a20f23ba1 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeEnd.java @@ -24,8 +24,6 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchChangeEnd extends TimeBoundMessage implements OffsetAware { +public class BatchChangeEnd extends BatchMessage { private MessageHeader header; - private Integer batchSize; - private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java index 972e1d021..41927a5b1 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchChangeStart.java @@ -19,7 +19,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.DeltaStates; /** * @author Anindya Chatterjee @@ -27,9 +27,7 @@ @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class BatchChangeStart extends TimeBoundMessage implements ReceiptAware, OffsetAware { +public class BatchChangeStart extends BatchMessage implements ReceiptAware { private MessageHeader header; - private LastWriteWinState feed; - private Integer batchSize; - private Integer nextOffset; + private DeltaStates feed; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java index b2559b7e7..575e7bb1c 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchEndAck.java @@ -24,8 +24,6 @@ */ @Data @EqualsAndHashCode(callSuper = true) -public class BatchEndAck extends TimeBoundMessage implements OffsetAware { +public class BatchEndAck extends BatchMessage { private MessageHeader header; - private Integer batchSize; - private Integer nextOffset; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java new file mode 100644 index 000000000..f95a8f4ce --- /dev/null +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.sync.message; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; +import org.dizitart.no2.sync.crdt.Timestamps; + +/** + * @author Anindya Chatterjee + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class BatchMessage implements DataGateMessage { + private Integer nextOffset; + private Integer batchSize; + private Timestamps startTime; + private Timestamps endTime; +} diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java index c9b0afccd..99f7f131e 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ConnectAck.java @@ -17,14 +17,14 @@ package org.dizitart.no2.sync.message; import lombok.Data; +import lombok.EqualsAndHashCode; /** * @author Anindya Chatterjee */ @Data -public class ConnectAck implements OffsetAware { +@EqualsAndHashCode(callSuper = true) +public class ConnectAck extends BatchMessage { private MessageHeader header; private Long tombstoneTtl; - private Integer nextOffset; - private Integer batchSize; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/DataGateFeed.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/DataGateFeed.java index 18e85ed07..053a3ea6b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/DataGateFeed.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/DataGateFeed.java @@ -17,7 +17,7 @@ package org.dizitart.no2.sync.message; import lombok.Data; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.DeltaStates; /** * @author Anindya Chatterjee. @@ -25,5 +25,5 @@ @Data public class DataGateFeed implements ReceiptAware { private MessageHeader header; - private LastWriteWinState feed; + private DeltaStates feed; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ReceiptAware.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ReceiptAware.java index aa67587c3..d65f70479 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ReceiptAware.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/ReceiptAware.java @@ -17,7 +17,7 @@ package org.dizitart.no2.sync.message; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.DeltaStates; import java.util.HashSet; import java.util.Map; @@ -27,7 +27,7 @@ * @author Anindya Chatterjee */ public interface ReceiptAware extends DataGateMessage { - LastWriteWinState getFeed(); + DeltaStates getFeed(); default Receipt calculateReceipt() { Set added = new HashSet<>(); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/CloseReason.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/CloseReason.java index 6911ba905..00e2b9ace 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/CloseReason.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/CloseReason.java @@ -18,6 +18,7 @@ package org.dizitart.no2.sync.net; import lombok.Data; +import org.dizitart.no2.sync.ReplicationException; /** * @author Anindya Chatterjee @@ -28,8 +29,34 @@ public class CloseReason { public static final CloseReason ClientClose = new CloseReason("User Disconnected"); private final String reason; + private final Throwable error; public CloseReason(String reason) { this.reason = reason; + this.error = null; + } + + public CloseReason(String reason, Throwable error) { + this.reason = reason; + this.error = error; + } + + public String getReasonMessage() { + Throwable cause = error; + while (true) { + if (cause == null) { + return reason; + } else if (cause instanceof ReplicationException) { + String message = cause.getMessage(); + while (true) { + if (message.length() > 123) { + message = message.substring(0, message.lastIndexOf(':')); + } else { + return message; + } + } + } + cause = error.getCause(); + } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateClient.java similarity index 98% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateClient.java index f9946a596..5cf40b7f4 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocket.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateClient.java @@ -34,13 +34,13 @@ * @author Anindya Chatterjee */ @Slf4j -public class DataGateSocket { +public class DataGateClient { private final Config config; private OkHttpClient httpClient; private Request request; - public DataGateSocket(Config config) { + public DataGateClient(Config config) { this.config = config; configure(config); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java b/nitrite-replication/src/test/java/org/dizitart/no2/IntegrationTest.java similarity index 74% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java rename to nitrite-replication/src/test/java/org/dizitart/no2/IntegrationTest.java index d95c6adde..05aea5389 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/OffsetAware.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/IntegrationTest.java @@ -15,14 +15,10 @@ * */ -package org.dizitart.no2.sync.message; +package org.dizitart.no2; /** * @author Anindya Chatterjee */ -public interface OffsetAware extends DataGateMessage { - Integer getNextOffset(); - void setNextOffset(Integer offset); - Integer getBatchSize(); - void setBatchSize(Integer batchSize); +public interface IntegrationTest { } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java b/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java index 377daa7e1..e48c446b9 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java @@ -17,24 +17,30 @@ package org.dizitart.no2; +import com.github.javafaker.Faker; +import lombok.SneakyThrows; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.collection.meta.Attributes; import org.dizitart.no2.common.Constants; import org.dizitart.no2.mvstore.MVStoreModule; +import org.dizitart.no2.store.NitriteMap; +import org.dizitart.no2.store.NitriteStore; import org.junit.Assert; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.List; -import java.util.Random; import java.util.stream.Collectors; /** * @author Anindya Chatterjee */ public class TestUtils { + private final static Faker faker = new Faker(); private TestUtils() {} - private static final Random random = new Random(); - public static Nitrite createDb() { MVStoreModule storeModule = MVStoreModule.withConfig() .build(); @@ -57,6 +63,11 @@ public static Nitrite createDb(String filePath) { .openOrCreate(); } + @SneakyThrows + public static void deleteDb(String filePath) { + Files.delete(Paths.get(filePath)); + } + public static void assertEquals(NitriteCollection c1, NitriteCollection c2) { List l1 = c1.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); List l2 = c2.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); @@ -80,20 +91,14 @@ public static Document trimMeta(Document document) { public static Document randomDocument() { return Document.createDocument() - .put("firstName", randomString()) - .put("lastName", randomString()) - .put("age", random.nextInt()); + .put("firstName", faker.name().firstName()) + .put("lastName", faker.name().lastName()) + .put("age", faker.random().nextLong()); } - private static String randomString() { - int leftLimit = 97; // letter 'a' - int rightLimit = 122; // letter 'z' - int targetStringLength = 10; - - return random.ints(leftLimit, rightLimit + 1) - .limit(targetStringLength) - .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) - .toString(); + public static NitriteMap getTombstone(NitriteCollection collection) { + NitriteStore nitriteStore = collection.getStore(); + String tombStoneName = collection.getAttributes().get(Attributes.TOMBSTONE); + return nitriteStore.openMap(tombStoneName, NitriteId.class, Long.class); } - } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/AnotherIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/AnotherIntegrationTest.java new file mode 100644 index 000000000..a6cd1c936 --- /dev/null +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/AnotherIntegrationTest.java @@ -0,0 +1,681 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.Nitrite; +import org.dizitart.no2.TestUtils; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.filters.Filter; +import org.dizitart.no2.sync.Replica; +import org.dizitart.no2.sync.event.ReplicationEvent; +import org.dizitart.no2.sync.event.ReplicationEventType; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MongoDBContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.utility.DockerImageName; + +import java.util.List; +import java.util.Random; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; +import static org.dizitart.no2.TestUtils.*; +import static org.dizitart.no2.integration.DataGateIntegrationTest.getRandomTempDbFile; +import static org.junit.Assert.assertEquals; + +/** + * @author Anindya Chatterjee + */ +@Slf4j +public class AnotherIntegrationTest { + private String dbFile1, dbFile2; + private Nitrite db1, db2; + + private final Network network = Network.newNetwork(); + private GenericContainer datagate; + private MongoDBContainer mongodb; + private GenericContainer mongoSeed; + + @Rule(order = 0) + public Retry retry = new Retry(1); + + @Before + public void setUp() throws Exception { + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log); + + mongodb = new MongoDBContainer( + DockerImageName.parse("mongo:latest")) + .withNetwork(network) + .withNetworkAliases("mongo") + .withExposedPorts(27017); + + mongoSeed = new GenericContainer<>(new ImageFromDockerfile() + .withFileFromClasspath("appConfig.json", "mongo-seed/appConfig.json") + .withFileFromClasspath("Dockerfile", "mongo-seed/Dockerfile")) + .withNetwork(network); + + mongodb.start(); + mongoSeed.start(); + + datagate = new GenericContainer<>( + DockerImageName.parse("nitrite/nitrite-datagate:latest")) + .withEnv("MONGO_URL", "mongodb://mongo:27017/datagate") + .withImagePullPolicy(imageName -> false) + .withNetwork(network) + .withLogConsumer(logConsumer) + .withExposedPorts(46005); + + datagate.start(); + + UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd@gmail.com"); + UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd2@gmail.com"); + UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd3@gmail.com"); + } + + @After + public void cleanUp() { + mongodb.stop(); + mongoSeed.stop(); + datagate.stop(); + + if (db1 != null && dbFile1 != null) { + db1.close(); + TestUtils.deleteDb(dbFile1); + } + + if (db2 != null && dbFile2 != null) { + db2.close(); + TestUtils.deleteDb(dbFile2); + } + } + + @Test + public void testSingleUserSingleReplica() throws Exception { + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + db1 = createDb(dbFile1); + NitriteCollection c1 = db1.getCollection("testSingleUserSingleReplica"); + + Replica replica = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("integration-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + + replica.connect(); + + Random random = new Random(); + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + await().atMost(5, SECONDS).until(() -> c1.size() == 10); + c1.remove(Filter.ALL); + await().atMost(5, SECONDS).until(() -> c1.size() == 0); + replica.disconnectNow(); + } + + @Test + public void testSingleUserMultiReplica() throws Exception { + dbFile1 = getRandomTempDbFile(); + dbFile2 = getRandomTempDbFile(); + + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + db1 = createDb(dbFile1); + db2 = createDb(dbFile2); + + NitriteCollection c1 = db1.getCollection("testSingleUserMultiReplica"); + NitriteCollection c2 = db2.getCollection("testSingleUserMultiReplica"); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("integration-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .acceptAllCertificates(true) + .create(); + + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("integration-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r2") + .acceptAllCertificates(true) + .create(); + + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + await().atMost(5, SECONDS).until(() -> c1.size() == 10); + assertEquals(c2.size(), 0); + + r2.connect(); + await().atMost(15, SECONDS).until(() -> c2.size() == 10); + + Random random = new Random(); + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + + await().atMost(10, SECONDS).until(() -> c1.size() == 40); + assertEquals(c2.size(), 40); + + r1.disconnect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + r1.connect(); + await().atMost(10, SECONDS).until(() -> c1.size() == 70 && c2.size() == 70); + TestUtils.assertEquals(c1, c2); + + c2.remove(Filter.ALL); + + await().atMost(10, SECONDS).until(() -> c2.size() == 0); + + await().atMost(30, SECONDS).until(() -> c1.size() == 0); + TestUtils.assertEquals(c1, c2); + + r1.disconnectNow(); + r2.disconnectNow(); + } + + @Test + public void testMultiUserSingleReplica() throws Exception { + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + String jwt1 = UserClient.getToken(host, port, "abcd@gmail.com"); + String jwt2 = UserClient.getToken(host, port, "abcd2@gmail.com"); + String jwt3 = UserClient.getToken(host, port, "abcd3@gmail.com"); + + Nitrite db1 = createDb(); + NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica"); + + Nitrite db2 = createDb(); + NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica"); + + Nitrite db3 = createDb(); + NitriteCollection c3 = db3.getCollection("testMultiUserSingleReplica"); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt1) + .create(); + r1.connect(); + + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd2@gmail.com", jwt2) + .create(); + r2.connect(); + + Replica r3 = Replica.builder() + .of(c3) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd3@gmail.com", jwt3) + .create(); + r3.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + } + + for (int i = 0; i < 30; i++) { + Document document = randomDocument(); + c3.insert(document); + } + + await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20 && c3.size() == 30); + + TestUtils.assertNotEquals(c1, c2); + TestUtils.assertNotEquals(c1, c3); + TestUtils.assertNotEquals(c2, c3); + + r1.disconnectNow(); + r2.disconnectNow(); + r3.disconnectNow(); + } + + @Test + public void testMultiUserMultiReplica() throws Exception { + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + String jwt1 = UserClient.getToken(host, port, "abcd@gmail.com"); + String jwt2 = UserClient.getToken(host, port, "abcd2@gmail.com"); + + Nitrite db1 = createDb(); + NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica1"); + + Nitrite db2 = createDb(); + NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica2"); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt1) + .create(); + r1.connect(); + + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd2@gmail.com", jwt2) + .create(); + r2.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + } + + await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20); + + TestUtils.assertNotEquals(c1, c2); + r1.disconnectNow(); + r2.disconnectNow(); + } + + @Test + public void testSecurityInCorrectCredentials() { + dbFile1 = getRandomTempDbFile(); + + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + Nitrite db1 = createDb(dbFile1); + NitriteCollection c1 = db1.getCollection("testSecurityInCorrectCredentials"); + + AtomicReference errorEvent = new AtomicReference<>(); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", "wrong_token") + .addReplicationEventListener(event -> { + if (event.getEventType() == ReplicationEventType.Error && errorEvent.get() == null) { + errorEvent.set(event); + } + }) + .create(); + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + assertEquals(c1.size(), 10); + + await().atMost(5, SECONDS).until(() -> { + ReplicationEvent replicationEvent = errorEvent.get(); + return replicationEvent.getError().getMessage().contains("failed to validate token"); + }); + r1.disconnectNow(); + } + + @Test + public void testCloseDbAndReconnect() throws Exception { + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + dbFile1 = getRandomTempDbFile(); + dbFile2 = getRandomTempDbFile(); + + db1 = createDb(dbFile1); + + db2 = createDb(dbFile2); + + NitriteCollection c1 = db1.getCollection("testCloseDbAndReconnect"); + NitriteCollection c2 = db2.getCollection("testCloseDbAndReconnect"); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .create(); + + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r2") + .create(); + + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + NitriteCollection finalC1 = c1; + await().atMost(5, SECONDS).until(() -> finalC1.size() == 10); + assertEquals(c2.size(), 0); + + r2.connect(); + await().atMost(5, SECONDS).until(() -> c2.size() == 10); + + Random random = new Random(); + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + NitriteCollection finalC2 = c1; + await().atMost(10, SECONDS).until(() -> finalC2.size() == 40); + assertEquals(c2.size(), 40); + + r1.disconnect(); + r1.close(); + db1.close(); + + db1 = createDb(dbFile1); + c1 = db1.getCollection("testCloseDbAndReconnect"); + r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .create(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + r1.connect(); + NitriteCollection finalC = c1; + await().atMost(10, SECONDS).until(() -> finalC.size() == 70 && c2.size() == 70); + TestUtils.assertEquals(c1, c2); + + log.info("Collection Size - " + finalC.find().size()); + log.info("Collection Attributes - " + finalC.getAttributes()); + log.info("Tombstone Size - " + getTombstone(finalC).entries().size()); + log.info("Tombstone Attributes - " + getTombstone(finalC).getAttributes()); + + c2.remove(Filter.ALL); + + try { + await().atMost(50, SECONDS).until(() -> finalC.size() == 0); + TestUtils.assertEquals(c1, c2); + } catch (Exception e) { + log.info("Test Failed. Current status as below"); + log.info("Collection content - " + finalC.find().toList()); + log.info("Collection Attributes - " + finalC.getAttributes()); + log.info("Tombstone content - " + getTombstone(finalC).entries().toList()); + log.info("Tombstone Attributes - " + getTombstone(finalC).getAttributes()); + throw e; + } finally { + r1.disconnectNow(); + r2.disconnectNow(); + } + } + + @Test + public void testDelayedConnect() throws Exception { + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + dbFile1 = getRandomTempDbFile(); + + Nitrite db1 = createDb(dbFile1); + + NitriteCollection c1 = db1.getCollection("testDelayedConnect"); + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + // allow it to reach the datagate server + Thread.sleep(5000); + + r1.disconnect(); + r1.close(); + db1.close(); + + Nitrite db2 = createDb(); + NitriteCollection c2 = db2.getCollection("testDelayedConnect"); + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + r2.connect(); + await().atMost(5, SECONDS).until(() -> c2.size() == 10); + + r1.disconnectNow(); + r2.disconnectNow(); + } + + @Test + public void testDelayedConnectRemoveAll() throws Exception { + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + dbFile1 = getRandomTempDbFile(); + + Nitrite db = createDb(dbFile1); + NitriteCollection c1 = db.getCollection("testDelayedConnect"); + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .create(); + + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + c1.remove(Filter.ALL); + assertEquals(c1.size(), 0); + + r1.disconnect(); + r1.close(); + db.close(); + + Nitrite db2 = createDb(); + NitriteCollection c2 = db2.getCollection("testDelayedConnect"); + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r2") + .create(); + + r2.connect(); + + for (int i = 0; i < 5; i++) { + Document document = randomDocument(); + c2.insert(document); + } + + db = createDb(dbFile1); + NitriteCollection c3 = db.getCollection("testDelayedConnect"); + r1 = Replica.builder() + .of(c3) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .create(); + + r1.connect(); + + await().atMost(5, SECONDS).until(() -> { + List l1 = c3.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); + List l2 = c2.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); + return l1.equals(l2); + }); + + r1.disconnectNow(); + r2.disconnectNow(); + } +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index a17af9283..ad7ad3e82 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -16,178 +16,659 @@ package org.dizitart.no2.integration; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.palantir.docker.compose.DockerComposeRule; +import com.palantir.docker.compose.configuration.ProjectName; +import com.palantir.docker.compose.configuration.ShutdownStrategy; +import com.palantir.docker.compose.connection.DockerPort; +import com.palantir.docker.compose.connection.waiting.HealthChecks; import lombok.extern.slf4j.Slf4j; -import okhttp3.*; +import org.dizitart.no2.IntegrationTest; import org.dizitart.no2.Nitrite; +import org.dizitart.no2.TestUtils; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.filters.Filter; import org.dizitart.no2.sync.Replica; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.dizitart.no2.collection.Document.createDocument; +import org.dizitart.no2.sync.ReplicationException; +import org.dizitart.no2.sync.event.ReplicationEvent; +import org.dizitart.no2.sync.event.ReplicationEventType; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; import static org.dizitart.no2.TestUtils.createDb; +import static org.dizitart.no2.TestUtils.randomDocument; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * @author Anindya Chatterjee */ @Slf4j +@Category(IntegrationTest.class) public class DataGateIntegrationTest { - // TODO: Use https://www.testcontainers.org/ and use datagate-container for integration test - - public static void main(String[] args) { - Path dbPath = null; - try { - createUser(); - dbPath = Files.createTempFile("no2-datagate-it", "db"); - - Nitrite db = createDb(dbPath.toFile().getPath()); - - NitriteCollection collection = db.getCollection("datagateIntegration"); - Document document = createDocument().put("firstName", "Anindya") - .put("lastName", "Chatterjee") - .put("address", createDocument("street", "1234 Abcd Street") - .put("pin", 123456)); - collection.insert(document); - - String jwt = getToken(); - - log.info("Token - " + jwt); - Replica replica = Replica.builder() - .of(collection) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .acceptAllCertificates(true) - .create(); - - replica.connect(); - Thread.sleep(10000); - } catch (Exception e) { - log.error("Exception during replication", e); - } finally { + private static final int DATAGATE_PORT = 46005; + private static final String MONGODB = "mongo"; + private static final String DATAGATE = "datagate"; + + private String dbFile1, dbFile2; + private Nitrite db1, db2; + + private DockerPort datagate; + + @Rule(order = 0) + public Retry retry = new Retry(3); + + @Rule(order = 1) + public DockerComposeRule dockerRule = DockerComposeRule.builder() + .file("src/test/resources/docker-compose.yml") + .projectName(ProjectName.random()) + .waitingForService(MONGODB, HealthChecks.toHaveAllPortsOpen()) + .waitingForService(DATAGATE, HealthChecks.toHaveAllPortsOpen()) + .shutdownStrategy(ShutdownStrategy.GRACEFUL) + .build(); + + public static String getRandomTempDbFile() { + String dataDir = System.getProperty("java.io.tmpdir") + File.separator + + "nitrite" + File.separator + "data"; + File file = new File(dataDir); + if (!file.exists()) { + assertTrue(file.mkdirs()); + } + return file.getPath() + File.separator + UUID.randomUUID() + ".db"; + } + + @Before + public void setUpContainer() throws Exception { + datagate = dockerRule.containers() + .container(DATAGATE) + .port(DATAGATE_PORT); + + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + UserClient.createUser(host, port, "abcd@gmail.com"); + UserClient.createUser(host, port, "abcd2@gmail.com"); + UserClient.createUser(host, port, "abcd3@gmail.com"); + } + + @After + public void after() throws IOException, InterruptedException { + dockerRule.dockerCompose().down(); + dockerRule.dockerCompose().kill(); + dockerRule.dockerCompose().rm(); + + if (db1 != null && dbFile1 != null) { + db1.close(); + TestUtils.deleteDb(dbFile1); + } + + if (db2 != null && dbFile2 != null) { + db2.close(); + TestUtils.deleteDb(dbFile2); + } + } + + @Test + public void testSingleUserSingleReplica() throws Exception { + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + db1 = createDb(dbFile1); + NitriteCollection c1 = db1.getCollection("testSingleUserSingleReplica"); + + Replica replica = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("integration-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + + replica.connect(); + + Random random = new Random(); + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); try { - if (dbPath != null) { - Files.delete(dbPath); - } - } catch (Exception e) { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { e.printStackTrace(); } } + + await().atMost(5, SECONDS).until(() -> c1.size() == 10); + c1.remove(Filter.ALL); + await().atMost(5, SECONDS).until(() -> c1.size() == 0); + replica.disconnectNow(); } - private static void createUser() throws Exception { - OkHttpClient client = getUnsafeOkHttpClient(); - Request request = new Request.Builder() - .url("https://127.0.0.1:3030/exists?email=abcd@gmail.com") - .build(); - - Call call = client.newCall(request); - Response response = call.execute(); - ObjectMapper mapper = new ObjectMapper(); - assert response.body() != null; - JsonNode jsonNode = mapper.readValue(response.body().string(), JsonNode.class); - if (jsonNode.has("exists")) { - if (jsonNode.get("exists").asBoolean()) { - return; + @Test + public void testSingleUserMultiReplica() throws Exception { + dbFile1 = getRandomTempDbFile(); + dbFile2 = getRandomTempDbFile(); + + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + db1 = createDb(dbFile1); + db2 = createDb(dbFile2); + + NitriteCollection c1 = db1.getCollection("testSingleUserMultiReplica"); + NitriteCollection c2 = db2.getCollection("testSingleUserMultiReplica"); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("integration-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .acceptAllCertificates(true) + .chunkSize(100) + .create(); + + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("integration-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r2") + .acceptAllCertificates(true) + .chunkSize(100) + .create(); + + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + await().atMost(5, SECONDS).until(() -> c1.size() == 10); + assertEquals(c2.size(), 0); + + r2.connect(); + await().atMost(15, SECONDS).until(() -> c2.size() == 10); + + Random random = new Random(); + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); } } - String json = "{" + - "\"email\":\"abcd@gmail.com\"," + - "\"password\":\"chang3me\"," + - "\"firstName\":\"Anindya\"," + - "\"lastName\":\"Chatterjee\"," + - "\"roles\": [\"admin\"]}"; - RequestBody body = RequestBody.create( - MediaType.parse("application/json"), json); - request = new Request.Builder() - .url("https://127.0.0.1:3030/register") - .post(body) - .build(); + await().atMost(10, SECONDS).until(() -> c1.size() == 40); + assertEquals(c2.size(), 40); - call = client.newCall(request); - response = call.execute(); + r1.disconnect(); - if (response.code() != 201) { - throw new Exception("user creation failed"); + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } } - } - private static String getToken() throws Exception { - OkHttpClient client = getUnsafeOkHttpClient(); - String json = "{" + - "\"email\":\"abcd@gmail.com\"," + - "\"password\":\"chang3me\"}"; - RequestBody body = RequestBody.create( - MediaType.parse("application/json"), json); - - Request request = new Request.Builder() - .url("https://127.0.0.1:3030/login") - .post(body) - .build(); - - Call call = client.newCall(request); - Response response = call.execute(); - - if (response.code() == 200) { - assert response.body() != null; - json = response.body().string(); - ObjectMapper mapper = new ObjectMapper(); - JsonNode jsonNode = mapper.readValue(json, JsonNode.class); - - if (jsonNode.has("token")) { - return jsonNode.get("token").asText(); + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); } } - throw new Exception("failed to login"); + r1.connect(); + await().atMost(10, SECONDS).until(() -> c1.size() == 70 && c2.size() == 70); + TestUtils.assertEquals(c1, c2); + + c2.remove(Filter.ALL); + + await().atMost(10, SECONDS).until(() -> c2.size() == 0); + + await().atMost(30, SECONDS).until(() -> c1.size() == 0); + TestUtils.assertEquals(c1, c2); + + r1.disconnectNow(); + r2.disconnectNow(); } - private static OkHttpClient getUnsafeOkHttpClient() { - try { - final TrustManager[] trustAllCerts = new TrustManager[]{ - new X509TrustManager() { - - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] chain, - String authType) { - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] chain, - String authType) { - } - - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[]{}; - } + @Test + public void testMultiUserSingleReplica() throws Exception { + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + String jwt1 = UserClient.getToken(host, port, "abcd@gmail.com"); + String jwt2 = UserClient.getToken(host, port, "abcd2@gmail.com"); + String jwt3 = UserClient.getToken(host, port, "abcd3@gmail.com"); + + Nitrite db1 = createDb(); + NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica"); + + Nitrite db2 = createDb(); + NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica"); + + Nitrite db3 = createDb(); + NitriteCollection c3 = db3.getCollection("testMultiUserSingleReplica"); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt1) + .create(); + r1.connect(); + + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd2@gmail.com", jwt2) + .create(); + r2.connect(); + + Replica r3 = Replica.builder() + .of(c3) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd3@gmail.com", jwt3) + .create(); + r3.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + } + + for (int i = 0; i < 30; i++) { + Document document = randomDocument(); + c3.insert(document); + } + + await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20 && c3.size() == 30); + + TestUtils.assertNotEquals(c1, c2); + TestUtils.assertNotEquals(c1, c3); + TestUtils.assertNotEquals(c2, c3); + + r1.disconnectNow(); + r2.disconnectNow(); + r3.disconnectNow(); + } + + @Test + public void testMultiUserMultiReplica() throws Exception { + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + String jwt1 = UserClient.getToken(host, port, "abcd@gmail.com"); + String jwt2 = UserClient.getToken(host, port, "abcd2@gmail.com"); + + Nitrite db1 = createDb(); + NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica1"); + + Nitrite db2 = createDb(); + NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica2"); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt1) + .create(); + r1.connect(); + + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd2@gmail.com", jwt2) + .create(); + r2.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + } + + await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20); + + TestUtils.assertNotEquals(c1, c2); + r1.disconnectNow(); + r2.disconnectNow(); + } + + @Test + public void testSecurityInCorrectCredentials() { + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + Nitrite db1 = createDb(); + NitriteCollection c1 = db1.getCollection("testSecurity"); + + AtomicReference errorEvent = new AtomicReference<>(); + + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", "wrong_token") + .addReplicationEventListener(event -> { + if (event.getEventType() == ReplicationEventType.Error && errorEvent.get() == null) { + errorEvent.set(event); } - }; + }) + .create(); + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + assertEquals(c1.size(), 10); + await().atMost(5, SECONDS).until(() -> { + ReplicationEvent replicationEvent = errorEvent.get(); + return replicationEvent.getError() instanceof ReplicationException && + replicationEvent.getError().getMessage().contains("failed to validate token"); + }); + r1.disconnectNow(); + } + + @Test + public void testCloseDbAndReconnect() throws Exception { + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); - final SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + dbFile1 = getRandomTempDbFile(); - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + Nitrite db = createDb(dbFile1); - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]); + Nitrite db2 = createDb(); - builder.hostnameVerifier((hostname, session) -> true); + NitriteCollection c1 = db.getCollection("testCloseDbAndReconnect"); + NitriteCollection c2 = db2.getCollection("testCloseDbAndReconnect"); - return builder.build(); - } catch (Exception e) { - throw new RuntimeException(e); + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); } + + NitriteCollection finalC1 = c1; + await().atMost(5, SECONDS).until(() -> finalC1.size() == 10); + assertEquals(c2.size(), 0); + + r2.connect(); + await().atMost(5, SECONDS).until(() -> c2.size() == 10); + + Random random = new Random(); + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + NitriteCollection finalC2 = c1; + await().atMost(10, SECONDS).until(() -> finalC2.size() == 40); + assertEquals(c2.size(), 40); + + r1.disconnect(); + r1.close(); + db.close(); + + db = createDb(dbFile1); + c1 = db.getCollection("testCloseDbAndReconnect"); + r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + for (int i = 0; i < 20; i++) { + Document document = randomDocument(); + c2.insert(document); + try { + Thread.sleep(random.nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + r1.connect(); + NitriteCollection finalC = c1; + await().atMost(10, SECONDS).until(() -> finalC.size() == 70 && c2.size() == 70); + TestUtils.assertEquals(c1, c2); + + c2.remove(Filter.ALL); + + await().atMost(10, SECONDS).until(() -> c2.size() == 0); + await().atMost(5, SECONDS).until(() -> finalC.size() == 0); + TestUtils.assertEquals(c1, c2); + + r1.disconnectNow(); + r2.disconnectNow(); + } + + @Test + public void testDelayedConnect() throws Exception { + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + dbFile1 = getRandomTempDbFile(); + + Nitrite db1 = createDb(dbFile1); + + NitriteCollection c1 = db1.getCollection("testDelayedConnect"); + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + await().atMost(5, SECONDS).until(() -> c1.size() == 10); + + r1.disconnect(); + r1.close(); + db1.close(); + + Nitrite db2 = createDb(); + NitriteCollection c2 = db2.getCollection("testDelayedConnect"); + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .create(); + r2.connect(); + await().atMost(5, SECONDS).until(() -> c2.size() == 10); + + r1.disconnectNow(); + r2.disconnectNow(); + } + + @Test + public void testDelayedConnectRemoveAll() throws Exception { + String host = datagate.getIp(); + Integer port = datagate.getExternalPort(); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + dbFile1 = getRandomTempDbFile(); + + Nitrite db = createDb(dbFile1); + NitriteCollection c1 = db.getCollection("testDelayedConnect"); + Replica r1 = Replica.builder() + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .create(); + + r1.connect(); + + for (int i = 0; i < 10; i++) { + Document document = randomDocument(); + c1.insert(document); + } + + c1.remove(Filter.ALL); + assertEquals(c1.size(), 0); + + r1.disconnect(); + r1.close(); + db.close(); + + Nitrite db2 = createDb(); + NitriteCollection c2 = db2.getCollection("testDelayedConnect"); + Replica r2 = Replica.builder() + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r2") + .create(); + + r2.connect(); + + for (int i = 0; i < 5; i++) { + Document document = randomDocument(); + c2.insert(document); + } + + db = createDb(dbFile1); + NitriteCollection c3 = db.getCollection("testDelayedConnect"); + r1 = Replica.builder() + .of(c3) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .create(); + + r1.connect(); + + await().atMost(5, SECONDS).until(() -> { + List l1 = c3.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); + List l2 = c2.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); + return l1.equals(l2); + }); + + r1.disconnectNow(); + r2.disconnectNow(); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java new file mode 100644 index 000000000..3d6a27d11 --- /dev/null +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * @author Anindya Chatterjee + */ +public class Retry implements TestRule { + private final int retryCount; + + public Retry(int retryCount) { + this.retryCount = retryCount; + } + + public Statement apply(Statement base, Description description) { + return statement(base, description); + } + + private Statement statement(final Statement base, final Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + Throwable caughtThrowable = null; + + // implement retry logic here + for (int i = 0; i < retryCount; i++) { + try { + base.evaluate(); + return; + } catch (Throwable t) { + caughtThrowable = t; + System.err.println(description.getDisplayName() + ": run " + (i + 1) + " failed"); + } + } + System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures"); + throw caughtThrowable; + } + }; + } +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java new file mode 100644 index 000000000..2bc2e1723 --- /dev/null +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.*; + +/** + * @author Anindya Chatterjee + */ +public class UserClient { + +// @Test + public void test() throws Exception { + createUser("127.0.0.1", 46005, "abcd@gmail.com"); + } + + public static void createUser(String host, Integer port, String user) throws Exception { + OkHttpClient client = getUnsafeOkHttpClient(); + Request request = new Request.Builder() + .url("http://" + host + ":" + port + "/exists?email=" + user) + .build(); + + Call call = client.newCall(request); + Response response; + try { + response = call.execute(); + ObjectMapper mapper = new ObjectMapper(); + assert response.body() != null; + JsonNode jsonNode = mapper.readValue(response.body().string(), JsonNode.class); + if (jsonNode.has("exists")) { + if (jsonNode.get("exists").asBoolean()) { + return; + } + } + } catch (Exception e) { + System.out.println("Error checking user " + user); + e.printStackTrace(); + return; + } + + String json = "{" + + "\"email\":\"" + user + "\"," + + "\"password\":\"chang3me\"," + + "\"firstName\":\"Anindya\"," + + "\"lastName\":\"Chatterjee\"," + + "\"roles\": [\"admin\"]" + + "}"; + + RequestBody body = RequestBody.create( + MediaType.parse("application/json"), json); + + request = new Request.Builder() + .url("http://" + host + ":" + port + "/register") + .post(body) + .build(); + + call = client.newCall(request); + response = call.execute(); + + if (response.code() != 201) { + throw new Exception("user creation failed"); + } + } + + public static String getToken(String host, Integer port, String user) throws Exception { + OkHttpClient client = getUnsafeOkHttpClient(); + String json = "{" + + "\"email\":\"" + user + "\"," + + "\"password\":\"chang3me\"}"; + + + RequestBody body = RequestBody.create( + MediaType.parse("application/json"), json); + + Request request = new Request.Builder() + .url("http://" + host + ":" + port + "/login") + .post(body) + .build(); + + Call call = client.newCall(request); + Response response = call.execute(); + + if (response.code() == 200) { + assert response.body() != null; + json = response.body().string(); + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readValue(json, JsonNode.class); + + if (jsonNode.has("token")) { + return jsonNode.get("token").asText(); + } + } + + System.err.println(response.body().string()); + throw new Exception("failed to login"); + } + + private static OkHttpClient getUnsafeOkHttpClient() { + try { +// final TrustManager[] trustAllCerts = new TrustManager[]{ +// new X509TrustManager() { +// +// @Override +// public void checkClientTrusted(java.security.cert.X509Certificate[] chain, +// String authType) { +// } +// +// @Override +// public void checkServerTrusted(java.security.cert.X509Certificate[] chain, +// String authType) { +// } +// +// @Override +// public java.security.cert.X509Certificate[] getAcceptedIssuers() { +// return new java.security.cert.X509Certificate[]{}; +// } +// } +// }; +// +// final SSLContext sslContext = SSLContext.getInstance("SSL"); +// sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); +// +// final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + OkHttpClient.Builder builder = new OkHttpClient.Builder(); +// builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]); + +// builder.hostnameVerifier((hostname, session) -> true); + builder.retryOnConnectionFailure(true); + + return builder.build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java index 605f69179..03fc1090c 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java @@ -103,7 +103,7 @@ public void testServerClose() { await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().find().size() == 10); server.stop(); - await().atMost(5, SECONDS).until(() -> !r1.isConnected()); + await().atMost(5, SECONDS).until(() -> r1.isDisconnected()); } @Test(expected = ReplicationException.class) diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java index 9bb593628..78fab5ea2 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java @@ -17,7 +17,6 @@ package org.dizitart.no2.mock; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.Nitrite; import org.dizitart.no2.Retry; @@ -29,15 +28,12 @@ import org.dizitart.no2.mock.server.MockRepository; import org.dizitart.no2.mock.server.ServerLastWriteWinMap; import org.dizitart.no2.sync.Replica; -import org.dizitart.no2.sync.ReplicatedCollection; -import org.dizitart.no2.sync.crdt.LastWriteWinMap; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import java.io.File; -import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; @@ -425,7 +421,7 @@ public void testSecurityInCorrectCredentials() { } assertEquals(c1.size(), 10); - await().atMost(5, SECONDS).until(() -> !r1.isConnected()); + await().atMost(5, SECONDS).until(() -> r1.isDisconnected()); r1.disconnectNow(); } @@ -686,9 +682,9 @@ public void testGarbageCollect() { c1.remove(Filter.ALL); assertEquals(c1.size(), 0); - final LastWriteWinMap lastWriteWinMap = getCrdt(r1); - await().atMost(5, SECONDS).until(() -> c1.size() == 0 - && lastWriteWinMap.getTombstoneMap().size() == 10); +// final ConflictFreeReplicatedDataType lastWriteWinMap = getCrdt(r1); +// await().atMost(5, SECONDS).until(() -> c1.size() == 0 +// && lastWriteWinMap.getTombstoneMap().size() == 10); r1.disconnect(); r1.close(); @@ -709,18 +705,10 @@ public void testGarbageCollect() { r1.connect(); - final LastWriteWinMap finalLastWriteWinMap = getCrdt(r1); - await().atMost(5, SECONDS).until(() -> c2.size() == 0 - && finalLastWriteWinMap.getTombstoneMap().size() == 0); +// final LastWriteWinMap finalLastWriteWinMap = getCrdt(r1); +// await().atMost(5, SECONDS).until(() -> c2.size() == 0 +// && finalLastWriteWinMap.getTombstoneMap().size() == 0); r1.disconnectNow(); } - - @SneakyThrows - private LastWriteWinMap getCrdt(Replica replica) { - Field field = Replica.class.getDeclaredField("replicatedCollection"); - field.setAccessible(true); - ReplicatedCollection replicatedCollection = (ReplicatedCollection) field.get(replica); - return replicatedCollection.getLastWriteWinMap(); - } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java index 220d6374d..e0ba2af47 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java @@ -29,7 +29,8 @@ import org.dizitart.no2.sync.MessageFactory; import org.dizitart.no2.sync.MessageTransformer; import org.dizitart.no2.sync.ReplicationException; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.DeltaStates; +import org.dizitart.no2.sync.crdt.Timestamps; import org.dizitart.no2.sync.crdt.Tombstone; import org.dizitart.no2.sync.message.*; @@ -204,7 +205,7 @@ protected void handleBatchChangeStart(Session session, BatchChangeStart batchCha String collection = userName + "@" + batchChangeStart.getHeader().getCollection(); String replicaId = batchChangeStart.getHeader().getOrigin(); ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - replica.merge(batchChangeStart.getFeed(), batchChangeStart.getEndTime()); + replica.merge(batchChangeStart.getFeed(), System.currentTimeMillis()); feed.setHeader(createHeader(session, MessageType.DataGateFeed, batchChangeStart.getHeader().getCollection(), userName, replicaId, batchChangeStart.getHeader().getTransactionId())); @@ -230,7 +231,7 @@ protected void handleBatchChangeContinue(Session session, BatchChangeContinue ba String collection = userName + "@" + batchChangeContinue.getHeader().getCollection(); String replicaId = batchChangeContinue.getHeader().getOrigin(); ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - replica.merge(batchChangeContinue.getFeed(), batchChangeContinue.getEndTime()); + replica.merge(batchChangeContinue.getFeed(), System.currentTimeMillis()); feed.setHeader(createHeader(session, MessageType.DataGateFeed, batchChangeContinue.getHeader().getCollection(), userName, replicaId, batchChangeContinue.getHeader().getTransactionId())); @@ -264,8 +265,11 @@ protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeE session.getBasicRemote().sendText(message); ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - LastWriteWinState changesSince = replica.getChangesSince(batchChangeEnd.getStartTime(), - batchChangeEnd.getEndTime(), 0, batchSize); + Timestamps startTime = batchChangeEnd.getStartTime(); +// Timestamps endTime = replica.getLastModifiedTime(); + + DeltaStates changesSince = replica.getChangesSince(null, + null, 0, batchSize); BatchChangeStart batchChangeStart = new BatchChangeStart(); @@ -293,8 +297,8 @@ protected void handleBatchAck(Session session, BatchAck batchAck) throws IOExcep } ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - LastWriteWinState changesSince = replica.getChangesSince(batchAck.getStartTime(), - batchAck.getEndTime(), offset, batchAck.getBatchSize()); + DeltaStates changesSince = replica.getChangesSince(null, + null, offset, batchAck.getBatchSize()); boolean hasMore = !(changesSince.getChangeSet().size() == 0 && changesSince.getTombstoneMap().size() == 0); if (hasMore) { diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java index 2d1ffec3e..1adc6cee4 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java @@ -26,7 +26,7 @@ import org.dizitart.no2.common.streams.BoundedStream; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.store.NitriteMap; -import org.dizitart.no2.sync.crdt.LastWriteWinState; +import org.dizitart.no2.sync.crdt.DeltaStates; import org.dizitart.no2.sync.crdt.Tombstone; import java.util.Map; @@ -52,7 +52,7 @@ public ServerLastWriteWinMap(NitriteCollection collection, NitriteMap stream diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/LastWriteWinStateTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/DeltaStatesTest.java similarity index 72% rename from nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/LastWriteWinStateTest.java rename to nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/DeltaStatesTest.java index 863b37dec..316074ed1 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/LastWriteWinStateTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/DeltaStatesTest.java @@ -4,10 +4,10 @@ import static org.junit.Assert.assertEquals; -public class LastWriteWinStateTest { +public class DeltaStatesTest { @Test public void testConstructor() { - assertEquals("LastWriteWinState(changeSet=[], tombstoneMap={})", (new LastWriteWinState()).toString()); + assertEquals("LastWriteWinState(changeSet=[], tombstoneMap={})", (new DeltaStates()).toString()); } } diff --git a/nitrite-replication/src/test/resources/docker-compose.yml b/nitrite-replication/src/test/resources/docker-compose.yml new file mode 100644 index 000000000..c8f3ce76e --- /dev/null +++ b/nitrite-replication/src/test/resources/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.3' + +services: + mongo: + image: 'mongo' + container_name: 'mongo' + restart: always + ports: + - '27017:27017' + + mongo-express: + image: 'mongo-express' + container_name: 'mongo-express' + depends_on: + - 'mongo' + ports: + - '8081:8081' + restart: always + + mongo-seed: + depends_on: + - 'mongo' + build: ./mongo-seed + container_name: 'mongo-seed' + + datagate: + depends_on: + - 'mongo-seed' + image: 'nitrite/nitrite-datagate:latest' + container_name: 'nitrite-datagate' + ports: + - '46005:46005' + restart: always + environment: + MONGO_URL: mongodb://mongo:27017/datagate + GO_ENV: production + SERVER_PORT: 46005 + SERVER_HOST: 0.0.0.0 \ No newline at end of file diff --git a/nitrite-replication/src/test/resources/logback.xml b/nitrite-replication/src/test/resources/logback.xml index 500a2f118..622f16c30 100644 --- a/nitrite-replication/src/test/resources/logback.xml +++ b/nitrite-replication/src/test/resources/logback.xml @@ -21,8 +21,17 @@ - + + tests.log + true + + %-4relative [%thread] %-5level %logger{35} - %msg%n + + + + + diff --git a/nitrite-replication/src/test/resources/mongo-seed/Dockerfile b/nitrite-replication/src/test/resources/mongo-seed/Dockerfile new file mode 100644 index 000000000..185131e7c --- /dev/null +++ b/nitrite-replication/src/test/resources/mongo-seed/Dockerfile @@ -0,0 +1,5 @@ +FROM mongo + +COPY appConfig.json /appConfig.json + +CMD mongoimport --host mongo --db datagate --collection appConfig --drop --type json --file /appConfig.json --jsonArray \ No newline at end of file diff --git a/nitrite-replication/src/test/resources/mongo-seed/appConfig.json b/nitrite-replication/src/test/resources/mongo-seed/appConfig.json new file mode 100644 index 000000000..a9fe0dc62 --- /dev/null +++ b/nitrite-replication/src/test/resources/mongo-seed/appConfig.json @@ -0,0 +1,46 @@ +[ + { + "_id": "appConfig", + "debug_message": true, + "tombstone_ttl": 180, + "alert_config": { + "log_format": "text", + "response_time_threshold": 20, + "sentry_dsn": "", + "enable_syslog": false, + "syslog_address": "", + "slack_channel": "", + "email_address": "", + "air_brake_id": -1, + "air_brake_key": "", + "hide_sensitive_data": true + }, + "auth_config": { + "init_user": "test", + "init_password": "test", + "internal_public_key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE3TlJVL2FNWnF6Sm8vdnEyREFwMgpyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDZk9FU1NEek5oZG1jTURnWUZ6MVpFd2loVUFDRDFKMzdXM0xRCkpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWZFNlBLY2pYSHU0NVBISTlRb1JDd1VzcDdGNE5PZElIWm12UjYKeFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUDdaL28wcXBGTmRDa2k3aDV6Vjd1TmlqMFFHbU5rN0hRa3FGMQpWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRUDhyT0hYQUFjYkt0THRpSzlOM2FVWVhkM2Y0VnBWR0FaSlExCms1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWthUHAzci9LNUVXNUg2VUVCcEs2elRibU1JYUFJcVdMUnZ3ZjIKN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yM3JCMCtCWW1ta2VPcUt1OVpEdlUyV292VnRGeFdVQmU1TXNqSApPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjWVgrR3A3Qmo5amtrTEpoRS9OTHZ0akFQOFpQR1lCWXhZTHhNCjNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG1yR1MxaDZSbEFITE1ISUtSenZiYVRPRFZFdGtzUDNSQVUvVzEKZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWE1RZWo2QnZlOGIzZWozaHozQ2ZydnUwNnNFcjJqREF0UXZSNApZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNMW1VODJhL09aYjRyZklISGFQTjR0TGJTZW1LZWJVZGliVzd0ClFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=", + "internal_private_key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBN05SVS9hTVpxekpvL3ZxMkRBcDJyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDCmZPRVNTRHpOaGRtY01EZ1lGejFaRXdpaFVBQ0QxSjM3VzNMUUpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWYKRTZQS2NqWEh1NDVQSEk5UW9SQ3dVc3A3RjROT2RJSFptdlI2eFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUAo3Wi9vMHFwRk5kQ2tpN2g1elY3dU5pajBRR21OazdIUWtxRjFWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRClA4ck9IWEFBY2JLdEx0aUs5TjNhVVlYZDNmNFZwVkdBWkpRMWs1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWsKYVBwM3IvSzVFVzVINlVFQnBLNnpUYm1NSWFBSXFXTFJ2d2YyN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yMwpyQjArQlltbWtlT3FLdTlaRHZVMldvdlZ0RnhXVUJlNU1zakhPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjCllYK0dwN0JqOWpra0xKaEUvTkx2dGpBUDhaUEdZQll4WUx4TTNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG0KckdTMWg2UmxBSExNSElLUnp2YmFUT0RWRXRrc1AzUkFVL1cxZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWApNUWVqNkJ2ZThiM2VqM2h6M0NmcnZ1MDZzRXIyakRBdFF2UjRZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNCjFtVTgyYS9PWmI0cmZJSEhhUE40dExiU2VtS2ViVWRpYlc3dFFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUEKQVFLQ0FnRUF4YnlYWmRxTE1ReDY3QUhxZyswc29TMkZYU29DUmJXT2wvVSt1T0cxaWdyZDdSbkdkRHY3NXNLVgpteDlSTUZWMWo5blNDSGxaTHBIdmdGT1RyZ3dUekVoaXRKRjZsWnVEWjJOQjBRa0xLaWNMNVdKR2IwQi9aSktRCnZJR2FmaThPMUJ3NkREWXhSaldGQ1BQdCsxb0xNN2Z2Q2pHYUNpNUtsSFJxZU1GTytBc3lEWHZMM2t4NHVZUWYKRzBxa1NHQnpta3lidWk3Qm1SSkllanVwK1BQRUlpcno0UklOdlcyelJjLzhOYlh3TXNvTjM4RWY5NU5lT3VmcwpCd0ozWkxpNmRLN1RmT08zbG9WeUQwRVlZV0g1eGRORmRuNTdvNEs5SGZibjBXQTV2ZGdJVUxCTUxiYUt2aVo3CjdSaldBK1FaK2lVL1lqTDgxSXBwRVB5SVFlYmxmVlRqQitQa3pKSWQ1R2dac0x6MlFka09xRnZWVE9pT0hNYlQKbDBIS3RmaFpod2pBRG1FeVdQaXIxOHNuVGs3SHoxbmh0eHV3ZGRlREZsZFZ2cU1xZlEyK25tTVBYRjdXbk9uWAppTGcyQlE0ZDFHS3c0WTBMR3FPdWl3ckFtOThlMEJLN2swdTN5WlVtRzRQRE91bzZFV2pyM0pCQ1p1YmViSVBvCkhhVnpBWllhTlZsbHN2dm9LOGp6eWN5aHo3cTBjK1lwRUJHLzNHTGFFbmdaSzVMUzRTZnhxU1FXc25aZjBmeE0KL0VBdWpEMUVjanZaTXJ6ejZ3SWxkd2I3Qkh6N3NBb1dXdmh1aUdZcEdWSzZSTUZhUDNmdXFvMkkwRVUrdEV5QQpOaW5mVDA3SkFGNnUwT1Zpc0dndVdiMHNGR2VKWis3UlVTc3RUbkYxQWh6dTgzdUM5N1VDZ2dFQkFQZzl4MXJqCjl5ZGhEaGFYWXQxNk9sQWU5NFR6eW41N2NzUlBlRlRSZjV4ZjF1MHVyQzJYZ2IvYnZ3MHI2SGc3bVBIRUV1NjMKU1ZxNUx2QzFEVHo5QzNJR1o3MkpyM1RWVTRsNlI2clVtNVBUYXExaE10M25WWlVLK2tUa0NWNE5wOVE3Vm0wMgpWT1IrSjZpM2JZOFU1bkZEemU1ZEk2QjRzWTFoeGQweW1wQW5Xc3dPSG5HMnFlclVEaHFHU2lJOFpzNmo0NTZJCmJZSkFkZ1BQRVZHNVY1V3owbnp2RG1YK3RQM05RRXF3NEE2MW9tQW9tVS8zODNQSVJ4MnJJSjRTT3JlUXNyd1QKdXBHTVJ0QWZ4bVV1aVJvRHI2eFNiczJ6SWRZcVgwcloxSnlBaXdqTUpNa2RBWWZmMGw1UUlwK1VVOTNYQ01nawpuSmFoc3JzRDdpcVluanNDZ2dFQkFQUTdQcVFTay9wY2ltdS9LSVhRSHhEN0N2Q2txY3I4UGpWTVo0NG5jMk5vCkZROEU2UzgxQmNFVEVDWmx4MVRyVkFVVmdFajhwTG9rTytHcUlzMGtHMHduQUNITVZLMS9nbE4vRCtSbm9zYXYKR1hmbnpnRGJzVVlNcDRCcDAwK1Uyc1ByWGFyOFU3ZWRZSnM2ZmhKcTAvbC85Y3BHcTByUHlncVZiWUd5KzBsaQpiR2NkMkxvTEhOdmdRS2dLZys1Zmg2WHFMU3lXWnJLcWQxVnhmSGVyZ01GUis4RXhodXNXRlpLTzFHZ1FPMzc2CjRsV0ZuUDBtMnJLMjlzSDJ4bWE4UVE0STFjRS95VEhBTDNOTUoyTCtTQjlHUy91ZGc5WGczdFI4dytZdW1EN3EKR09GeWdtTmZzR1NYYXpkRWxBTEFHRElENUEwd0UvRWdselNCanpxM3VsOENnZ0VBWTE2SGdMQ2tiTlVEQ0xRTQoxVTlxTEV4WkZKVnFSM3N2RTdva0Z2L05yMUVGL2VlaThKVW5VUitydUtBTTdLUWVzeGlqNDM3bkZEUHd3RllaCk9JS3FwRGhBS3JVRTBTWGJ6THB3R2NnRmh3VW9QTU1kMDRvWXpoS1k0QjdRU1IvNlFKQ0lKaXVMaS9PYitJT0UKamJQMkV2enJZREZVWTVZc3JNV29xTVRxN2kxeXdTQWR1N005RFUxWlgvREZtRExKaklvNlFXbW5QRzZGVHowQwpWODV6YXUrU29JUXBKVmJ5S0c2Uy85TVJ2Wkdqc0E1UVlKeUdqYUJzSjBvclFsdFZ1Y2xvWXJVYkI4dzVSSEtUCnZra0VoSzlaRVFmbVp0ei8vSFQxdEViQ1B1dU52RFhMdTkycWtUTmRTSGVYaEgyaG5MbkpRQ1Mzc2V5RVdTeFgKbUNHRHBRS0NBUUVBaTNyYVIzR2t1VExvaXFoZFNDNlh6MmJQMUtiMW9VdDFhNUw3QVNCZXNjTGJZL3gxLzlQVQpPWFBkb1ZBM0NyUnJBNHhIKzJidDNMQ2Mwa0FNS0FRYTR0N1RJSHBGVWVDa1dYTVRiR29UZUV5L3lzN0R3NUcwCktFRkoxL2lZQ2JjRlNTYStFOHlQTXluWjVrejlleDh2ZUNvd0FSbGk4aExCWEZJQ2ZEUHZkdldTMjBFY2FRTzMKczRyYTRoMC9RMytqUkluOHlwNEtnTGNCOS9ZY0Uyd0syRjB0M2lPZTNkdDY3bnhMcWpLN0I4WFlST2ROeFBYUApxSWo5VzhESGhoeTFPb0twTVBod3VzejdUR21OaE9lYjRPQ1F2RjQwMEl6Z05aSWJmdlhWVlBqMHhLeFU4dFBQCk5XT1VnN2ZTbjg5OUFmTmU1bmt5cWw3bWU4SVNQb0ozR1FLQ0FRQlBhOVZwM0pkOE9CZUlQelhsc2lPS2haSFIKYS94a1BqbnBVMkJvZzAzYllEcXdjWFRnOWtOWTc2U3FteXNaSFlkTDFCc2NLUFplZzc2SS9hbXhxYTZib29wWgpzTHAzOEQ0dk1NNk9ZeVhzaC9zUk82NDVuQjhIMGpzcGN4eXhySzF1MlBiK3M0MWZXTGVGNC9NWWdiMFFtbFFDCkYyZEJwVTkvZHJSQjhRNmd6NnFHeFQ5THk0SUxnazFhU05HMTRTTCtQWGg5amlPem92c05IY1FSbWI1eldJVkgKbURrdGdoMFh0S3NKVStBbEtrMHVoaUxZeGZNd3U3ZDhreENEdmhGYmRUQ3BIWjdNbzEzdW1udmNJRjllbnRoZApRc1BiYm5TK2JFVHZYckVWajA3R01rU0FJcjBkY0ZONldyTjRuTTVDcmpmMk1NQmJTSDFnR2J6QjZmTzEKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K", + "external_public_key": "", + "enable_jwt_provider": true, + "jwk_url": "", + "jwt_user_path": "email", + "issuer_id": "d515ef82-5579-44d0-9919-59d273dca7c0" + }, + "email_config": { + "smtp_host": "smtp.gmail.com", + "smtp_port": "587", + "auth_required": true, + "sender": "", + "password": "" + }, + "tls_config": { + "enable_ssl": false, + "server_key": "", + "server_cert": "" + }, + "websocket_config": { + "write_wait": 10, + "pong_wait": 60 + } + } +] \ No newline at end of file diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/EntrySet.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/EntrySet.java index 764952621..4b22bb002 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/EntrySet.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/EntrySet.java @@ -59,24 +59,13 @@ public boolean hasNext() { @SuppressWarnings("unchecked") public Pair next() { K key = (K) objectFormatter.decodeKey(rawEntryIterator.key(), keyType); - try { - V value = (V) objectFormatter.decode(rawEntryIterator.value(), valueType); - if (reverse) { - rawEntryIterator.prev(); - } else { - rawEntryIterator.next(); - } - return new Pair<>(key, value); - } catch (Exception e) { - System.out.println(new String(rawEntryIterator.value())); - throw e; + V value = (V) objectFormatter.decode(rawEntryIterator.value(), valueType); + if (reverse) { + rawEntryIterator.prev(); + } else { + rawEntryIterator.next(); } - } - - @Override - protected void finalize() throws Throwable { - rawEntryIterator.close(); - super.finalize(); + return new Pair<>(key, value); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java index 52a7c8edc..1eb51af12 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java @@ -56,7 +56,7 @@ public CollectionFactory(LockService lockService) { * * @param name the name * @param nitriteConfig the nitrite config - * @param writeCatalogue the write catalogue + * @param writeCatalogue to write catalogue * @return the collection */ public NitriteCollection getCollection(String name, NitriteConfig nitriteConfig, boolean writeCatalogue) { diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/Document.java b/nitrite/src/main/java/org/dizitart/no2/collection/Document.java index 871600e46..9b47640c3 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/Document.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/Document.java @@ -151,13 +151,21 @@ static Document createDocument(Map documentMap) { Document merge(Document update); /** - * Checks if a key exists in the document. + * Checks if a top level key exists in the document. * * @param key the key * @return the boolean */ boolean containsKey(String key); + /** + * Checks if a top level field or embedded field exists in the document. + * + * @param field the field + * @return the boolean + */ + boolean containsField(String field); + /** * Gets the document revision number. * diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java index 95e418649..dc2c1db44 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java @@ -176,10 +176,20 @@ public Document merge(Document document) { @Override public boolean containsKey(String key) { -// return getFields().contains(key); return super.containsKey(key); } + @Override + public boolean containsField(String field) { + if (containsKey(field)) { + // search top level + return true; + } else { + // search deep level + return getFields().contains(field); + } + } + @Override public boolean equals(Object other) { if (other == this) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java index 850505a56..2405785c8 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java @@ -28,10 +28,10 @@ import static org.dizitart.no2.common.Constants.ID_SUFFIX; /** - * An unique identifier across the Nitrite database. Each document in + * A unique identifier across the Nitrite database. Each document in * a nitrite collection is associated with a {@link NitriteId}. *

- * During insertion if an unique object is supplied in the '_id' field + * During insertion if a unique object is supplied in the '_id' field * of the document, then the value of the '_id' field will be used to * create a new {@link NitriteId}. If that is not supplied, then nitrite * will auto generate one and supply it in the '_id' field of the document. @@ -83,7 +83,7 @@ public static boolean validId(Object value) { Long.parseLong(value.toString()); return true; } catch (Exception e) { - throw new InvalidIdException("id must be a string representation of 64bit decimal number"); + throw new InvalidIdException("id must be a string representation of 64bit decimal number " + value); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java b/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java index 07562d701..875c36f08 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java @@ -16,9 +16,7 @@ package org.dizitart.no2.collection.meta; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; import java.io.IOException; import java.io.ObjectInputStream; @@ -33,47 +31,70 @@ * @author Anindya Chatterjee * @since 1.0 */ -@EqualsAndHashCode +@Data public class Attributes implements Serializable { + private static final long serialVersionUID = 1481284930L; + /** * The constant CREATED_TIME. */ - public static final String CREATED_TIME = "createdTime"; + public static final String CREATED_TIME = "created_at"; + /** * The constant LAST_MODIFIED_TIME. */ - public static final String LAST_MODIFIED_TIME = "lastModifiedTime"; + public static final String LAST_MODIFIED_TIME = "last_modified_at"; + /** * The constant OWNER. */ public static final String OWNER = "owner"; + /** * The constant UNIQUE_ID. */ public static final String UNIQUE_ID = "uuid"; - /** - * The constant LAST_SYNCED. - */ - public static final String LAST_SYNCED = "lastSynced"; + /** * The constant SYNC_LOCK. */ - public static final String SYNC_LOCK = "syncLock"; + public static final String SYNC_LOCK = "sync_lock"; + /** * The constant EXPIRY_WAIT. */ - public static final String EXPIRY_WAIT = "expiryWait"; + public static final String EXPIRY_WAIT = "expiry_wait"; + /** * The constant TOMBSTONE. */ public static final String TOMBSTONE = "tombstone"; + + /** + * The constant LAST_SYNCED. + */ + public static final String LOCAL_COLLECTION_SYNCED_TIME = "local_collection_synced_at"; + + /** + * The constant REMOTE_COLLECTION_SYNCED_TIME. + */ + public static final String REMOTE_COLLECTION_SYNCED_TIME = "remote_collection_synced_at"; + + /** + * The constant TOMBSTONE_SYNC_TIME. + */ + public static final String LOCAL_TOMBSTONE_SYNCED_TIME = "local_tombstone_synced_at"; + + /** + * The constant REMOTE_TOMBSTONE_SYNC_TIME. + */ + public static final String REMOTE_TOMBSTONE_SYNCED_TIME = "remote_tombstone_synced_at"; + /** * The constant REPLICA. */ public static final String REPLICA = "replica"; - private static final long serialVersionUID = 1481284930L; - @Getter @Setter private Map attributes; /** diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/meta/MetadataAware.java b/nitrite/src/main/java/org/dizitart/no2/collection/meta/MetadataAware.java index 1f185141a..cf9ef2545 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/meta/MetadataAware.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/meta/MetadataAware.java @@ -18,14 +18,14 @@ /** * Interface to be implemented by database objects that wish to be - * aware of their meta data. + * aware of their metadata. * * @author Anindya Chatterjee. * @since 1.0 */ public interface MetadataAware { /** - * Returns the meta data attributes of an object. + * Returns the metadata attributes of an object. * * @return the meta data attributes. */ diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java index 9d6dc9a45..59249ce8c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java @@ -179,7 +179,7 @@ private void buildIndexInternal(IndexDescriptor indexDescriptor, boolean rebuild } } finally { // remove dirty marker to denote indexing completed successfully - // if dirty marker is found in any index, it needs to be rebuild + // if dirty marker is found in any index, it needs to be rebuilt indexManager.endIndexing(fields); getBuildFlag(fields).set(false); alert(EventType.IndexEnd, fields); diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java index 1b97b6b7a..aad8506ee 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java @@ -91,7 +91,7 @@ public IndexDescriptor(String indexType, Fields fields, String collectionName) { public int compareTo(IndexDescriptor other) { if (other == null) return 1; - // compound index have highest cardinality + // compound index have the highest cardinality if (this.isCompoundIndex() && !other.isCompoundIndex()) return 1; // unique index has the next highest cardinality diff --git a/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java b/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java index 5711fd77f..d273804ba 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java @@ -95,8 +95,8 @@ public interface NitriteMap extends MetadataAware, AutoCloseable { void put(Key key, Value value); /** - * Get the number of entries, as a integer. Integer.MAX_VALUE is returned if - * there are more than this entries. + * Get the number of entries, as an integer. Integer.MAX_VALUE is returned if + * there are more than these entries. * * @return the number of entries, as an integer. */ @@ -112,7 +112,7 @@ public interface NitriteMap extends MetadataAware, AutoCloseable { Value putIfAbsent(Key key, Value value); /** - * Get the smallest key that is larger than the given key, or null if no + * Get the lest key that is greater than the given key, or null if no * such key exists. * * @param key the key @@ -121,7 +121,7 @@ public interface NitriteMap extends MetadataAware, AutoCloseable { Key higherKey(Key key); /** - * Get the smallest key that is larger or equal to this key. + * Get the least key that is greater than or equal to this key. * * @param key the key * @return the result. From f22318653b6dd3c01e7b420e4c8f8db906201f41 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 4 Oct 2021 00:45:29 +0530 Subject: [PATCH 21/78] datagate issue resolved --- .../no2/mvstore/compat/v3/MigrationUtil.java | 2 +- nitrite-replication/build.gradle | 9 - .../dizitart/no2/sync/BatchChangeSender.java | 26 +- .../java/org/dizitart/no2/sync/Config.java | 2 + .../org/dizitart/no2/sync/FeedLedger.java | 73 +- .../org/dizitart/no2/sync/ReplicaBuilder.java | 64 +- .../no2/sync/ReplicatedCollection.java | 15 +- .../crdt/ConflictFreeReplicatedDataType.java | 157 ++-- .../no2/sync/crdt/LastWriteWinMap.java | 103 +-- .../crdt/{Timestamps.java => Markers.java} | 6 +- .../sync/handlers/BatchChangeEndHandler.java | 4 +- .../no2/sync/handlers/BatchEndAckHandler.java | 2 +- .../no2/sync/handlers/ReceiptAckSender.java | 4 +- .../no2/sync/handlers/ReceiptLedgerAware.java | 1 + .../no2/sync/message/BatchMessage.java | 6 +- .../dizitart/no2/sync/message/Receipt.java | 14 + .../no2/sync/net/DataGateSocketListener.java | 53 -- .../test/java/org/dizitart/no2/TestUtils.java | 18 +- .../integration/AnotherIntegrationTest.java | 681 ----------------- .../integration/DataGateIntegrationTest.java | 352 ++++++--- .../org/dizitart/no2/integration/Retry.java | 59 -- .../dizitart/no2/integration/UserClient.java | 40 +- .../no2/mock/ReplicaNegativeTest.java | 180 ----- .../org/dizitart/no2/mock/ReplicaTest.java | 714 ------------------ .../no2/mock/server/MockDataGateEndpoint.java | 403 ---------- .../no2/mock/server/MockDataGateServer.java | 45 -- .../no2/mock/server/MockRepository.java | 64 -- .../mock/server/ServerLastWriteWinMap.java | 148 ---- .../no2/sync/crdt/DeltaStatesTest.java | 2 +- .../src/test/resources/logback.xml | 11 - .../no2/collection/meta/Attributes.java | 21 +- .../collection/operation/WriteOperations.java | 6 +- 32 files changed, 548 insertions(+), 2737 deletions(-) rename nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/{Timestamps.java => Markers.java} (88%) delete mode 100644 nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocketListener.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/AnotherIntegrationTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateServer.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java delete mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java index 85779f8f7..53300ab0b 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java @@ -167,7 +167,7 @@ private static Attributes attributes(Compat.Attributes value) { Attributes attributes = new Attributes(); attributes.set(Attributes.CREATED_TIME, Long.toString(value.getCreatedTime())); attributes.set(Attributes.LAST_MODIFIED_TIME, Long.toString(value.getLastModifiedTime())); - attributes.set(Attributes.LOCAL_COLLECTION_SYNCED_TIME, Long.toString(value.getLastSynced())); + attributes.set(Attributes.LOCAL_COLLECTION_MARKER, Long.toString(value.getLastSynced())); attributes.set(Attributes.SYNC_LOCK, Long.toString(value.getSyncLock())); attributes.set(Attributes.EXPIRY_WAIT, Long.toString(value.getExpiryWait())); if (value.getCollection() != null) { diff --git a/nitrite-replication/build.gradle b/nitrite-replication/build.gradle index cddfc843a..97d781f26 100644 --- a/nitrite-replication/build.gradle +++ b/nitrite-replication/build.gradle @@ -70,15 +70,6 @@ dependencies { test { testLogging.showStandardStreams = false testLogging.exceptionFormat = 'full' - useJUnit { - excludeCategories 'org.dizitart.no2.IntegrationTest' - } -} - -task integrationTest(type: Test) { - useJUnit { - includeCategories 'org.dizitart.no2.IntegrationTest' - } } jacocoTestReport { diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java index 98effe2fd..b64ede68f 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/BatchChangeSender.java @@ -20,7 +20,7 @@ import okhttp3.WebSocket; import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; import org.dizitart.no2.sync.crdt.DeltaStates; -import org.dizitart.no2.sync.crdt.Timestamps; +import org.dizitart.no2.sync.crdt.Markers; import org.dizitart.no2.sync.message.*; /** @@ -38,8 +38,8 @@ public class BatchChangeSender { private boolean hasMore; private State currentState; private MessageFactory messageFactory; - private Timestamps startTime; - private Timestamps endTime; + private Markers startMarkers; + private Markers endMarkers; public BatchChangeSender(Config config, ReplicatedCollection replicatedCollection, @@ -54,12 +54,12 @@ public BatchChangeSender(Config config, } public void sendAndReceive(WebSocket webSocket, BatchMessage batchMessage) { - if (startTime == null) { - startTime = replicatedDataType.getLocalSyncedTime(); + if (startMarkers == null) { + startMarkers = replicatedDataType.getLocalStartMarkers(); } - if (endTime == null) { - endTime = replicatedDataType.getLastModifiedTime(); + if (endMarkers == null) { + endMarkers = replicatedDataType.getLocalEndMarkers(); } switch (currentState) { @@ -80,7 +80,7 @@ private void sendStartMessage(WebSocket webSocket, BatchMessage batchMessage) { startMessage.setNextOffset(config.getChunkSize()); startMessage.setBatchSize(config.getChunkSize()); - DeltaStates state = replicatedDataType.delta(startTime, endTime, + DeltaStates state = replicatedDataType.delta(startMarkers, endMarkers, 0, config.getChunkSize()); startMessage.setFeed(state); @@ -91,7 +91,7 @@ private void sendStartMessage(WebSocket webSocket, BatchMessage batchMessage) { } private void sendChanges(WebSocket webSocket, BatchMessage batchMessage) { - DeltaStates state = replicatedDataType.delta(startTime, endTime, + DeltaStates state = replicatedDataType.delta(startMarkers, endMarkers, batchMessage.getNextOffset(), config.getChunkSize()); if (state.getChangeSet().size() == 0 && state.getTombstoneMap().size() == 0) { @@ -126,12 +126,12 @@ private void sendEndMessage(WebSocket webSocket, String correlationId) { BatchChangeEnd endMessage = messageFactory.createChangeEnd(config, replicatedCollection.getReplicaId(), correlationId); - Timestamps serverStartTime = replicatedDataType.getRemoteSyncedTime(); + Markers serverStartMarkers = replicatedDataType.getRemoteStartMarkers(); endMessage.setBatchSize(config.getChunkSize()); - endMessage.setStartTime(serverStartTime); + endMessage.setStartMarkers(serverStartMarkers); - // end time will be passed back in batch end ack message - endMessage.setEndTime(endTime); + // end markers will be passed back in batch end ack message + endMessage.setEndMarkers(endMarkers); dataGateSocketListener.sendMessage(webSocket, endMessage); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java index a85bc864e..76bfccf53 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Config.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import okhttp3.Request; +import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.sync.event.ReplicationEventListener; @@ -33,6 +34,7 @@ */ @Data public class Config { + private Nitrite db; private NitriteCollection collection; private Integer chunkSize; private String userName; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java index d9a9aa9bd..e913c7621 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java @@ -16,12 +16,11 @@ package org.dizitart.no2.sync; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.meta.Attributes; -import org.dizitart.no2.collection.meta.MetadataAware; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.sync.crdt.DeltaStates; import org.dizitart.no2.sync.message.Receipt; @@ -30,24 +29,25 @@ import java.util.Map; import java.util.Set; +import static org.dizitart.no2.collection.meta.Attributes.FEED_LEDGER; + /** * @author Anindya Chatterjee */ @Slf4j public class FeedLedger { - private static final String JOURNAL = "no2_feed_ledger"; private final Config config; - private final MetadataAware metadataAware; + private NitriteCollection journal; - public FeedLedger(Config config, MetadataAware metadataAware) { + public FeedLedger(Config config) { this.config = config; - this.metadataAware = metadataAware; + initializeLedger(); } public void writeOff(Receipt receipt) { Receipt current = getCurrent(); - if (receipt != null && current != null) { + if (receipt != null) { for (String id : receipt.getAdded()) { current.getAdded().remove(id); } @@ -85,33 +85,46 @@ public Receipt getFinalReceipt() { return getCurrent(); } - private Receipt getCurrent() { - try { - Attributes attributes = metadataAware.getAttributes(); - String json = attributes.get(JOURNAL); - if (StringUtils.isNullOrEmpty(json)) { - return new Receipt(new HashSet<>(), new HashSet<>()); - } + private void initializeLedger() { + NitriteCollection collection = config.getCollection(); + String feedLedgerName = getFeedLedgerName(collection); + + Nitrite db = config.getDb(); + this.journal = db.getCollection(feedLedgerName); + } - ObjectMapper objectMapper = config.getObjectMapper(); - return objectMapper.readValue(json, Receipt.class); - } catch (JsonProcessingException e) { - log.error("Error while opening replica ledger", e); - throw new ReplicationException("failed to open replica ledger", e, false); + private String getFeedLedgerName(NitriteCollection collection) { + Attributes attributes = collection.getAttributes(); + String feedLedgerName = attributes.get(FEED_LEDGER); + if (StringUtils.isNullOrEmpty(feedLedgerName)) { + feedLedgerName = collection.getName() + "_" + FEED_LEDGER; + attributes.set(FEED_LEDGER, feedLedgerName); + collection.setAttributes(attributes); + } + return feedLedgerName; + } + + private Receipt getCurrent() { + Document document = journal.find().firstOrNull(); + if (document == null) { + Receipt receipt = new Receipt(new HashSet<>(), new HashSet<>()); + document = receipt.toDocument(); + journal.insert(document); + return receipt; + } else { + return Receipt.fromDocument(document); } } private void setCurrent(Receipt receipt) { - try { - ObjectMapper objectMapper = config.getObjectMapper(); - String json = objectMapper.writeValueAsString(receipt); - Attributes attributes = metadataAware.getAttributes(); - attributes.set(JOURNAL, json); - - metadataAware.setAttributes(attributes); - } catch (JsonProcessingException e) { - log.error("Error while writing replica ledger", e); - throw new ReplicationException("failed to write replica ledger", e, false); + Document document = journal.find().firstOrNull(); + if (document == null) { + document = receipt.toDocument(); + journal.insert(document); + } else { + document.put("added", receipt.getAdded()); + document.put("removed", receipt.getRemoved()); + journal.update(document); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java index 2494fd7f5..6d58543cf 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import okhttp3.Request; +import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.sync.event.ReplicationEventListener; @@ -42,6 +43,7 @@ */ @Slf4j public class ReplicaBuilder { + private Nitrite db; private NitriteCollection collection; private String remoteHost; private Integer remotePort; @@ -70,6 +72,17 @@ public class ReplicaBuilder { eventListeners = new ArrayList<>(); } + /** + * Creates a replica from a {@link Nitrite} database. + * + * @param db the db + * @return the replica builder + */ + public ReplicaBuilder database(Nitrite db) { + this.db = db; + return this; + } + /** * Creates a replica of a {@link NitriteCollection}. * @@ -244,33 +257,30 @@ public ReplicaBuilder replicaName(String name) { * @return the replica */ public Replica create() { - if (collection != null) { - Request.Builder builder = createRequestBuilder(); - - Config config = new Config(); - config.setCollection(collection); - config.setChunkSize(chunkSize); - config.setUserName(userName); - config.setTenant(tenant); - config.setPollingRate(getTimeoutInMillis(pollingRate)); - config.setObjectMapper(objectMapper); - config.setTimeout(timeout); - config.setRequestBuilder(builder); - config.setProxy(proxy); - config.setAcceptAllCertificates(acceptAllCertificates); - config.setAuthToken(authToken); - config.setEventListeners(eventListeners); - config.setReplicaName(replicaName); - - ReplicatedCollection replicatedCollection = new ReplicatedCollection(config); - return new Replica(config, replicatedCollection); - } else { - throw new ReplicationException("no collection or repository has been specified for replication", true); - } + validateBuilder(); + Request.Builder builder = createRequestBuilder(); + + Config config = new Config(); + config.setDb(db); + config.setCollection(collection); + config.setChunkSize(chunkSize); + config.setUserName(userName); + config.setTenant(tenant); + config.setPollingRate(getTimeoutInMillis(pollingRate)); + config.setObjectMapper(objectMapper); + config.setTimeout(timeout); + config.setRequestBuilder(builder); + config.setProxy(proxy); + config.setAcceptAllCertificates(acceptAllCertificates); + config.setAuthToken(authToken); + config.setEventListeners(eventListeners); + config.setReplicaName(replicaName); + + ReplicatedCollection replicatedCollection = new ReplicatedCollection(config); + return new Replica(config, replicatedCollection); } private Request.Builder createRequestBuilder() { - validateBuilder(); String remoteUrl = String.format(Locale.getDefault(), "ws://%s:%d/ws/datagate/%s/%s/%s", remoteHost, remotePort, tenant, collection.getName(), userName); @@ -301,8 +311,12 @@ private void validateBuilder() { throw new ReplicationException("tenant id is a mandatory field"); } + if (db == null) { + throw new ReplicationException("database is a mandatory field"); + } + if (collection == null || isNullOrEmpty(collection.getName())) { - throw new ReplicationException("collection is a mandatory field"); + throw new ReplicationException("collection or repository is a mandatory field"); } if (isNullOrEmpty(userName)) { diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java index 1c704a324..cc0e6efd2 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java @@ -25,7 +25,7 @@ import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; import org.dizitart.no2.sync.crdt.LastWriteWinMap; -import org.dizitart.no2.sync.crdt.Timestamps; +import org.dizitart.no2.sync.crdt.Markers; import org.dizitart.no2.sync.event.CollectionChangeListener; import org.dizitart.no2.sync.handlers.ReceiptLedgerAware; import org.dizitart.no2.sync.message.BatchMessage; @@ -64,6 +64,7 @@ public void startReplication() { DataGateClient dataGateClient = new DataGateClient(config); dataGateSocketListener = new DataGateSocketListener(config, this); batchChangeSender = new BatchChangeSender(config, this, dataGateSocketListener); + replicatedDataType.resetCounter(); dataGateClient.setListener(dataGateSocketListener); } @@ -116,18 +117,18 @@ public String getReplicaId() { return replicaId; } - public void setLocalSyncedTime(Timestamps syncedTime) { - replicatedDataType.setLocalSyncedTime(syncedTime); + public void setLocalNextMarkers(Markers markers) { + replicatedDataType.setLocalNextMarkers(markers); } - public void setRemoteSyncedTime(Timestamps syncedTime) { - replicatedDataType.setRemoteSyncedTime(syncedTime); + public void setRemoteNextMarkers(Markers markers) { + replicatedDataType.setRemoteNextMarkers(markers); } private void initialize() { stopped = new AtomicBoolean(true); - replicatedDataType = new LastWriteWinMap(collection); - feedLedger = new FeedLedger(config, collection); + replicatedDataType = new LastWriteWinMap(config); + feedLedger = new FeedLedger(config); CollectionChangeListener changeListener = new CollectionChangeListener(replicatedDataType); getCollection().subscribe(changeListener); } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java index 52e147fab..36b37695b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java @@ -17,75 +17,94 @@ package org.dizitart.no2.sync.crdt; +import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.collection.meta.Attributes; import org.dizitart.no2.common.SortOrder; -import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.common.util.StringUtils; -import org.dizitart.no2.store.NitriteMap; -import org.dizitart.no2.store.NitriteStore; +import org.dizitart.no2.filters.Filter; +import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.sync.Config; import org.dizitart.no2.sync.message.Receipt; import java.util.HashSet; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import static org.dizitart.no2.collection.FindOptions.orderBy; import static org.dizitart.no2.collection.meta.Attributes.*; import static org.dizitart.no2.common.Constants.DOC_MODIFIED; -import static org.dizitart.no2.common.Constants.INTERNAL_NAME_SEPARATOR; +import static org.dizitart.no2.filters.FluentFilter.where; +import static org.dizitart.no2.index.IndexOptions.indexOptions; /** * @author Anindya Chatterjee */ public abstract class ConflictFreeReplicatedDataType implements AutoCloseable { - protected final NitriteCollection collection; - protected NitriteMap tombstoneMap; + protected static final String TOMBSTONE_COUNTER = "tombstone_counter"; + protected static final String DELETED_TIME = "deleted_time"; + protected static final String TOMBSTONE_SOURCE = "tombstone_source"; + + protected final Config config; + protected NitriteCollection collection; + protected NitriteCollection tombstones; + protected AtomicInteger counter; public abstract void createTombstone(NitriteId nitriteId, Long deleteTime); public abstract void merge(DeltaStates deltaStates); - public abstract DeltaStates delta(Timestamps startMarker, Timestamps endMarker, + public abstract DeltaStates delta(Markers startMarker, Markers endMarker, int offset, int size); - protected ConflictFreeReplicatedDataType(NitriteCollection collection) { - this.collection = collection; - createTombstones(); + protected ConflictFreeReplicatedDataType(Config config) { + this.config = config; + initializeDataType(); + } + + public void resetCounter() { + this.counter = new AtomicInteger(0); } public Long getTombstoneTime(NitriteId id) { - return tombstoneMap.get(id); + Document tombstone = tombstones.getById(id); + if (tombstone == null) return 0L; + return tombstone.get(DELETED_TIME, Long.class); } public Document getDocument(NitriteId id) { return collection.getById(id); } - public Timestamps getLastModifiedTime() { - Timestamps lastModifiedTime = new Timestamps(); + public Markers getLocalEndMarkers() { + Markers markers = new Markers(); // get last updated document from DOC_MODIFIED index and get its modified time - Document latest = collection.find(FindOptions.orderBy(DOC_MODIFIED, SortOrder.Descending)).firstOrNull(); - lastModifiedTime.setCollectionTime(latest == null ? 0 : latest.getLastModifiedSinceEpoch()); + Document latest = collection.find(orderBy(DOC_MODIFIED, SortOrder.Descending)).firstOrNull(); + markers.setCollectionMarker(latest == null ? 0 : latest.getLastModifiedSinceEpoch()); + + // get the highest tombstone counter for the current collection + Document latestTombstone = tombstones.find( + where(TOMBSTONE_SOURCE).eq(collection.getName()), + orderBy(TOMBSTONE_COUNTER, SortOrder.Descending)).firstOrNull(); - Attributes attributes = getTombstoneAttributes(); - lastModifiedTime.setTombstoneTime(Long.parseLong(attributes.get(LAST_MODIFIED_TIME))); + markers.setTombstoneMarker(latestTombstone == null ? 0L : latestTombstone.get(TOMBSTONE_COUNTER, Long.class)); - return lastModifiedTime; + return markers; } public Receipt collectGarbage(long collectTime) { Set removeSet = new HashSet<>(); - for (Pair entry : tombstoneMap.entries()) { - if (entry.getSecond() < collectTime) { - removeSet.add(entry.getFirst()); + for (Document entry : tombstones.find()) { + if (entry.get(TOMBSTONE_COUNTER, Long.class) < collectTime) { + removeSet.add(entry.getId()); } } Receipt garbage = new Receipt(); for (NitriteId nitriteId : removeSet) { - tombstoneMap.remove(nitriteId); + tombstones.remove(Filter.byId(nitriteId)); garbage.getRemoved().add(nitriteId.getIdValue()); } @@ -96,37 +115,25 @@ public Receipt collectGarbage(long collectTime) { public void close() { // collection should not be closed as it may be used outside of replication // but as tombstone is only used in replication, so close tombstone. - if (tombstoneMap != null) { - tombstoneMap.close(); + if (tombstones != null) { + tombstones.close(); } } - public Timestamps getLocalSyncedTime() { - return getSyncedTime(LOCAL_COLLECTION_SYNCED_TIME, LOCAL_TOMBSTONE_SYNCED_TIME); + public Markers getLocalStartMarkers() { + return getMarkers(LOCAL_COLLECTION_MARKER, LOCAL_TOMBSTONE_MARKER); } - public Timestamps getRemoteSyncedTime() { - return getSyncedTime(REMOTE_COLLECTION_SYNCED_TIME, REMOTE_TOMBSTONE_SYNCED_TIME); + public void setLocalNextMarkers(Markers markers) { + setMarkers(LOCAL_COLLECTION_MARKER, LOCAL_TOMBSTONE_MARKER, markers); } - public void setLocalSyncedTime(Timestamps timestamps) { - Attributes collectionAttributes = getCollectionAttributes(); - collectionAttributes.set(LOCAL_COLLECTION_SYNCED_TIME, Objects.toString(timestamps.getCollectionTime())); - collection.setAttributes(collectionAttributes); - - Attributes tombstoneAttributes = getTombstoneAttributes(); - tombstoneAttributes.set(LOCAL_TOMBSTONE_SYNCED_TIME, Objects.toString(timestamps.getTombstoneTime())); - tombstoneMap.setAttributes(tombstoneAttributes); + public Markers getRemoteStartMarkers() { + return getMarkers(REMOTE_COLLECTION_MARKER, REMOTE_TOMBSTONE_MARKER); } - public void setRemoteSyncedTime(Timestamps timestamps) { - Attributes collectionAttributes = getCollectionAttributes(); - collectionAttributes.set(REMOTE_COLLECTION_SYNCED_TIME, Objects.toString(timestamps.getCollectionTime())); - collection.setAttributes(collectionAttributes); - - Attributes tombstoneAttributes = getTombstoneAttributes(); - tombstoneAttributes.set(REMOTE_TOMBSTONE_SYNCED_TIME, Objects.toString(timestamps.getTombstoneTime())); - tombstoneMap.setAttributes(tombstoneAttributes); + public void setRemoteNextMarkers(Markers markers) { + setMarkers(REMOTE_COLLECTION_MARKER, REMOTE_TOMBSTONE_MARKER, markers); } public Attributes getCollectionAttributes() { @@ -140,49 +147,73 @@ public Attributes getCollectionAttributes() { } public Attributes getTombstoneAttributes() { - Attributes attributes = tombstoneMap.getAttributes(); + Attributes attributes = tombstones.getAttributes(); if (attributes == null) { attributes = new Attributes(); - tombstoneMap.setAttributes(attributes); + tombstones.setAttributes(attributes); } return attributes; } - private void createTombstones() { - NitriteStore store = collection.getStore(); + private void initializeDataType() { + this.collection = config.getCollection(); + this.counter = new AtomicInteger(0); + + Nitrite db = config.getDb(); Attributes collectionAttributes = getCollectionAttributes(); String tombstoneName = getTombstoneName(collectionAttributes); - this.tombstoneMap = store.openMap(tombstoneName, NitriteId.class, Long.class); - collection.setAttributes(collectionAttributes); + + this.tombstones = db.getCollection(tombstoneName); + ensureIndices(); } private String getTombstoneName(Attributes attributes) { String tombstoneName = attributes.get(TOMBSTONE); if (StringUtils.isNullOrEmpty(tombstoneName)) { - tombstoneName = collection.getName() + INTERNAL_NAME_SEPARATOR + TOMBSTONE; + tombstoneName = collection.getName() + "_" + TOMBSTONE; attributes.set(TOMBSTONE, tombstoneName); } return tombstoneName; } - private Timestamps getSyncedTime(String collectionKey, String tombstoneKey) { - Timestamps remoteSyncedTime = new Timestamps(); + private Markers getMarkers(String collectionKey, String tombstoneKey) { + Markers markers = new Markers(); - String remoteCollectionSyncedTime = getCollectionAttributes().get(collectionKey); - if (StringUtils.isNullOrEmpty(remoteCollectionSyncedTime)) { - remoteSyncedTime.setCollectionTime(0L); + String collectionMarker = getCollectionAttributes().get(collectionKey); + if (StringUtils.isNullOrEmpty(collectionMarker)) { + markers.setCollectionMarker(0L); } else { - remoteSyncedTime.setCollectionTime(Long.parseLong(remoteCollectionSyncedTime)); + markers.setCollectionMarker(Long.parseLong(collectionMarker)); } - String remoteTombstoneSyncedTime = getTombstoneAttributes().get(tombstoneKey); - if (StringUtils.isNullOrEmpty(remoteTombstoneSyncedTime)) { - remoteSyncedTime.setTombstoneTime(0L); + String tombstoneMarker = getTombstoneAttributes().get(tombstoneKey); + if (StringUtils.isNullOrEmpty(tombstoneMarker)) { + markers.setTombstoneMarker(0L); } else { - remoteSyncedTime.setTombstoneTime(Long.parseLong(remoteTombstoneSyncedTime)); + markers.setTombstoneMarker(Long.parseLong(tombstoneMarker)); } - return remoteSyncedTime; + return markers; + } + + private void setMarkers(String collectionKey, String tombstoneKey, Markers markers) { + Attributes collectionAttributes = getCollectionAttributes(); + collectionAttributes.set(collectionKey, Objects.toString(markers.getCollectionMarker())); + collection.setAttributes(collectionAttributes); + + Attributes tombstoneAttributes = getTombstoneAttributes(); + tombstoneAttributes.set(tombstoneKey, Objects.toString(markers.getTombstoneMarker())); + tombstones.setAttributes(tombstoneAttributes); + } + + private void ensureIndices() { + if (!collection.hasIndex(DOC_MODIFIED)) { + collection.createIndex(indexOptions(IndexType.NON_UNIQUE), DOC_MODIFIED); + } + + if (!tombstones.hasIndex(TOMBSTONE_COUNTER)) { + tombstones.createIndex(indexOptions(IndexType.NON_UNIQUE), TOMBSTONE_COUNTER); + } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java index 2c229bf93..3dd3fa261 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/LastWriteWinMap.java @@ -19,45 +19,32 @@ import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.DocumentCursor; -import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.collection.meta.Attributes; -import org.dizitart.no2.common.streams.BoundedStream; -import org.dizitart.no2.common.tuples.Pair; -import org.dizitart.no2.common.util.Iterables; -import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.filters.Filter; +import org.dizitart.no2.sync.Config; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import static org.dizitart.no2.collection.FindOptions.skipBy; -import static org.dizitart.no2.collection.meta.Attributes.LAST_MODIFIED_TIME; import static org.dizitart.no2.common.Constants.*; import static org.dizitart.no2.filters.Filter.and; import static org.dizitart.no2.filters.FluentFilter.where; -import static org.dizitart.no2.index.IndexOptions.indexOptions; /** * @author Anindya Chatterjee. */ @Slf4j public class LastWriteWinMap extends ConflictFreeReplicatedDataType { - - public LastWriteWinMap(NitriteCollection collection) { - super(collection); - ensureIndices(); + public LastWriteWinMap(Config config) { + super(config); } @Override public void createTombstone(NitriteId nitriteId, Long deleteTime) { - if (tombstoneMap != null) { - if (tombstoneMap.containsKey(nitriteId)) { - Long time = tombstoneMap.get(nitriteId); - if (deleteTime > time) { - writeTombstoneEntry(nitriteId, deleteTime); - } - } else { - writeTombstoneEntry(nitriteId, deleteTime); - } + if (tombstones != null) { + writeTombstoneEntry(nitriteId, deleteTime, collection.getName()); } } @@ -77,11 +64,11 @@ public void merge(DeltaStates deltaStates) { } @Override - public DeltaStates delta(Timestamps startMarker, Timestamps endMarker, int offset, int size) { + public DeltaStates delta(Markers startMarker, Markers endMarker, int offset, int size) { DeltaStates deltaStates = new DeltaStates(); - deltaStates.setChangeSet(getDocumentChanges(startMarker.getCollectionTime(), endMarker.getCollectionTime(), + deltaStates.setChangeSet(getDocumentChanges(startMarker.getCollectionMarker(), endMarker.getCollectionMarker(), offset, size)); - deltaStates.setTombstoneMap(getTombstoneChanges(startMarker.getTombstoneTime(), endMarker.getTombstoneTime(), + deltaStates.setTombstoneMap(getTombstoneChanges(startMarker.getTombstoneMarker(), endMarker.getTombstoneMarker(), offset, size)); return deltaStates; @@ -100,16 +87,17 @@ private Set getDocumentChanges(Long startTime, Long endTime, private Map getTombstoneChanges(Long startTime, Long endTime, int offset, int size) { - BoundedStream stream - = new BoundedStream<>((long) offset, (long) size, tombstoneMap.entries()); + DocumentCursor cursor = tombstones.find( + and( + where(TOMBSTONE_COUNTER).gt(startTime), + where(TOMBSTONE_COUNTER).lte(endTime), + where(TOMBSTONE_SOURCE).eq(collection.getName()) + ), skipBy(offset).limit(size)); Map tombstoneMap = new HashMap<>(); - for (Pair entry : stream) { - Long syncTimestamp = entry.getSecond(); - if (syncTimestamp > startTime && syncTimestamp <= endTime) { - tombstoneMap.put(entry.getFirst().getIdValue(), - entry.getSecond()); - } + for (Document entry : cursor) { + Long deletedTime = entry.get(DELETED_TIME, Long.class); + tombstoneMap.put(entry.getId().getIdValue(), deletedTime); } return tombstoneMap; } @@ -120,8 +108,9 @@ private void put(Document value) { Document entry = collection.getById(key); if (entry == null) { - if (tombstoneMap.containsKey(key)) { - Long tombstoneTime = tombstoneMap.get(key); + Document tombstone = tombstones.getById(key); + if (tombstone != null) { + Long tombstoneTime = tombstone.get(TOMBSTONE_COUNTER, Long.class); Long docModifiedTime = value.getLastModifiedSinceEpoch(); if (docModifiedTime >= tombstoneTime) { @@ -169,42 +158,24 @@ private void remove(NitriteId key, long timestamp) { entry.put(DOC_SOURCE, REPLICATOR); collection.remove(entry); - createTombstone(key, timestamp); + writeTombstoneEntry(key, timestamp, REPLICATOR); } } - private void writeTombstoneEntry(NitriteId nitriteId, Long deleteTime) { - // if current deleted time is greater than previous deleted time, - // update the deleted time - tombstoneMap.put(nitriteId, deleteTime); - - // update last modified date in tombstone attributes - Attributes attributes = getTombstoneAttributes(); - long lastModifiedTime = Long.parseLong(attributes.get(LAST_MODIFIED_TIME)); - if (deleteTime > lastModifiedTime) { - // if deleted date is higher than the already saved last modified time - // then only update it, otherwise ignore - attributes.set(LAST_MODIFIED_TIME, Long.toString(deleteTime)); - tombstoneMap.setAttributes(attributes); - } + private void writeTombstoneEntry(NitriteId nitriteId, Long deleteTime, String source) { + // to make the counter unique for each item for bulk delete + long tombstoneCounter = REPLICATOR.equalsIgnoreCase(source) + ? 0 : System.currentTimeMillis() + counter.incrementAndGet(); + Document tombstone = Document + .createDocument(DOC_ID, nitriteId.getIdValue()) + .put(DELETED_TIME, deleteTime) + .put(TOMBSTONE_COUNTER, tombstoneCounter) + .put(TOMBSTONE_SOURCE, source); + + tombstones.insert(tombstone); } private void destroyTombstone(NitriteId nitriteId) { - tombstoneMap.remove(nitriteId); - - // update last modified date in tombstone attributes - Attributes attributes = getTombstoneAttributes(); - List deleteTimes = Iterables.toList(tombstoneMap.values()); - Collections.sort(deleteTimes, Collections.reverseOrder()); - - long lastModifiedTime = deleteTimes.get(0); - attributes.set(LAST_MODIFIED_TIME, Long.toString(lastModifiedTime)); - tombstoneMap.setAttributes(attributes); - } - - private void ensureIndices() { - if (!collection.hasIndex(DOC_MODIFIED)) { - collection.createIndex(indexOptions(IndexType.NON_UNIQUE), DOC_MODIFIED); - } + tombstones.remove(Filter.byId(nitriteId)); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Timestamps.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Markers.java similarity index 88% rename from nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Timestamps.java rename to nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Markers.java index e23d9537b..dbf3a326b 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Timestamps.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/Markers.java @@ -23,7 +23,7 @@ * @author Anindya Chatterjee */ @Data -public class Timestamps { - private Long collectionTime; - private Long tombstoneTime; +public class Markers { + private Long collectionMarker; + private Long tombstoneMarker; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java index b29c81576..da7271801 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchChangeEndHandler.java @@ -40,13 +40,11 @@ public void handleMessage(WebSocket webSocket, BatchChangeEnd message) { MessageFactory factory = new MessageFactory(); BatchEndAck batchEndAck = factory.createBatchEndAck(replicatedCollection.getConfig(), replicatedCollection.getReplicaId(), message.getHeader().getTransactionId()); - batchEndAck.setStartTime(message.getStartTime()); - batchEndAck.setEndTime(message.getEndTime()); batchEndAck.getHeader().setCorrelationId(message.getHeader().getId()); DataGateSocketListener dataGateSocketListener = replicatedCollection.getDataGateSocketListener(); dataGateSocketListener.sendMessage(webSocket, batchEndAck); - replicatedCollection.setRemoteSyncedTime(message.getEndTime()); + replicatedCollection.setRemoteNextMarkers(message.getEndMarkers()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java index ff2d00fa1..49be97ffe 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/BatchEndAckHandler.java @@ -34,6 +34,6 @@ public BatchEndAckHandler(ReplicatedCollection replicatedCollection) { @Override public void handleMessage(WebSocket webSocket, BatchEndAck message) { - replicatedCollection.setLocalSyncedTime(message.getEndTime()); + replicatedCollection.setLocalNextMarkers(message.getEndMarkers()); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java index ee3adf8df..4ebf7fd5f 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptAckSender.java @@ -47,8 +47,8 @@ default void sendAck(WebSocket webSocket, ReceiptAware message) { batchAck.setBatchSize(batchMessage.getBatchSize()); // set start time and end time - batchAck.setStartTime(batchMessage.getStartTime()); - batchAck.setEndTime(batchMessage.getEndTime()); + batchAck.setStartMarkers(batchMessage.getStartMarkers()); + batchAck.setEndMarkers(batchMessage.getEndMarkers()); DataGateSocketListener dataGateSocketListener = getReplicatedCollection().getDataGateSocketListener(); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java index 195f5c957..283dd371a 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/handlers/ReceiptLedgerAware.java @@ -26,6 +26,7 @@ import java.util.HashSet; /** + * TODO: documentation * @author Anindya Chatterjee */ public interface ReceiptLedgerAware { diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java index f95a8f4ce..f8ab01f0e 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/BatchMessage.java @@ -19,7 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; -import org.dizitart.no2.sync.crdt.Timestamps; +import org.dizitart.no2.sync.crdt.Markers; /** * @author Anindya Chatterjee @@ -29,6 +29,6 @@ public abstract class BatchMessage implements DataGateMessage { private Integer nextOffset; private Integer batchSize; - private Timestamps startTime; - private Timestamps endTime; + private Markers startMarkers; + private Markers endMarkers; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/Receipt.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/Receipt.java index e560ea26c..95dc45aa7 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/Receipt.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/message/Receipt.java @@ -19,6 +19,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import org.dizitart.no2.collection.Document; import java.util.HashSet; import java.util.Set; @@ -32,4 +33,17 @@ public class Receipt { private Set added = new HashSet<>(); private Set removed = new HashSet<>(); + + @SuppressWarnings("unchecked") + public static Receipt fromDocument(Document document) { + return new Receipt( + (Set) document.get("added", Set.class), + (Set) document.get("removed", Set.class) + ); + } + + public Document toDocument() { + return Document.createDocument("added", added) + .put("removed", removed); + } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocketListener.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocketListener.java deleted file mode 100644 index 5df52b353..000000000 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateSocketListener.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.sync.net; - -import okhttp3.Response; -import okio.ByteString; - -/** - * @author Anindya Chatterjee - */ -public interface DataGateSocketListener { - default void onOpen(Response response) { - - } - - default void onMessage(String text) { - - } - - default void onMessage(ByteString bytes) { - - } - - default void onReconnect() { - - } - - default void onClosing(int code, String reason) { - - } - - default void onClosed(int code, String reason) { - - } - - default void onFailure(Throwable error, Response response) { - - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java b/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java index e48c446b9..4f7af2d40 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java @@ -29,11 +29,15 @@ import org.dizitart.no2.store.NitriteStore; import org.junit.Assert; +import java.io.File; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; +import static org.junit.Assert.assertTrue; + /** * @author Anindya Chatterjee */ @@ -96,9 +100,19 @@ public static Document randomDocument() { .put("age", faker.random().nextLong()); } - public static NitriteMap getTombstone(NitriteCollection collection) { + public static String getRandomTempDbFile() { + String dataDir = System.getProperty("java.io.tmpdir") + File.separator + + "nitrite" + File.separator + "data"; + File file = new File(dataDir); + if (!file.exists()) { + assertTrue(file.mkdirs()); + } + return file.getPath() + File.separator + UUID.randomUUID() + ".db"; + } + + public static NitriteMap getTombstone(NitriteCollection collection) { NitriteStore nitriteStore = collection.getStore(); String tombStoneName = collection.getAttributes().get(Attributes.TOMBSTONE); - return nitriteStore.openMap(tombStoneName, NitriteId.class, Long.class); + return nitriteStore.openMap(tombStoneName, NitriteId.class, Document.class); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/AnotherIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/AnotherIntegrationTest.java deleted file mode 100644 index a6cd1c936..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/AnotherIntegrationTest.java +++ /dev/null @@ -1,681 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.integration; - -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.Nitrite; -import org.dizitart.no2.TestUtils; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.filters.Filter; -import org.dizitart.no2.sync.Replica; -import org.dizitart.no2.sync.event.ReplicationEvent; -import org.dizitart.no2.sync.event.ReplicationEventType; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.testcontainers.containers.GenericContainer; -import org.testcontainers.containers.MongoDBContainer; -import org.testcontainers.containers.Network; -import org.testcontainers.containers.output.Slf4jLogConsumer; -import org.testcontainers.images.builder.ImageFromDockerfile; -import org.testcontainers.utility.DockerImageName; - -import java.util.List; -import java.util.Random; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.awaitility.Awaitility.await; -import static org.dizitart.no2.TestUtils.*; -import static org.dizitart.no2.integration.DataGateIntegrationTest.getRandomTempDbFile; -import static org.junit.Assert.assertEquals; - -/** - * @author Anindya Chatterjee - */ -@Slf4j -public class AnotherIntegrationTest { - private String dbFile1, dbFile2; - private Nitrite db1, db2; - - private final Network network = Network.newNetwork(); - private GenericContainer datagate; - private MongoDBContainer mongodb; - private GenericContainer mongoSeed; - - @Rule(order = 0) - public Retry retry = new Retry(1); - - @Before - public void setUp() throws Exception { - Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log); - - mongodb = new MongoDBContainer( - DockerImageName.parse("mongo:latest")) - .withNetwork(network) - .withNetworkAliases("mongo") - .withExposedPorts(27017); - - mongoSeed = new GenericContainer<>(new ImageFromDockerfile() - .withFileFromClasspath("appConfig.json", "mongo-seed/appConfig.json") - .withFileFromClasspath("Dockerfile", "mongo-seed/Dockerfile")) - .withNetwork(network); - - mongodb.start(); - mongoSeed.start(); - - datagate = new GenericContainer<>( - DockerImageName.parse("nitrite/nitrite-datagate:latest")) - .withEnv("MONGO_URL", "mongodb://mongo:27017/datagate") - .withImagePullPolicy(imageName -> false) - .withNetwork(network) - .withLogConsumer(logConsumer) - .withExposedPorts(46005); - - datagate.start(); - - UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd@gmail.com"); - UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd2@gmail.com"); - UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd3@gmail.com"); - } - - @After - public void cleanUp() { - mongodb.stop(); - mongoSeed.stop(); - datagate.stop(); - - if (db1 != null && dbFile1 != null) { - db1.close(); - TestUtils.deleteDb(dbFile1); - } - - if (db2 != null && dbFile2 != null) { - db2.close(); - TestUtils.deleteDb(dbFile2); - } - } - - @Test - public void testSingleUserSingleReplica() throws Exception { - String host = datagate.getHost(); - Integer port = datagate.getMappedPort(46005); - - String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); - - db1 = createDb(dbFile1); - NitriteCollection c1 = db1.getCollection("testSingleUserSingleReplica"); - - Replica replica = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("integration-test") - .jwtAuth("abcd@gmail.com", jwt) - .create(); - - replica.connect(); - - Random random = new Random(); - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - await().atMost(5, SECONDS).until(() -> c1.size() == 10); - c1.remove(Filter.ALL); - await().atMost(5, SECONDS).until(() -> c1.size() == 0); - replica.disconnectNow(); - } - - @Test - public void testSingleUserMultiReplica() throws Exception { - dbFile1 = getRandomTempDbFile(); - dbFile2 = getRandomTempDbFile(); - - String host = datagate.getHost(); - Integer port = datagate.getMappedPort(46005); - - db1 = createDb(dbFile1); - db2 = createDb(dbFile2); - - NitriteCollection c1 = db1.getCollection("testSingleUserMultiReplica"); - NitriteCollection c2 = db2.getCollection("testSingleUserMultiReplica"); - - String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("integration-test") - .jwtAuth("abcd@gmail.com", jwt) - .replicaName("r1") - .acceptAllCertificates(true) - .create(); - - Replica r2 = Replica.builder() - .of(c2) - .remoteHost(host) - .remotePort(port) - .tenant("integration-test") - .jwtAuth("abcd@gmail.com", jwt) - .replicaName("r2") - .acceptAllCertificates(true) - .create(); - - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - await().atMost(5, SECONDS).until(() -> c1.size() == 10); - assertEquals(c2.size(), 0); - - r2.connect(); - await().atMost(15, SECONDS).until(() -> c2.size() == 10); - - Random random = new Random(); - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - - await().atMost(10, SECONDS).until(() -> c1.size() == 40); - assertEquals(c2.size(), 40); - - r1.disconnect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - r1.connect(); - await().atMost(10, SECONDS).until(() -> c1.size() == 70 && c2.size() == 70); - TestUtils.assertEquals(c1, c2); - - c2.remove(Filter.ALL); - - await().atMost(10, SECONDS).until(() -> c2.size() == 0); - - await().atMost(30, SECONDS).until(() -> c1.size() == 0); - TestUtils.assertEquals(c1, c2); - - r1.disconnectNow(); - r2.disconnectNow(); - } - - @Test - public void testMultiUserSingleReplica() throws Exception { - String host = datagate.getHost(); - Integer port = datagate.getMappedPort(46005); - - String jwt1 = UserClient.getToken(host, port, "abcd@gmail.com"); - String jwt2 = UserClient.getToken(host, port, "abcd2@gmail.com"); - String jwt3 = UserClient.getToken(host, port, "abcd3@gmail.com"); - - Nitrite db1 = createDb(); - NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica"); - - Nitrite db2 = createDb(); - NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica"); - - Nitrite db3 = createDb(); - NitriteCollection c3 = db3.getCollection("testMultiUserSingleReplica"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt1) - .create(); - r1.connect(); - - Replica r2 = Replica.builder() - .of(c2) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd2@gmail.com", jwt2) - .create(); - r2.connect(); - - Replica r3 = Replica.builder() - .of(c3) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd3@gmail.com", jwt3) - .create(); - r3.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - } - - for (int i = 0; i < 30; i++) { - Document document = randomDocument(); - c3.insert(document); - } - - await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20 && c3.size() == 30); - - TestUtils.assertNotEquals(c1, c2); - TestUtils.assertNotEquals(c1, c3); - TestUtils.assertNotEquals(c2, c3); - - r1.disconnectNow(); - r2.disconnectNow(); - r3.disconnectNow(); - } - - @Test - public void testMultiUserMultiReplica() throws Exception { - String host = datagate.getHost(); - Integer port = datagate.getMappedPort(46005); - - String jwt1 = UserClient.getToken(host, port, "abcd@gmail.com"); - String jwt2 = UserClient.getToken(host, port, "abcd2@gmail.com"); - - Nitrite db1 = createDb(); - NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica1"); - - Nitrite db2 = createDb(); - NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica2"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt1) - .create(); - r1.connect(); - - Replica r2 = Replica.builder() - .of(c2) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd2@gmail.com", jwt2) - .create(); - r2.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - } - - await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20); - - TestUtils.assertNotEquals(c1, c2); - r1.disconnectNow(); - r2.disconnectNow(); - } - - @Test - public void testSecurityInCorrectCredentials() { - dbFile1 = getRandomTempDbFile(); - - String host = datagate.getHost(); - Integer port = datagate.getMappedPort(46005); - - Nitrite db1 = createDb(dbFile1); - NitriteCollection c1 = db1.getCollection("testSecurityInCorrectCredentials"); - - AtomicReference errorEvent = new AtomicReference<>(); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", "wrong_token") - .addReplicationEventListener(event -> { - if (event.getEventType() == ReplicationEventType.Error && errorEvent.get() == null) { - errorEvent.set(event); - } - }) - .create(); - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - assertEquals(c1.size(), 10); - - await().atMost(5, SECONDS).until(() -> { - ReplicationEvent replicationEvent = errorEvent.get(); - return replicationEvent.getError().getMessage().contains("failed to validate token"); - }); - r1.disconnectNow(); - } - - @Test - public void testCloseDbAndReconnect() throws Exception { - String host = datagate.getHost(); - Integer port = datagate.getMappedPort(46005); - - String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); - - dbFile1 = getRandomTempDbFile(); - dbFile2 = getRandomTempDbFile(); - - db1 = createDb(dbFile1); - - db2 = createDb(dbFile2); - - NitriteCollection c1 = db1.getCollection("testCloseDbAndReconnect"); - NitriteCollection c2 = db2.getCollection("testCloseDbAndReconnect"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .replicaName("r1") - .create(); - - Replica r2 = Replica.builder() - .of(c2) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .replicaName("r2") - .create(); - - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - NitriteCollection finalC1 = c1; - await().atMost(5, SECONDS).until(() -> finalC1.size() == 10); - assertEquals(c2.size(), 0); - - r2.connect(); - await().atMost(5, SECONDS).until(() -> c2.size() == 10); - - Random random = new Random(); - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - NitriteCollection finalC2 = c1; - await().atMost(10, SECONDS).until(() -> finalC2.size() == 40); - assertEquals(c2.size(), 40); - - r1.disconnect(); - r1.close(); - db1.close(); - - db1 = createDb(dbFile1); - c1 = db1.getCollection("testCloseDbAndReconnect"); - r1 = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .replicaName("r1") - .create(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - r1.connect(); - NitriteCollection finalC = c1; - await().atMost(10, SECONDS).until(() -> finalC.size() == 70 && c2.size() == 70); - TestUtils.assertEquals(c1, c2); - - log.info("Collection Size - " + finalC.find().size()); - log.info("Collection Attributes - " + finalC.getAttributes()); - log.info("Tombstone Size - " + getTombstone(finalC).entries().size()); - log.info("Tombstone Attributes - " + getTombstone(finalC).getAttributes()); - - c2.remove(Filter.ALL); - - try { - await().atMost(50, SECONDS).until(() -> finalC.size() == 0); - TestUtils.assertEquals(c1, c2); - } catch (Exception e) { - log.info("Test Failed. Current status as below"); - log.info("Collection content - " + finalC.find().toList()); - log.info("Collection Attributes - " + finalC.getAttributes()); - log.info("Tombstone content - " + getTombstone(finalC).entries().toList()); - log.info("Tombstone Attributes - " + getTombstone(finalC).getAttributes()); - throw e; - } finally { - r1.disconnectNow(); - r2.disconnectNow(); - } - } - - @Test - public void testDelayedConnect() throws Exception { - String host = datagate.getHost(); - Integer port = datagate.getMappedPort(46005); - - String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); - - dbFile1 = getRandomTempDbFile(); - - Nitrite db1 = createDb(dbFile1); - - NitriteCollection c1 = db1.getCollection("testDelayedConnect"); - Replica r1 = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .create(); - - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - // allow it to reach the datagate server - Thread.sleep(5000); - - r1.disconnect(); - r1.close(); - db1.close(); - - Nitrite db2 = createDb(); - NitriteCollection c2 = db2.getCollection("testDelayedConnect"); - Replica r2 = Replica.builder() - .of(c2) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .create(); - r2.connect(); - await().atMost(5, SECONDS).until(() -> c2.size() == 10); - - r1.disconnectNow(); - r2.disconnectNow(); - } - - @Test - public void testDelayedConnectRemoveAll() throws Exception { - String host = datagate.getHost(); - Integer port = datagate.getMappedPort(46005); - - String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); - - dbFile1 = getRandomTempDbFile(); - - Nitrite db = createDb(dbFile1); - NitriteCollection c1 = db.getCollection("testDelayedConnect"); - Replica r1 = Replica.builder() - .of(c1) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .replicaName("r1") - .create(); - - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - c1.remove(Filter.ALL); - assertEquals(c1.size(), 0); - - r1.disconnect(); - r1.close(); - db.close(); - - Nitrite db2 = createDb(); - NitriteCollection c2 = db2.getCollection("testDelayedConnect"); - Replica r2 = Replica.builder() - .of(c2) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .replicaName("r2") - .create(); - - r2.connect(); - - for (int i = 0; i < 5; i++) { - Document document = randomDocument(); - c2.insert(document); - } - - db = createDb(dbFile1); - NitriteCollection c3 = db.getCollection("testDelayedConnect"); - r1 = Replica.builder() - .of(c3) - .remoteHost(host) - .remotePort(port) - .tenant("junit-test") - .jwtAuth("abcd@gmail.com", jwt) - .replicaName("r1") - .create(); - - r1.connect(); - - await().atMost(5, SECONDS).until(() -> { - List l1 = c3.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); - List l2 = c2.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); - return l1.equals(l2); - }); - - r1.disconnectNow(); - r2.disconnectNow(); - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index ad7ad3e82..0b9f02723 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -1,130 +1,130 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2021 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.integration; -import com.palantir.docker.compose.DockerComposeRule; -import com.palantir.docker.compose.configuration.ProjectName; -import com.palantir.docker.compose.configuration.ShutdownStrategy; -import com.palantir.docker.compose.connection.DockerPort; -import com.palantir.docker.compose.connection.waiting.HealthChecks; import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.IntegrationTest; import org.dizitart.no2.Nitrite; +import org.dizitart.no2.Retry; import org.dizitart.no2.TestUtils; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.sync.Replica; -import org.dizitart.no2.sync.ReplicationException; import org.dizitart.no2.sync.event.ReplicationEvent; import org.dizitart.no2.sync.event.ReplicationEventType; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.experimental.categories.Category; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.MongoDBContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.shaded.org.bouncycastle.util.Objects; +import org.testcontainers.utility.DockerImageName; -import java.io.File; -import java.io.IOException; import java.util.List; import java.util.Random; -import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import static java.util.concurrent.TimeUnit.SECONDS; import static org.awaitility.Awaitility.await; -import static org.dizitart.no2.TestUtils.createDb; -import static org.dizitart.no2.TestUtils.randomDocument; +import static org.dizitart.no2.TestUtils.*; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.*; /** * @author Anindya Chatterjee */ @Slf4j -@Category(IntegrationTest.class) public class DataGateIntegrationTest { - private static final int DATAGATE_PORT = 46005; - private static final String MONGODB = "mongo"; - private static final String DATAGATE = "datagate"; - + private final Network network = Network.newNetwork(); private String dbFile1, dbFile2; private Nitrite db1, db2; - - private DockerPort datagate; + private GenericContainer datagate; + private MongoDBContainer mongodb; + private GenericContainer mongoSeed; @Rule(order = 0) public Retry retry = new Retry(3); - @Rule(order = 1) - public DockerComposeRule dockerRule = DockerComposeRule.builder() - .file("src/test/resources/docker-compose.yml") - .projectName(ProjectName.random()) - .waitingForService(MONGODB, HealthChecks.toHaveAllPortsOpen()) - .waitingForService(DATAGATE, HealthChecks.toHaveAllPortsOpen()) - .shutdownStrategy(ShutdownStrategy.GRACEFUL) - .build(); - - public static String getRandomTempDbFile() { - String dataDir = System.getProperty("java.io.tmpdir") + File.separator - + "nitrite" + File.separator + "data"; - File file = new File(dataDir); - if (!file.exists()) { - assertTrue(file.mkdirs()); - } - return file.getPath() + File.separator + UUID.randomUUID() + ".db"; - } - @Before - public void setUpContainer() throws Exception { - datagate = dockerRule.containers() - .container(DATAGATE) - .port(DATAGATE_PORT); - - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); - - UserClient.createUser(host, port, "abcd@gmail.com"); - UserClient.createUser(host, port, "abcd2@gmail.com"); - UserClient.createUser(host, port, "abcd3@gmail.com"); + public void setUp() throws Exception { + Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(log); + + mongodb = new MongoDBContainer( + DockerImageName.parse("mongo:latest")) + .withNetwork(network) + .withNetworkAliases("mongo") + .withExposedPorts(27017); + + mongoSeed = new GenericContainer<>(new ImageFromDockerfile() + .withFileFromClasspath("appConfig.json", "mongo-seed/appConfig.json") + .withFileFromClasspath("Dockerfile", "mongo-seed/Dockerfile")) + .withNetwork(network); + + mongodb.start(); + mongoSeed.start(); + + datagate = new GenericContainer<>( + DockerImageName.parse("nitrite/nitrite-datagate:latest")) + .withEnv("MONGO_URL", "mongodb://mongo:27017/datagate") + .withImagePullPolicy(imageName -> false) + .withNetwork(network) + .withLogConsumer(logConsumer) + .withExposedPorts(46005); + + datagate.start(); + + UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd@gmail.com"); + UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd2@gmail.com"); + UserClient.createUser(datagate.getHost(), datagate.getFirstMappedPort(), "abcd3@gmail.com"); } @After - public void after() throws IOException, InterruptedException { - dockerRule.dockerCompose().down(); - dockerRule.dockerCompose().kill(); - dockerRule.dockerCompose().rm(); + public void cleanUp() { + mongodb.stop(); + mongoSeed.stop(); + datagate.stop(); if (db1 != null && dbFile1 != null) { - db1.close(); + if (!db1.isClosed()) { + db1.close(); + } TestUtils.deleteDb(dbFile1); } if (db2 != null && dbFile2 != null) { - db2.close(); + if (!db2.isClosed()) { + db2.close(); + } TestUtils.deleteDb(dbFile2); } } @Test public void testSingleUserSingleReplica() throws Exception { - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); @@ -132,6 +132,7 @@ public void testSingleUserSingleReplica() throws Exception { NitriteCollection c1 = db1.getCollection("testSingleUserSingleReplica"); Replica replica = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) @@ -163,8 +164,8 @@ public void testSingleUserMultiReplica() throws Exception { dbFile1 = getRandomTempDbFile(); dbFile2 = getRandomTempDbFile(); - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); db1 = createDb(dbFile1); db2 = createDb(dbFile2); @@ -175,6 +176,7 @@ public void testSingleUserMultiReplica() throws Exception { String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); Replica r1 = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) @@ -182,10 +184,10 @@ public void testSingleUserMultiReplica() throws Exception { .jwtAuth("abcd@gmail.com", jwt) .replicaName("r1") .acceptAllCertificates(true) - .chunkSize(100) .create(); Replica r2 = Replica.builder() + .database(db2) .of(c2) .remoteHost(host) .remotePort(port) @@ -193,7 +195,6 @@ public void testSingleUserMultiReplica() throws Exception { .jwtAuth("abcd@gmail.com", jwt) .replicaName("r2") .acceptAllCertificates(true) - .chunkSize(100) .create(); r1.connect(); @@ -273,8 +274,8 @@ public void testSingleUserMultiReplica() throws Exception { @Test public void testMultiUserSingleReplica() throws Exception { - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); String jwt1 = UserClient.getToken(host, port, "abcd@gmail.com"); String jwt2 = UserClient.getToken(host, port, "abcd2@gmail.com"); @@ -290,6 +291,7 @@ public void testMultiUserSingleReplica() throws Exception { NitriteCollection c3 = db3.getCollection("testMultiUserSingleReplica"); Replica r1 = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) @@ -299,6 +301,7 @@ public void testMultiUserSingleReplica() throws Exception { r1.connect(); Replica r2 = Replica.builder() + .database(db2) .of(c2) .remoteHost(host) .remotePort(port) @@ -308,6 +311,7 @@ public void testMultiUserSingleReplica() throws Exception { r2.connect(); Replica r3 = Replica.builder() + .database(db3) .of(c3) .remoteHost(host) .remotePort(port) @@ -344,8 +348,8 @@ public void testMultiUserSingleReplica() throws Exception { @Test public void testMultiUserMultiReplica() throws Exception { - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); String jwt1 = UserClient.getToken(host, port, "abcd@gmail.com"); String jwt2 = UserClient.getToken(host, port, "abcd2@gmail.com"); @@ -357,6 +361,7 @@ public void testMultiUserMultiReplica() throws Exception { NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica2"); Replica r1 = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) @@ -366,6 +371,7 @@ public void testMultiUserMultiReplica() throws Exception { r1.connect(); Replica r2 = Replica.builder() + .database(db2) .of(c2) .remoteHost(host) .remotePort(port) @@ -393,15 +399,18 @@ public void testMultiUserMultiReplica() throws Exception { @Test public void testSecurityInCorrectCredentials() { - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); + dbFile1 = getRandomTempDbFile(); - Nitrite db1 = createDb(); - NitriteCollection c1 = db1.getCollection("testSecurity"); + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + db1 = createDb(dbFile1); + NitriteCollection c1 = db1.getCollection("testSecurityInCorrectCredentials"); AtomicReference errorEvent = new AtomicReference<>(); Replica r1 = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) @@ -421,44 +430,48 @@ public void testSecurityInCorrectCredentials() { } assertEquals(c1.size(), 10); + await().atMost(5, SECONDS).until(() -> { ReplicationEvent replicationEvent = errorEvent.get(); - return replicationEvent.getError() instanceof ReplicationException && - replicationEvent.getError().getMessage().contains("failed to validate token"); + return replicationEvent.getError().getMessage().contains("failed to validate token"); }); r1.disconnectNow(); } @Test public void testCloseDbAndReconnect() throws Exception { - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); dbFile1 = getRandomTempDbFile(); + dbFile2 = getRandomTempDbFile(); - Nitrite db = createDb(dbFile1); - - Nitrite db2 = createDb(); + db1 = createDb(dbFile1); + db2 = createDb(dbFile2); - NitriteCollection c1 = db.getCollection("testCloseDbAndReconnect"); + NitriteCollection c1 = db1.getCollection("testCloseDbAndReconnect"); NitriteCollection c2 = db2.getCollection("testCloseDbAndReconnect"); Replica r1 = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) .tenant("junit-test") .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") .create(); Replica r2 = Replica.builder() + .database(db2) .of(c2) .remoteHost(host) .remotePort(port) .tenant("junit-test") .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r2") .create(); r1.connect(); @@ -479,11 +492,6 @@ public void testCloseDbAndReconnect() throws Exception { for (int i = 0; i < 10; i++) { Document document = randomDocument(); c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } } for (int i = 0; i < 20; i++) { @@ -502,16 +510,18 @@ public void testCloseDbAndReconnect() throws Exception { r1.disconnect(); r1.close(); - db.close(); + db1.close(); - db = createDb(dbFile1); - c1 = db.getCollection("testCloseDbAndReconnect"); + db1 = createDb(dbFile1); + c1 = db1.getCollection("testCloseDbAndReconnect"); r1 = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) .tenant("junit-test") .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") .create(); for (int i = 0; i < 10; i++) { @@ -541,8 +551,7 @@ public void testCloseDbAndReconnect() throws Exception { c2.remove(Filter.ALL); - await().atMost(10, SECONDS).until(() -> c2.size() == 0); - await().atMost(5, SECONDS).until(() -> finalC.size() == 0); + await().atMost(10, SECONDS).until(() -> finalC.size() == 0); TestUtils.assertEquals(c1, c2); r1.disconnectNow(); @@ -551,17 +560,18 @@ public void testCloseDbAndReconnect() throws Exception { @Test public void testDelayedConnect() throws Exception { - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); dbFile1 = getRandomTempDbFile(); - Nitrite db1 = createDb(dbFile1); + db1 = createDb(dbFile1); NitriteCollection c1 = db1.getCollection("testDelayedConnect"); Replica r1 = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) @@ -575,7 +585,9 @@ public void testDelayedConnect() throws Exception { Document document = randomDocument(); c1.insert(document); } - await().atMost(5, SECONDS).until(() -> c1.size() == 10); + + // allow it to reach the datagate server + Thread.sleep(5000); r1.disconnect(); r1.close(); @@ -584,6 +596,7 @@ public void testDelayedConnect() throws Exception { Nitrite db2 = createDb(); NitriteCollection c2 = db2.getCollection("testDelayedConnect"); Replica r2 = Replica.builder() + .database(db2) .of(c2) .remoteHost(host) .remotePort(port) @@ -599,16 +612,17 @@ public void testDelayedConnect() throws Exception { @Test public void testDelayedConnectRemoveAll() throws Exception { - String host = datagate.getIp(); - Integer port = datagate.getExternalPort(); + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); dbFile1 = getRandomTempDbFile(); - Nitrite db = createDb(dbFile1); - NitriteCollection c1 = db.getCollection("testDelayedConnect"); + db1 = createDb(dbFile1); + NitriteCollection c1 = db1.getCollection("testDelayedConnect"); Replica r1 = Replica.builder() + .database(db1) .of(c1) .remoteHost(host) .remotePort(port) @@ -629,11 +643,12 @@ public void testDelayedConnectRemoveAll() throws Exception { r1.disconnect(); r1.close(); - db.close(); + db1.close(); Nitrite db2 = createDb(); NitriteCollection c2 = db2.getCollection("testDelayedConnect"); Replica r2 = Replica.builder() + .database(db2) .of(c2) .remoteHost(host) .remotePort(port) @@ -649,9 +664,10 @@ public void testDelayedConnectRemoveAll() throws Exception { c2.insert(document); } - db = createDb(dbFile1); - NitriteCollection c3 = db.getCollection("testDelayedConnect"); + db1 = createDb(dbFile1); + NitriteCollection c3 = db1.getCollection("testDelayedConnect"); r1 = Replica.builder() + .database(db1) .of(c3) .remoteHost(host) .remotePort(port) @@ -671,4 +687,136 @@ public void testDelayedConnectRemoveAll() throws Exception { r1.disconnectNow(); r2.disconnectNow(); } + + @Test + public void testHighestRevisionWin() throws Exception { + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + dbFile1 = getRandomTempDbFile(); + dbFile2 = getRandomTempDbFile(); + + db1 = createDb(dbFile1); + db2 = createDb(dbFile2); + + NitriteCollection c1 = db1.getCollection("testHighestRevisionWin"); + NitriteCollection c2 = db2.getCollection("testHighestRevisionWin"); + + Replica r1 = Replica.builder() + .database(db1) + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .create(); + + Replica r2 = Replica.builder() + .database(db2) + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r2") + .create(); + + r1.connect(); + r2.connect(); + + Document document = randomDocument(); + c1.insert(document); + + await().atMost(5, SECONDS).until(() -> c2.size() == 1); + + Document d2 = c2.find().firstOrNull(); + d2.put("age", 38); + + assertTrue(Objects.areEqual(c1.find().firstOrNull().getRevision(), 1)); + assertNotEquals(c1.find().firstOrNull().get("age"), d2.get("age")); + + c2.update(d2); + + await().atMost(5, SECONDS).until(() -> { + Document d1 = c1.find().firstOrNull(); + return Objects.areEqual(d1.get("age"), 38) && Objects.areEqual(d1.getRevision(), 2); + }); + } + + @Test + public void testRandomInsertDelete() throws Exception { + String host = datagate.getHost(); + Integer port = datagate.getMappedPort(46005); + + String jwt = UserClient.getToken(host, port, "abcd@gmail.com"); + + dbFile1 = getRandomTempDbFile(); + dbFile2 = getRandomTempDbFile(); + + db1 = createDb(dbFile1); + db2 = createDb(dbFile2); + + NitriteCollection c1 = db1.getCollection("testRandomInsertDelete"); + NitriteCollection c2 = db2.getCollection("testRandomInsertDelete"); + + Replica r1 = Replica.builder() + .database(db1) + .of(c1) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r1") + .create(); + + Replica r2 = Replica.builder() + .database(db2) + .of(c2) + .remoteHost(host) + .remotePort(port) + .tenant("junit-test") + .jwtAuth("abcd@gmail.com", jwt) + .replicaName("r2") + .create(); + + r1.connect(); + r2.connect(); + + Random random = new Random(); + + // insert + for (int i = 0; i < random.nextInt(50); i++) { + Document document = randomDocument(); + c1.insert(document); + Thread.sleep(random.nextInt(100)); + } + + // insert + for (int i = 0; i < random.nextInt(30); i++) { + Document document = randomDocument(); + c2.insert(document); + Thread.sleep(random.nextInt(100)); + } + + // update + DocumentCursor cursor = c1.find(); + for (Document document : cursor) { + document.put("age", random.nextInt()); + c1.update(document); + } + + // delete + for (int i = 0; i < random.nextInt(30); i++) { + Document document = c1.find().firstOrNull(); + c1.remove(document); + Thread.sleep(random.nextInt(100)); + } + + await().atMost(20, SECONDS).until(() -> c1.size() > 0 && c1.size() == c2.size()); + System.out.println("C1 Size = " + c1.size()); + TestUtils.assertEquals(c1, c2); + } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java deleted file mode 100644 index 3d6a27d11..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/Retry.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.integration; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -/** - * @author Anindya Chatterjee - */ -public class Retry implements TestRule { - private final int retryCount; - - public Retry(int retryCount) { - this.retryCount = retryCount; - } - - public Statement apply(Statement base, Description description) { - return statement(base, description); - } - - private Statement statement(final Statement base, final Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - Throwable caughtThrowable = null; - - // implement retry logic here - for (int i = 0; i < retryCount; i++) { - try { - base.evaluate(); - return; - } catch (Throwable t) { - caughtThrowable = t; - System.err.println(description.getDisplayName() + ": run " + (i + 1) + " failed"); - } - } - System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures"); - throw caughtThrowable; - } - }; - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java index 2bc2e1723..eff7705eb 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java @@ -19,18 +19,14 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; import okhttp3.*; /** * @author Anindya Chatterjee */ +@Slf4j public class UserClient { - -// @Test - public void test() throws Exception { - createUser("127.0.0.1", 46005, "abcd@gmail.com"); - } - public static void createUser(String host, Integer port, String user) throws Exception { OkHttpClient client = getUnsafeOkHttpClient(); Request request = new Request.Builder() @@ -50,8 +46,7 @@ public static void createUser(String host, Integer port, String user) throws Exc } } } catch (Exception e) { - System.out.println("Error checking user " + user); - e.printStackTrace(); + log.error("Error checking user " + user, e); return; } @@ -108,41 +103,12 @@ public static String getToken(String host, Integer port, String user) throws Exc } } - System.err.println(response.body().string()); throw new Exception("failed to login"); } private static OkHttpClient getUnsafeOkHttpClient() { try { -// final TrustManager[] trustAllCerts = new TrustManager[]{ -// new X509TrustManager() { -// -// @Override -// public void checkClientTrusted(java.security.cert.X509Certificate[] chain, -// String authType) { -// } -// -// @Override -// public void checkServerTrusted(java.security.cert.X509Certificate[] chain, -// String authType) { -// } -// -// @Override -// public java.security.cert.X509Certificate[] getAcceptedIssuers() { -// return new java.security.cert.X509Certificate[]{}; -// } -// } -// }; -// -// final SSLContext sslContext = SSLContext.getInstance("SSL"); -// sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); -// -// final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - OkHttpClient.Builder builder = new OkHttpClient.Builder(); -// builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]); - -// builder.hostnameVerifier((hostname, session) -> true); builder.retryOnConnectionFailure(true); return builder.build(); diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java deleted file mode 100644 index 03fc1090c..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaNegativeTest.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.mock; - -import org.dizitart.no2.Nitrite; -import org.dizitart.no2.Retry; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.common.concurrent.ThreadPoolManager; -import org.dizitart.no2.mock.server.MockDataGateServer; -import org.dizitart.no2.mock.server.MockRepository; -import org.dizitart.no2.mock.server.ServerLastWriteWinMap; -import org.dizitart.no2.sync.Replica; -import org.dizitart.no2.sync.ReplicationException; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -import java.util.concurrent.ExecutorService; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.awaitility.Awaitility.await; -import static org.dizitart.no2.TestUtils.createDb; -import static org.dizitart.no2.TestUtils.randomDocument; -import static org.dizitart.no2.mock.ReplicaTest.getRandomTempDbFile; -import static org.junit.Assert.*; - -/** - * @author Anindya Chatterjee - */ -public class ReplicaNegativeTest { - private MockDataGateServer server; - private String dbFile; - private ExecutorService executorService; - private MockRepository mockRepository; - - @Rule - public Retry retry = new Retry(3); - - @Before - public void setUp() throws Exception { - dbFile = getRandomTempDbFile(); - server = new MockDataGateServer(46005); - executorService = ThreadPoolManager.getThreadPool(2, "ReplicaNegativeTest"); - server.start(); - mockRepository = MockRepository.getInstance(); - } - - @After - public void cleanUp() { - if (executorService != null) { - executorService.shutdown(); - } - server.stop(); - } - - @Test - public void testServerClose() { - mockRepository.getUserMap().put("anidotnet", "abcd"); - - Nitrite db1 = createDb(dbFile); - - NitriteCollection c1 = db1.getCollection("testServerClose"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - - r1.connect(); - - executorService.submit(() -> { - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - }); - - await().atMost(5, SECONDS).until(() -> mockRepository.getCollectionReplicaMap().size() == 1); - assertEquals(mockRepository.getUserReplicaMap().size(), 1); - assertTrue(mockRepository.getUserReplicaMap().containsKey("anidotnet")); - assertTrue(mockRepository.getCollectionReplicaMap().containsKey("anidotnet@testServerClose")); - ServerLastWriteWinMap lastWriteWinMap = mockRepository.getReplicaStore().get("anidotnet@testServerClose"); - - await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().find().size() == 10); - server.stop(); - await().atMost(5, SECONDS).until(() -> r1.isDisconnected()); - } - - @Test(expected = ReplicationException.class) - public void testRemoteHostValidation() { - Nitrite db1 = createDb(dbFile); - NitriteCollection c1 = db1.getCollection("testServerClose"); - - - Replica r1 = Replica.builder() - .of(c1) - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - } - - @Test(expected = ReplicationException.class) - public void testRemotePortValidation() { - Nitrite db1 = createDb(dbFile); - NitriteCollection c1 = db1.getCollection("testServerClose"); - - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(null) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - } - - @Test(expected = ReplicationException.class) - public void testTenantValidation() { - Nitrite db1 = createDb(dbFile); - NitriteCollection c1 = db1.getCollection("testServerClose"); - - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .jwtAuth("anidotnet", "abcd") - .create(); - } - - @Test(expected = ReplicationException.class) - public void testCollectionValidation() { - Nitrite db1 = createDb(dbFile); - NitriteCollection c1 = db1.getCollection("testServerClose"); - - - Replica r1 = Replica.builder() - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - } - - @Test(expected = ReplicationException.class) - public void testUserValidation() { - Nitrite db1 = createDb(dbFile); - NitriteCollection c1 = db1.getCollection("testServerClose"); - - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("", "abcd") - .create(); - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java deleted file mode 100644 index 78fab5ea2..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/ReplicaTest.java +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.mock; - -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.Nitrite; -import org.dizitart.no2.Retry; -import org.dizitart.no2.TestUtils; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.filters.Filter; -import org.dizitart.no2.mock.server.MockDataGateServer; -import org.dizitart.no2.mock.server.MockRepository; -import org.dizitart.no2.mock.server.ServerLastWriteWinMap; -import org.dizitart.no2.sync.Replica; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.List; -import java.util.Random; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.stream.Collectors; - -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.awaitility.Awaitility.await; -import static org.dizitart.no2.TestUtils.createDb; -import static org.dizitart.no2.TestUtils.randomDocument; -import static org.dizitart.no2.collection.Document.createDocument; -import static org.dizitart.no2.common.util.DocumentUtils.isSimilar; -import static org.dizitart.no2.filters.FluentFilter.where; -import static org.junit.Assert.*; - -/** - * @author Anindya Chatterjee - */ -@Slf4j -public class ReplicaTest { - private static MockDataGateServer server; - - @Rule - public Retry retry = new Retry(3); - - private String dbFile; - private ExecutorService executorService; - private MockRepository mockRepository; - private Nitrite db; - - public static String getRandomTempDbFile() { - String dataDir = System.getProperty("java.io.tmpdir") + File.separator - + "nitrite" + File.separator + "data"; - File file = new File(dataDir); - if (!file.exists()) { - assertTrue(file.mkdirs()); - } - return file.getPath() + File.separator + UUID.randomUUID() + ".db"; - } - - @Before - public void setUp() throws Exception { - server = new MockDataGateServer(46005); - server.start(); - dbFile = getRandomTempDbFile(); - executorService = Executors.newCachedThreadPool(); - mockRepository = MockRepository.getInstance(); - } - - @After - public void cleanUp() throws Exception { - if(!executorService.awaitTermination(2, SECONDS)) { - executorService.shutdown(); - } - - if (db != null && !db.isClosed()) { - db.close(); - } - - mockRepository.reset(); - if (Files.exists(Paths.get(dbFile))) { - Files.delete(Paths.get(dbFile)); - } - server.stop(); - } - - @Test - public void testSingleUserSingleReplica() { - mockRepository.getUserMap().put("anidotnet", "abcd"); - - db = createDb(dbFile); - NitriteCollection collection = db.getCollection("testSingleUserSingleReplica"); - Document document = createDocument() - .put("firstName", "Anindya") - .put("lastName", "Chatterjee") - .put("address", createDocument("street", "1234 Abcd Street") - .put("pin", 123456)); - collection.insert(document); - - Replica replica = Replica.builder() - .of(collection) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - - replica.connect(); - - await().atMost(5, SECONDS).until(() -> mockRepository.getCollectionReplicaMap().size() == 1); - assertEquals(mockRepository.getUserReplicaMap().size(), 1); - assertTrue(mockRepository.getUserReplicaMap().containsKey("anidotnet")); - assertTrue(mockRepository.getCollectionReplicaMap().containsKey("anidotnet@testSingleUserSingleReplica")); - ServerLastWriteWinMap lastWriteWinMap = mockRepository.getReplicaStore().get("anidotnet@testSingleUserSingleReplica"); - - await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().find().size() == 1); - Document doc = lastWriteWinMap.getCollection().find(where("firstName").eq("Anindya")).firstOrNull(); - - assertTrue(isSimilar(document, doc, "firstName", "lastName", "address", "pin")); - - collection.remove(doc); - await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().size() == 0); - doc = lastWriteWinMap.getCollection().find(where("firstName").eq("Anindya")).firstOrNull(); - assertNull(doc); - assertEquals(collection.size(), 0); - - collection.insert(document); - await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().size() == 1); - doc = lastWriteWinMap.getCollection().find(where("firstName").eq("Anindya")).firstOrNull(); - assertTrue(isSimilar(document, doc, "firstName", "lastName", "address", "pin")); - - replica.disconnect(); - collection.remove(doc); - await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().size() == 1); - doc = lastWriteWinMap.getCollection().find(where("firstName").eq("Anindya")).firstOrNull(); - assertTrue(isSimilar(document, doc, "firstName", "lastName", "address", "pin")); - - replica.connect(); - await().atMost(5, SECONDS).until(() -> lastWriteWinMap.getCollection().size() == 0); - doc = lastWriteWinMap.getCollection().find(where("firstName").eq("Anindya")).firstOrNull(); - assertNull(doc); - - replica.disconnectNow(); - } - - @Test - public void testSingleUserMultiReplica() { - mockRepository.getUserMap().put("anidotnet", "abcd"); - - db = createDb(dbFile); - - Nitrite db2 = createDb(); - - NitriteCollection c1 = db.getCollection("testSingleUserMultiReplica"); - NitriteCollection c2 = db2.getCollection("testSingleUserMultiReplica"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .replicaName("r1") - .create(); - - Replica r2 = Replica.builder() - .of(c2) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .replicaName("r2") - .create(); - - r1.connect(); - - executorService.submit(() -> { - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - }); - - await().atMost(5, SECONDS).until(() -> c1.size() == 10); - assertEquals(c2.size(), 0); - - r2.connect(); - await().atMost(5, SECONDS).until(() -> c2.size() == 10); - - Random random = new Random(); - executorService.submit(() -> { - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - - executorService.submit(() -> { - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - - - await().atMost(10, SECONDS).until(() -> c1.size() == 40); - assertEquals(c2.size(), 40); - - r1.disconnect(); - - executorService.submit(() -> { - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - - executorService.submit(() -> { - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - - r1.connect(); - await().atMost(10, SECONDS).until(() -> c1.size() == 70 && c2.size() == 70); - TestUtils.assertEquals(c1, c2); - - executorService.submit(() -> { - c2.remove(Filter.ALL); - }); - - await().atMost(10, SECONDS).until(() -> c2.size() == 0); - - await().atMost(10, SECONDS).until(() -> c1.size() == 0); - TestUtils.assertEquals(c1, c2); - - r1.disconnectNow(); - r2.disconnectNow(); - } - - @Test - public void testMultiUserSingleReplica() { - mockRepository.getUserMap().put("user1", "abcd"); - mockRepository.getUserMap().put("user2", "abcd"); - mockRepository.getUserMap().put("user3", "abcd"); - - Nitrite db1 = createDb(); - NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica"); - - Nitrite db2 = createDb(); - NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica"); - - Nitrite db3 = createDb(); - NitriteCollection c3 = db3.getCollection("testMultiUserSingleReplica"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("user1", "abcd") - .create(); - r1.connect(); - - Replica r2 = Replica.builder() - .of(c2) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("user2", "abcd") - .create(); - r2.connect(); - - Replica r3 = Replica.builder() - .of(c3) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("user3", "abcd") - .create(); - r3.connect(); - - executorService.submit(() -> { - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - }); - - executorService.submit(() -> { - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - } - }); - - executorService.submit(() -> { - for (int i = 0; i < 30; i++) { - Document document = randomDocument(); - c3.insert(document); - } - }); - - await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20 && c3.size() == 30); - - TestUtils.assertNotEquals(c1, c2); - TestUtils.assertNotEquals(c1, c3); - TestUtils.assertNotEquals(c2, c3); - - r1.disconnectNow(); - r2.disconnectNow(); - r3.disconnectNow(); - } - - @Test - public void testMultiUserMultiReplica() { - mockRepository.getUserMap().put("user1", "abcd"); - mockRepository.getUserMap().put("user2", "abcd"); - - Nitrite db1 = createDb(); - NitriteCollection c1 = db1.getCollection("testMultiUserSingleReplica1"); - - Nitrite db2 = createDb(); - NitriteCollection c2 = db2.getCollection("testMultiUserSingleReplica2"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .tenant("junit-test") - .jwtAuth("user1", "abcd") - .create(); - r1.connect(); - - Replica r2 = Replica.builder() - .of(c2) - .remoteHost("127.0.0.1") - .tenant("junit-test") - .jwtAuth("user2", "abcd") - .create(); - r2.connect(); - - executorService.submit(() -> { - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - }); - - executorService.submit(() -> { - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - } - }); - - await().atMost(5, SECONDS).until(() -> c1.size() == 10 && c2.size() == 20); - - TestUtils.assertNotEquals(c1, c2); - r1.disconnectNow(); - r2.disconnectNow(); - } - - @Test - public void testSecurityInCorrectCredentials() { - mockRepository.getUserMap().put("user", "abcd"); - - Nitrite db1 = createDb(); - NitriteCollection c1 = db1.getCollection("testSecurity"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .tenant("junit-test") - .jwtAuth("user", "wrong_token") - .create(); - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - assertEquals(c1.size(), 10); - await().atMost(5, SECONDS).until(() -> r1.isDisconnected()); - r1.disconnectNow(); - } - - @Test - public void testCloseDbAndReconnect() { - mockRepository.getUserMap().put("anidotnet", "abcd"); - - db = createDb(dbFile); - - Nitrite db2 = createDb(); - - NitriteCollection c1 = db.getCollection("testCloseDbAndReconnect"); - NitriteCollection c2 = db2.getCollection("testCloseDbAndReconnect"); - - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - - Replica r2 = Replica.builder() - .of(c2) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - NitriteCollection finalC1 = c1; - await().atMost(5, SECONDS).until(() -> finalC1.size() == 10); - assertEquals(c2.size(), 0); - - r2.connect(); - await().atMost(5, SECONDS).until(() -> c2.size() == 10); - - Random random = new Random(); - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - executorService.submit(() -> { - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - - NitriteCollection finalC2 = c1; - await().atMost(10, SECONDS).until(() -> finalC2.size() == 40); - assertEquals(c2.size(), 40); - - r1.disconnect(); - r1.close(); - db.close(); - - db = createDb(dbFile); - c1 = db.getCollection("testCloseDbAndReconnect"); - r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - executorService.submit(() -> { - for (int i = 0; i < 20; i++) { - Document document = randomDocument(); - c2.insert(document); - try { - Thread.sleep(random.nextInt(100)); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - - r1.connect(); - NitriteCollection finalC = c1; - await().atMost(10, SECONDS).until(() -> finalC.size() == 70 && c2.size() == 70); - TestUtils.assertEquals(c1, c2); - - executorService.submit(() -> { - c2.remove(Filter.ALL); - }); - - await().atMost(10, SECONDS).until(() -> c2.size() == 0); - await().atMost(5, SECONDS).until(() -> finalC.size() == 0); - TestUtils.assertEquals(c1, c2); - - r1.disconnectNow(); - r2.disconnectNow(); - } - - @Test - public void testDelayedConnect() { - mockRepository.getUserMap().put("anidotnet", "abcd"); - - Nitrite db1 = createDb(dbFile); - NitriteCollection c1 = db1.getCollection("testDelayedConnect"); - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - await().atMost(5, SECONDS).until(() -> c1.size() == 10); - - r1.disconnect(); - r1.close(); - db1.close(); - - Nitrite db2 = createDb(); - NitriteCollection c2 = db2.getCollection("testDelayedConnect"); - Replica r2 = Replica.builder() - .of(c2) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - r2.connect(); - await().atMost(5, SECONDS).until(() -> c2.size() == 10); - - r1.disconnectNow(); - r2.disconnectNow(); - } - - @Test - public void testDelayedConnectRemoveAll() { - mockRepository.getUserMap().put("anidotnet", "abcd"); - - db = createDb(dbFile); - NitriteCollection c1 = db.getCollection("testDelayedConnect"); - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .replicaName("r1") - .create(); - - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - - c1.remove(Filter.ALL); - assertEquals(c1.size(), 0); - - r1.disconnect(); - r1.close(); - db.close(); - - Nitrite db2 = createDb(); - NitriteCollection c2 = db2.getCollection("testDelayedConnect"); - Replica r2 = Replica.builder() - .of(c2) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .replicaName("r2") - .create(); - - r2.connect(); - - for (int i = 0; i < 5; i++) { - Document document = randomDocument(); - c2.insert(document); - } - - db = createDb(dbFile); - NitriteCollection c3 = db.getCollection("testDelayedConnect"); - r1 = Replica.builder() - .of(c3) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .replicaName("r1") - .create(); - - r1.connect(); - - await().atMost(5, SECONDS).until(() -> { - List l1 = c3.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); - List l2 = c2.find().toList().stream().map(TestUtils::trimMeta).collect(Collectors.toList()); - return l1.equals(l2); - }); - - r1.disconnectNow(); - r2.disconnectNow(); - } - - @Test - public void testGarbageCollect() { - mockRepository.getUserMap().put("anidotnet", "abcd"); - db = createDb(dbFile); - NitriteCollection c1 = db.getCollection("testGarbageCollect"); - Replica r1 = Replica.builder() - .of(c1) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .create(); - - r1.connect(); - - for (int i = 0; i < 10; i++) { - Document document = randomDocument(); - c1.insert(document); - } - await().atMost(5, SECONDS).until(() -> c1.size() == 10); - - c1.remove(Filter.ALL); - assertEquals(c1.size(), 0); - -// final ConflictFreeReplicatedDataType lastWriteWinMap = getCrdt(r1); -// await().atMost(5, SECONDS).until(() -> c1.size() == 0 -// && lastWriteWinMap.getTombstoneMap().size() == 10); - - r1.disconnect(); - r1.close(); - db.close(); - - mockRepository.setGcTtl(1000L); - - db = createDb(dbFile); - NitriteCollection c2 = db.getCollection("testGarbageCollect"); - r1 = Replica.builder() - .of(c2) - .remoteHost("127.0.0.1") - .remotePort(46005) - .tenant("junit-test") - .jwtAuth("anidotnet", "abcd") - .replicaName("r1") - .create(); - - r1.connect(); - -// final LastWriteWinMap finalLastWriteWinMap = getCrdt(r1); -// await().atMost(5, SECONDS).until(() -> c2.size() == 0 -// && finalLastWriteWinMap.getTombstoneMap().size() == 0); - - r1.disconnectNow(); - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java deleted file mode 100644 index e0ba2af47..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateEndpoint.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.mock.server; - -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.websocket.*; -import jakarta.websocket.server.PathParam; -import jakarta.websocket.server.ServerEndpoint; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.store.NitriteMap; -import org.dizitart.no2.sync.FeedLedger; -import org.dizitart.no2.sync.MessageFactory; -import org.dizitart.no2.sync.MessageTransformer; -import org.dizitart.no2.sync.ReplicationException; -import org.dizitart.no2.sync.crdt.DeltaStates; -import org.dizitart.no2.sync.crdt.Timestamps; -import org.dizitart.no2.sync.crdt.Tombstone; -import org.dizitart.no2.sync.message.*; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - -/** - * @author Anindya Chatterjee - */ -@Slf4j -@Data -@ServerEndpoint(value = "/ws/datagate/{tenant}/{collection}/{user}") -public class MockDataGateEndpoint { - private ObjectMapper objectMapper; - private MockRepository mockRepository; - private MessageFactory factory; - private MessageTransformer transformer; - - public MockDataGateEndpoint() { - objectMapper = new ObjectMapper(); - mockRepository = MockRepository.getInstance(); - factory = new MessageFactory(); - transformer = new MessageTransformer(objectMapper); - } - - @OnOpen - public void onOpen(@PathParam("user") String user, - @PathParam("tenant") String tenant, - @PathParam("collection") String collection, - Session session) { - log.info("DataGate server connection established"); - session.getUserProperties().put("user", user); - session.getUserProperties().put("tenant", tenant); - session.getUserProperties().put("collection", user + "@" + collection); - session.getUserProperties().put("authorized", false); - } - - @OnClose - public void onClose(CloseReason reason, Session session) { - log.warn("DataGate server closed due to {}", reason.getReasonPhrase()); - mockRepository.getAuthorizedSessions().remove(session); - } - - @OnMessage - public void onMessage(String message, Session session) throws IOException { - try { - log.info("Message received at server {}", message); - DataGateMessage dataGateMessage = transformer.transform(message); - if (dataGateMessage instanceof Connect) { - Connect connect = (Connect) dataGateMessage; - handleConnect(session, connect); - } else if (dataGateMessage instanceof BatchChangeStart) { - checkAuthorized(session); - BatchChangeStart batchChangeStart = (BatchChangeStart) dataGateMessage; - handleBatchChangeStart(session, batchChangeStart); - } else if (dataGateMessage instanceof BatchChangeContinue) { - checkAuthorized(session); - BatchChangeContinue batchChangeContinue = (BatchChangeContinue) dataGateMessage; - handleBatchChangeContinue(session, batchChangeContinue); - } else if (dataGateMessage instanceof BatchChangeEnd) { - checkAuthorized(session); - BatchChangeEnd batchChangeEnd = (BatchChangeEnd) dataGateMessage; - handleBatchChangeEnd(session, batchChangeEnd); - } else if (dataGateMessage instanceof DataGateFeed) { - checkAuthorized(session); - DataGateFeed dataGateFeed = (DataGateFeed) dataGateMessage; - handleDataGateFeed(session, dataGateFeed); - } else if (dataGateMessage instanceof BatchAck) { - checkAuthorized(session); - BatchAck batchAck = (BatchAck) dataGateMessage; - handleBatchAck(session, batchAck); - } else if (dataGateMessage instanceof BatchEndAck) { - checkAuthorized(session); - BatchEndAck batchEndAck = (BatchEndAck) dataGateMessage; - handleBatchEndAck(session, batchEndAck); - } else if (dataGateMessage instanceof Disconnect) { - checkAuthorized(session); - Disconnect disconnect = (Disconnect) dataGateMessage; - handleDisconnect(session, disconnect); - } - } catch (Exception e) { - log.error("Error while handling message {}", message, e); - e.printStackTrace(); - sendErrorMessage(session, e); - } - } - - @OnError - public void onError(Session session, Throwable ex) { - log.error("Error in DataGate server", ex); - - try { - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setHeader(createHeader(session, MessageType.Error, null, null, - mockRepository.getServerId(), "")); - errorMessage.setError(ex.getMessage()); - String message = objectMapper.writeValueAsString(errorMessage); - session.getBasicRemote().sendText(message); - } catch (Exception e) { - throw new ReplicationException("failed to send ErrorMessage", e, false); - } - } - - protected void handleConnect(Session session, Connect connect) throws IOException { - String replicaId = connect.getHeader().getOrigin(); - String userName = connect.getHeader().getUserName(); - String collection = userName + "@" + connect.getHeader().getCollection(); - - if (isValidAuth(userName, connect.getAuthToken())) { - session.getUserProperties().put("authorized", true); - session.getUserProperties().put("collection", collection); - session.getUserProperties().put("replica", replicaId); - - mockRepository.getAuthorizedSessions().add(session); - - List replicas; - if (mockRepository.getCollectionReplicaMap().containsKey(collection)) { - replicas = mockRepository.getCollectionReplicaMap().get(collection); - if (!replicas.contains(replicaId)) { - replicas.add(replicaId); - } - } else { - replicas = new ArrayList<>(); - replicas.add(replicaId); - } - mockRepository.getCollectionReplicaMap().put(collection, replicas); - - if (mockRepository.getUserReplicaMap().containsKey(userName)) { - replicas = mockRepository.getUserReplicaMap().get(userName); - if (!replicas.contains(replicaId)) { - replicas.add(replicaId); - } - } else { - replicas = new ArrayList<>(); - replicas.add(replicaId); - } - mockRepository.getUserReplicaMap().put(userName, replicas); - - if (!mockRepository.getReplicaStore().containsKey(collection)) { - ServerLastWriteWinMap replica = createCrdt(collection); - mockRepository.getReplicaStore().put(collection, replica); - } - - ConnectAck ack = new ConnectAck(); - ack.setHeader(createHeader(session, MessageType.ConnectAck, - connect.getHeader().getCollection(), userName, - mockRepository.getServerId(), connect.getHeader().getTransactionId())); - ack.setTombstoneTtl(mockRepository.getGcTtl()); - ack.setNextOffset(0); - ack.setBatchSize(0); - - String message = objectMapper.writeValueAsString(ack); - session.getBasicRemote().sendText(message); - } else { - session.getUserProperties().put("authorized", false); - ErrorMessage errorMessage = new ErrorMessage(); - errorMessage.setError("Unauthorized"); - errorMessage.setHeader(createHeader(session, MessageType.Error, - connect.getHeader().getCollection(), userName, - mockRepository.getServerId(), connect.getHeader().getTransactionId())); - String message = objectMapper.writeValueAsString(errorMessage); - session.getBasicRemote().sendText(message); - } - } - - protected void handleBatchChangeStart(Session session, BatchChangeStart batchChangeStart) throws IOException { - DataGateFeed feed = new DataGateFeed(); - - String userName = batchChangeStart.getHeader().getUserName(); - String collection = userName + "@" + batchChangeStart.getHeader().getCollection(); - String replicaId = batchChangeStart.getHeader().getOrigin(); - ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - replica.merge(batchChangeStart.getFeed(), System.currentTimeMillis()); - - feed.setHeader(createHeader(session, MessageType.DataGateFeed, batchChangeStart.getHeader().getCollection(), - userName, replicaId, batchChangeStart.getHeader().getTransactionId())); - feed.setFeed(batchChangeStart.getFeed()); - - BatchAck ack = new BatchAck(); - ack.setReceipt(feed.calculateReceipt()); - ack.setHeader(createHeader(session, MessageType.BatchAck, batchChangeStart.getHeader().getCollection(), - userName, mockRepository.getServerId(), batchChangeStart.getHeader().getTransactionId())); - ack.setStartTime(batchChangeStart.getStartTime()); - ack.setEndTime(batchChangeStart.getEndTime()); - ack.setNextOffset(batchChangeStart.getNextOffset()); - ack.setBatchSize(batchChangeStart.getBatchSize()); - - String message = objectMapper.writeValueAsString(ack); - session.getBasicRemote().sendText(message); - } - - protected void handleBatchChangeContinue(Session session, BatchChangeContinue batchChangeContinue) throws IOException { - DataGateFeed feed = new DataGateFeed(); - - String userName = batchChangeContinue.getHeader().getUserName(); - String collection = userName + "@" + batchChangeContinue.getHeader().getCollection(); - String replicaId = batchChangeContinue.getHeader().getOrigin(); - ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - replica.merge(batchChangeContinue.getFeed(), System.currentTimeMillis()); - - feed.setHeader(createHeader(session, MessageType.DataGateFeed, batchChangeContinue.getHeader().getCollection(), - userName, replicaId, batchChangeContinue.getHeader().getTransactionId())); - feed.setFeed(batchChangeContinue.getFeed()); - - BatchAck ack = new BatchAck(); - ack.setReceipt(feed.calculateReceipt()); - ack.setHeader(createHeader(session, MessageType.BatchAck, batchChangeContinue.getHeader().getCollection(), - userName, mockRepository.getServerId(), batchChangeContinue.getHeader().getTransactionId())); - ack.setStartTime(batchChangeContinue.getStartTime()); - ack.setEndTime(batchChangeContinue.getEndTime()); - ack.setNextOffset(batchChangeContinue.getNextOffset()); - ack.setBatchSize(batchChangeContinue.getBatchSize()); - - String message = objectMapper.writeValueAsString(ack); - session.getBasicRemote().sendText(message); - } - - protected void handleBatchChangeEnd(Session session, BatchChangeEnd batchChangeEnd) throws IOException { - Integer batchSize = batchChangeEnd.getBatchSize(); - String userName = batchChangeEnd.getHeader().getUserName(); - String collection = userName + "@" + batchChangeEnd.getHeader().getCollection(); - - BatchEndAck ack = new BatchEndAck(); - ack.setHeader(createHeader(session, MessageType.BatchEndAck, batchChangeEnd.getHeader().getCollection(), - userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getTransactionId())); - ack.setStartTime(batchChangeEnd.getStartTime()); - ack.setEndTime(batchChangeEnd.getEndTime()); - - String message = objectMapper.writeValueAsString(ack); - session.getBasicRemote().sendText(message); - - ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - Timestamps startTime = batchChangeEnd.getStartTime(); -// Timestamps endTime = replica.getLastModifiedTime(); - - DeltaStates changesSince = replica.getChangesSince(null, - null, 0, batchSize); - - - BatchChangeStart batchChangeStart = new BatchChangeStart(); - batchChangeStart.setHeader(createHeader(session, MessageType.BatchChangeStart, - collection, userName, mockRepository.getServerId(), batchChangeEnd.getHeader().getTransactionId())); - batchChangeStart.setStartTime(batchChangeEnd.getStartTime()); - batchChangeStart.setEndTime(batchChangeEnd.getEndTime()); - batchChangeStart.setFeed(changesSince); - batchChangeStart.setNextOffset(batchSize); - batchChangeStart.setBatchSize(batchSize); - - session.getBasicRemote().sendText(objectMapper.writeValueAsString(batchChangeStart)); - } - - protected void handleBatchAck(Session session, BatchAck batchAck) throws IOException { - String userName = batchAck.getHeader().getUserName(); - String collection = userName + "@" + batchAck.getHeader().getCollection(); - Integer offset = batchAck.getNextOffset(); - - Receipt receipt = batchAck.getReceipt(); - FeedLedger feedLedger = mockRepository.getFeedLedgerMap().get(collection); - if (feedLedger != null) { - feedLedger.writeOff(receipt); - mockRepository.getFeedLedgerMap().put(collection, feedLedger); - } - - ServerLastWriteWinMap replica = mockRepository.getReplicaStore().get(collection); - DeltaStates changesSince = replica.getChangesSince(null, - null, offset, batchAck.getBatchSize()); - - boolean hasMore = !(changesSince.getChangeSet().size() == 0 && changesSince.getTombstoneMap().size() == 0); - if (hasMore) { - BatchChangeContinue message = new BatchChangeContinue(); - message.setHeader(createHeader(session, MessageType.BatchChangeContinue, - collection, userName, mockRepository.getServerId(), batchAck.getHeader().getTransactionId())); - message.setFeed(changesSince); - message.setStartTime(batchAck.getStartTime()); - message.setEndTime(batchAck.getEndTime()); - message.setNextOffset(batchAck.getNextOffset() + batchAck.getBatchSize()); - message.setBatchSize(batchAck.getBatchSize()); - - session.getBasicRemote().sendText(objectMapper.writeValueAsString(message)); - } else { - BatchChangeEnd message = new BatchChangeEnd(); - message.setHeader(createHeader(session, MessageType.BatchChangeEnd, - collection, userName, mockRepository.getServerId(), batchAck.getHeader().getTransactionId())); - message.setStartTime(batchAck.getStartTime()); - message.setEndTime(batchAck.getEndTime()); - message.setNextOffset(batchAck.getNextOffset()); - message.setBatchSize(batchAck.getBatchSize()); - - session.getBasicRemote().sendText(objectMapper.writeValueAsString(message)); - } - } - - protected void handleDisconnect(Session session, Disconnect connect) { - String replicaId = connect.getHeader().getOrigin(); - String userName = connect.getHeader().getUserName(); - String collection = userName + "@" + connect.getHeader().getCollection(); - - mockRepository.getCollectionReplicaMap().get(collection).remove(replicaId); - mockRepository.getUserReplicaMap().get(userName).remove(replicaId); - mockRepository.getAuthorizedSessions().remove(session); - } - - protected void handleDataGateFeed(Session channel, DataGateFeed feed) { - - } - - protected void handleBatchEndAck(Session session, BatchEndAck batchEndAck) throws IOException { - // send disconnect - Disconnect disconnect = new Disconnect(); - String user = (String) session.getUserProperties().get("user"); - String collection = (String) session.getUserProperties().get("collection"); - - disconnect.setHeader(createHeader(session, MessageType.Disconnect, collection, user, - mockRepository.getServerId(), batchEndAck.getHeader().getTransactionId())); - session.getBasicRemote().sendText(objectMapper.writeValueAsString(disconnect)); - - mockRepository.getCollectionReplicaMap().get(collection).remove(batchEndAck.getHeader().getOrigin()); - mockRepository.getUserReplicaMap().get(user).remove(batchEndAck.getHeader().getOrigin()); - mockRepository.getAuthorizedSessions().remove(session); - } - - private ServerLastWriteWinMap createCrdt(String collection) { - NitriteCollection nc = mockRepository.getDb().getCollection(collection); - NitriteMap nitriteMap = - mockRepository.getDb().getConfig().getNitriteStore().openMap(collection + "-replica", - NitriteId.class, Tombstone.class); - return new ServerLastWriteWinMap(nc, nitriteMap); - } - - private void sendErrorMessage(Session session, Throwable error) throws IOException { - ErrorMessage errorMessage = new ErrorMessage(); - String user = (String) session.getUserProperties().get("user"); - String collection = (String) session.getUserProperties().get("collection"); - - errorMessage.setHeader(createHeader(session, MessageType.Error, collection, user, - mockRepository.getServerId(), "")); - errorMessage.setError(error.getMessage()); - session.getBasicRemote().sendText(objectMapper.writeValueAsString(errorMessage)); - } - - - private MessageHeader createHeader(Session session, MessageType messageType, String collection, - String userName, String origin, String correlationId) { - MessageHeader messageHeader = new MessageHeader(); - messageHeader.setId(UUID.randomUUID().toString()); - messageHeader.setTransactionId(correlationId); - messageHeader.setCollection(collection); - messageHeader.setMessageType(messageType); - messageHeader.setOrigin(origin); - messageHeader.setTimestamp(System.currentTimeMillis()); - messageHeader.setUserName(userName); - messageHeader.setTenant((String) session.getUserProperties().get("tenant")); - return messageHeader; - } - - private boolean isValidAuth(String userName, String authToken) { - if (mockRepository.getUserMap().containsKey(userName)) { - return mockRepository.getUserMap().get(userName).equals(authToken); - } - return false; - } - - private void checkAuthorized(Session session) { - if (!(boolean) session.getUserProperties().get("authorized")) { - throw new SecurityException("session is not authorized"); - } - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateServer.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateServer.java deleted file mode 100644 index 557c10936..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockDataGateServer.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.mock.server; - - -import org.glassfish.tyrus.server.Server; - -/** - * @author Anindya Chatterjee - */ -public class MockDataGateServer { - private final int port; - private Server server; - private final MockRepository mockRepository; - - public MockDataGateServer(int port) { - this.port = port; - this.mockRepository = MockRepository.getInstance(); - } - - public void start() throws Exception { - server = new Server("127.0.0.1", port, "", null, MockDataGateEndpoint.class); - server.start(); - Runtime.getRuntime().addShutdownHook(new Thread(() -> server.stop())); - } - - public void stop() { - server.stop(); - mockRepository.reset(); - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java deleted file mode 100644 index c62f774a0..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/MockRepository.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.mock.server; - -import jakarta.websocket.Session; -import lombok.Data; -import org.dizitart.no2.Nitrite; -import org.dizitart.no2.sync.FeedLedger; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -import static org.dizitart.no2.TestUtils.createDb; - -/** - * @author Anindya Chatterjee - */ -@Data -public class MockRepository { - private static MockRepository instance = new MockRepository(); - private Map> collectionReplicaMap; - private Map> userReplicaMap; - private Map replicaStore; - private Map feedLedgerMap; - private Nitrite db; - private String serverId; - private Set authorizedSessions; - private Map userMap; - private Long gcTtl; - - private MockRepository() { - collectionReplicaMap = new ConcurrentHashMap<>(); - userReplicaMap = new ConcurrentHashMap<>(); - replicaStore = new ConcurrentHashMap<>(); - authorizedSessions = new HashSet<>(); - userMap = new ConcurrentHashMap<>(); - feedLedgerMap = new ConcurrentHashMap<>(); - db = createDb(); - serverId = UUID.randomUUID().toString(); - gcTtl = 0L; - } - - public static MockRepository getInstance() { - return instance; - } - - public void reset() { - instance = new MockRepository(); - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java b/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java deleted file mode 100644 index 1adc6cee4..000000000 --- a/nitrite-replication/src/test/java/org/dizitart/no2/mock/server/ServerLastWriteWinMap.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.mock.server; - -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.DocumentCursor; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.common.streams.BoundedStream; -import org.dizitart.no2.common.tuples.Pair; -import org.dizitart.no2.store.NitriteMap; -import org.dizitart.no2.sync.crdt.DeltaStates; -import org.dizitart.no2.sync.crdt.Tombstone; - -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.dizitart.no2.collection.FindOptions.skipBy; -import static org.dizitart.no2.common.Constants.*; -import static org.dizitart.no2.filters.Filter.and; -import static org.dizitart.no2.filters.FluentFilter.where; - -/** - * @author Anindya Chatterjee - */ -@Data -@Slf4j -public class ServerLastWriteWinMap { - private NitriteCollection collection; - private NitriteMap tombstoneMap; - - public ServerLastWriteWinMap(NitriteCollection collection, NitriteMap tombstoneMap) { - this.collection = collection; - this.tombstoneMap = tombstoneMap; - } - - public void merge(DeltaStates snapshot, Long syncTime) { - if (snapshot.getChangeSet() != null) { - for (Document entry : snapshot.getChangeSet()) { - put(entry); - } - } - - if (snapshot.getTombstoneMap() != null) { - for (Map.Entry entry : snapshot.getTombstoneMap().entrySet()) { - remove(NitriteId.createId(entry.getKey()), entry.getValue(), syncTime); - } - } - } - - public DeltaStates getChangesSince(Long startTime, Long endTime, int offset, int size) { - DeltaStates state = new DeltaStates(); - - // send tombstone info - BoundedStream stream - = new BoundedStream<>((long) offset, (long) size, tombstoneMap.entries()); - - for (Pair entry : stream) { - Long syncTimestamp = entry.getSecond().getSyncTimestamp(); - if (syncTimestamp > startTime && syncTimestamp <= endTime) { - state.getTombstoneMap().put(entry.getFirst().getIdValue(), - entry.getSecond().getDeleteTimestamp()); - } - } - - DocumentCursor cursor = collection.find( - and( - where("_synced").gt(startTime), - where("_synced").lte(endTime) - ), skipBy(offset).limit(size)); - - Set documents = cursor.toSet().stream() - .peek(document -> document.remove("_synced")) - .collect(Collectors.toSet()); - - state.getChangeSet().addAll(documents); - - return state; - } - - private void put(Document value) { - if (value != null) { - value.put("_synced", System.currentTimeMillis()); - NitriteId key = value.getId(); - - Document entry = collection.getById(key); - if (entry == null) { - if (tombstoneMap.containsKey(key)) { - Long tombstoneTime = tombstoneMap.get(key).getDeleteTimestamp(); - Long docModifiedTime = value.getLastModifiedSinceEpoch(); - - if (docModifiedTime >= tombstoneTime) { - value.put(DOC_SOURCE, REPLICATOR); - collection.insert(value); - tombstoneMap.remove(key); - } - } else { - value.put(DOC_SOURCE, REPLICATOR); - collection.insert(value); - } - } else { - Long oldTime = entry.getLastModifiedSinceEpoch(); - Long newTime = value.getLastModifiedSinceEpoch(); - - if (newTime > oldTime) { - entry.put(DOC_SOURCE, REPLICATOR); - collection.remove(entry); - - value.put(DOC_SOURCE, REPLICATOR); - collection.insert(value); - } - } - } - } - - private void remove(NitriteId key, long timestamp, long syncTime) { - Document entry = collection.getById(key); - if (entry != null) { - entry.put(DOC_SOURCE, REPLICATOR); - collection.remove(entry); - Tombstone tombstone = new Tombstone(); - tombstone.setNitriteId(key); - tombstone.setDeleteTimestamp(timestamp); - tombstone.setSyncTimestamp(syncTime); - tombstoneMap.put(key, tombstone); - } else { - log.debug("No entry found to remove"); - } - } -} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/DeltaStatesTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/DeltaStatesTest.java index 316074ed1..586d4f110 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/DeltaStatesTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/sync/crdt/DeltaStatesTest.java @@ -7,7 +7,7 @@ public class DeltaStatesTest { @Test public void testConstructor() { - assertEquals("LastWriteWinState(changeSet=[], tombstoneMap={})", (new DeltaStates()).toString()); + assertEquals("DeltaStates(changeSet=[], tombstoneMap={})", (new DeltaStates()).toString()); } } diff --git a/nitrite-replication/src/test/resources/logback.xml b/nitrite-replication/src/test/resources/logback.xml index 622f16c30..f182a78c7 100644 --- a/nitrite-replication/src/test/resources/logback.xml +++ b/nitrite-replication/src/test/resources/logback.xml @@ -21,21 +21,10 @@ - - tests.log - true - - %-4relative [%thread] %-5level %logger{35} - %msg%n - - - - - - \ No newline at end of file diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java b/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java index 875c36f08..9a654648e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java @@ -71,24 +71,29 @@ public class Attributes implements Serializable { public static final String TOMBSTONE = "tombstone"; /** - * The constant LAST_SYNCED. + * The constant FEED_LEDGER. */ - public static final String LOCAL_COLLECTION_SYNCED_TIME = "local_collection_synced_at"; + public static final String FEED_LEDGER = "feed_ledger"; /** - * The constant REMOTE_COLLECTION_SYNCED_TIME. + * The constant LOCAL_COLLECTION_MARKER. */ - public static final String REMOTE_COLLECTION_SYNCED_TIME = "remote_collection_synced_at"; + public static final String LOCAL_COLLECTION_MARKER = "local_collection_marker"; /** - * The constant TOMBSTONE_SYNC_TIME. + * The constant REMOTE_COLLECTION_MARKER. */ - public static final String LOCAL_TOMBSTONE_SYNCED_TIME = "local_tombstone_synced_at"; + public static final String REMOTE_COLLECTION_MARKER = "remote_collection_marker"; /** - * The constant REMOTE_TOMBSTONE_SYNC_TIME. + * The constant LOCAL_TOMBSTONE_MARKER. */ - public static final String REMOTE_TOMBSTONE_SYNCED_TIME = "remote_tombstone_synced_at"; + public static final String LOCAL_TOMBSTONE_MARKER = "local_tombstone_marker"; + + /** + * The constant REMOTE_TOMBSTONE_MARKER. + */ + public static final String REMOTE_TOMBSTONE_MARKER = "remote_tombstone_marker"; /** * The constant REPLICA. diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java index ca0727f86..5294a0346 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java @@ -266,13 +266,13 @@ private CollectionEventInfo removeAndCreateEvent(Document document, Wr NitriteId nitriteId = document.getId(); document = nitriteMap.remove(nitriteId); if (document != null) { - long time = System.currentTimeMillis(); + long removedAt = System.currentTimeMillis(); documentIndexWriter.removeIndexEntry(document); writeResult.addToList(nitriteId); int rev = document.getRevision(); document.put(DOC_REVISION, rev + 1); - document.put(DOC_MODIFIED, time); + document.put(DOC_MODIFIED, removedAt); log.debug("Document removed {} from {}", document, nitriteMap.getName()); @@ -280,7 +280,7 @@ private CollectionEventInfo removeAndCreateEvent(Document document, Wr Document eventDoc = document.clone(); eventInfo.setItem(eventDoc); eventInfo.setEventType(EventType.Remove); - eventInfo.setTimestamp(time); + eventInfo.setTimestamp(removedAt); return eventInfo; } return null; From 79a36e1360e1bd4a122597d211f1ddac79ae70c4 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 4 Oct 2021 15:52:52 +0530 Subject: [PATCH 22/78] per app datagate config changes --- .../integration/DataGateIntegrationTest.java | 1 + .../src/test/resources/mongo-seed/Dockerfile | 3 +- .../test/resources/mongo-seed/appConfig.json | 102 ++++++++++-------- .../resources/mongo-seed/serverConfig.json | 29 +++++ 4 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 nitrite-replication/src/test/resources/mongo-seed/serverConfig.json diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index 0b9f02723..1ec0af948 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -79,6 +79,7 @@ public void setUp() throws Exception { mongoSeed = new GenericContainer<>(new ImageFromDockerfile() .withFileFromClasspath("appConfig.json", "mongo-seed/appConfig.json") + .withFileFromClasspath("serverConfig.json", "mongo-seed/serverConfig.json") .withFileFromClasspath("Dockerfile", "mongo-seed/Dockerfile")) .withNetwork(network); diff --git a/nitrite-replication/src/test/resources/mongo-seed/Dockerfile b/nitrite-replication/src/test/resources/mongo-seed/Dockerfile index 185131e7c..0c9977d91 100644 --- a/nitrite-replication/src/test/resources/mongo-seed/Dockerfile +++ b/nitrite-replication/src/test/resources/mongo-seed/Dockerfile @@ -2,4 +2,5 @@ FROM mongo COPY appConfig.json /appConfig.json -CMD mongoimport --host mongo --db datagate --collection appConfig --drop --type json --file /appConfig.json --jsonArray \ No newline at end of file +CMD mongoimport --host mongo --db datagate --collection serverConfigs --drop --type json --file /serverConfig.json --jsonArray +CMD mongoimport --host mongo --db datagate --collection appConfigs --drop --type json --file /appConfig.json --jsonArray \ No newline at end of file diff --git a/nitrite-replication/src/test/resources/mongo-seed/appConfig.json b/nitrite-replication/src/test/resources/mongo-seed/appConfig.json index a9fe0dc62..e1904dc67 100644 --- a/nitrite-replication/src/test/resources/mongo-seed/appConfig.json +++ b/nitrite-replication/src/test/resources/mongo-seed/appConfig.json @@ -1,46 +1,62 @@ [ - { - "_id": "appConfig", - "debug_message": true, - "tombstone_ttl": 180, - "alert_config": { - "log_format": "text", - "response_time_threshold": 20, - "sentry_dsn": "", - "enable_syslog": false, - "syslog_address": "", - "slack_channel": "", - "email_address": "", - "air_brake_id": -1, - "air_brake_key": "", - "hide_sensitive_data": true - }, - "auth_config": { - "init_user": "test", - "init_password": "test", - "internal_public_key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE3TlJVL2FNWnF6Sm8vdnEyREFwMgpyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDZk9FU1NEek5oZG1jTURnWUZ6MVpFd2loVUFDRDFKMzdXM0xRCkpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWZFNlBLY2pYSHU0NVBISTlRb1JDd1VzcDdGNE5PZElIWm12UjYKeFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUDdaL28wcXBGTmRDa2k3aDV6Vjd1TmlqMFFHbU5rN0hRa3FGMQpWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRUDhyT0hYQUFjYkt0THRpSzlOM2FVWVhkM2Y0VnBWR0FaSlExCms1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWthUHAzci9LNUVXNUg2VUVCcEs2elRibU1JYUFJcVdMUnZ3ZjIKN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yM3JCMCtCWW1ta2VPcUt1OVpEdlUyV292VnRGeFdVQmU1TXNqSApPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjWVgrR3A3Qmo5amtrTEpoRS9OTHZ0akFQOFpQR1lCWXhZTHhNCjNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG1yR1MxaDZSbEFITE1ISUtSenZiYVRPRFZFdGtzUDNSQVUvVzEKZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWE1RZWo2QnZlOGIzZWozaHozQ2ZydnUwNnNFcjJqREF0UXZSNApZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNMW1VODJhL09aYjRyZklISGFQTjR0TGJTZW1LZWJVZGliVzd0ClFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=", - "internal_private_key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBN05SVS9hTVpxekpvL3ZxMkRBcDJyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDCmZPRVNTRHpOaGRtY01EZ1lGejFaRXdpaFVBQ0QxSjM3VzNMUUpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWYKRTZQS2NqWEh1NDVQSEk5UW9SQ3dVc3A3RjROT2RJSFptdlI2eFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUAo3Wi9vMHFwRk5kQ2tpN2g1elY3dU5pajBRR21OazdIUWtxRjFWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRClA4ck9IWEFBY2JLdEx0aUs5TjNhVVlYZDNmNFZwVkdBWkpRMWs1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWsKYVBwM3IvSzVFVzVINlVFQnBLNnpUYm1NSWFBSXFXTFJ2d2YyN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yMwpyQjArQlltbWtlT3FLdTlaRHZVMldvdlZ0RnhXVUJlNU1zakhPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjCllYK0dwN0JqOWpra0xKaEUvTkx2dGpBUDhaUEdZQll4WUx4TTNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG0KckdTMWg2UmxBSExNSElLUnp2YmFUT0RWRXRrc1AzUkFVL1cxZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWApNUWVqNkJ2ZThiM2VqM2h6M0NmcnZ1MDZzRXIyakRBdFF2UjRZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNCjFtVTgyYS9PWmI0cmZJSEhhUE40dExiU2VtS2ViVWRpYlc3dFFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUEKQVFLQ0FnRUF4YnlYWmRxTE1ReDY3QUhxZyswc29TMkZYU29DUmJXT2wvVSt1T0cxaWdyZDdSbkdkRHY3NXNLVgpteDlSTUZWMWo5blNDSGxaTHBIdmdGT1RyZ3dUekVoaXRKRjZsWnVEWjJOQjBRa0xLaWNMNVdKR2IwQi9aSktRCnZJR2FmaThPMUJ3NkREWXhSaldGQ1BQdCsxb0xNN2Z2Q2pHYUNpNUtsSFJxZU1GTytBc3lEWHZMM2t4NHVZUWYKRzBxa1NHQnpta3lidWk3Qm1SSkllanVwK1BQRUlpcno0UklOdlcyelJjLzhOYlh3TXNvTjM4RWY5NU5lT3VmcwpCd0ozWkxpNmRLN1RmT08zbG9WeUQwRVlZV0g1eGRORmRuNTdvNEs5SGZibjBXQTV2ZGdJVUxCTUxiYUt2aVo3CjdSaldBK1FaK2lVL1lqTDgxSXBwRVB5SVFlYmxmVlRqQitQa3pKSWQ1R2dac0x6MlFka09xRnZWVE9pT0hNYlQKbDBIS3RmaFpod2pBRG1FeVdQaXIxOHNuVGs3SHoxbmh0eHV3ZGRlREZsZFZ2cU1xZlEyK25tTVBYRjdXbk9uWAppTGcyQlE0ZDFHS3c0WTBMR3FPdWl3ckFtOThlMEJLN2swdTN5WlVtRzRQRE91bzZFV2pyM0pCQ1p1YmViSVBvCkhhVnpBWllhTlZsbHN2dm9LOGp6eWN5aHo3cTBjK1lwRUJHLzNHTGFFbmdaSzVMUzRTZnhxU1FXc25aZjBmeE0KL0VBdWpEMUVjanZaTXJ6ejZ3SWxkd2I3Qkh6N3NBb1dXdmh1aUdZcEdWSzZSTUZhUDNmdXFvMkkwRVUrdEV5QQpOaW5mVDA3SkFGNnUwT1Zpc0dndVdiMHNGR2VKWis3UlVTc3RUbkYxQWh6dTgzdUM5N1VDZ2dFQkFQZzl4MXJqCjl5ZGhEaGFYWXQxNk9sQWU5NFR6eW41N2NzUlBlRlRSZjV4ZjF1MHVyQzJYZ2IvYnZ3MHI2SGc3bVBIRUV1NjMKU1ZxNUx2QzFEVHo5QzNJR1o3MkpyM1RWVTRsNlI2clVtNVBUYXExaE10M25WWlVLK2tUa0NWNE5wOVE3Vm0wMgpWT1IrSjZpM2JZOFU1bkZEemU1ZEk2QjRzWTFoeGQweW1wQW5Xc3dPSG5HMnFlclVEaHFHU2lJOFpzNmo0NTZJCmJZSkFkZ1BQRVZHNVY1V3owbnp2RG1YK3RQM05RRXF3NEE2MW9tQW9tVS8zODNQSVJ4MnJJSjRTT3JlUXNyd1QKdXBHTVJ0QWZ4bVV1aVJvRHI2eFNiczJ6SWRZcVgwcloxSnlBaXdqTUpNa2RBWWZmMGw1UUlwK1VVOTNYQ01nawpuSmFoc3JzRDdpcVluanNDZ2dFQkFQUTdQcVFTay9wY2ltdS9LSVhRSHhEN0N2Q2txY3I4UGpWTVo0NG5jMk5vCkZROEU2UzgxQmNFVEVDWmx4MVRyVkFVVmdFajhwTG9rTytHcUlzMGtHMHduQUNITVZLMS9nbE4vRCtSbm9zYXYKR1hmbnpnRGJzVVlNcDRCcDAwK1Uyc1ByWGFyOFU3ZWRZSnM2ZmhKcTAvbC85Y3BHcTByUHlncVZiWUd5KzBsaQpiR2NkMkxvTEhOdmdRS2dLZys1Zmg2WHFMU3lXWnJLcWQxVnhmSGVyZ01GUis4RXhodXNXRlpLTzFHZ1FPMzc2CjRsV0ZuUDBtMnJLMjlzSDJ4bWE4UVE0STFjRS95VEhBTDNOTUoyTCtTQjlHUy91ZGc5WGczdFI4dytZdW1EN3EKR09GeWdtTmZzR1NYYXpkRWxBTEFHRElENUEwd0UvRWdselNCanpxM3VsOENnZ0VBWTE2SGdMQ2tiTlVEQ0xRTQoxVTlxTEV4WkZKVnFSM3N2RTdva0Z2L05yMUVGL2VlaThKVW5VUitydUtBTTdLUWVzeGlqNDM3bkZEUHd3RllaCk9JS3FwRGhBS3JVRTBTWGJ6THB3R2NnRmh3VW9QTU1kMDRvWXpoS1k0QjdRU1IvNlFKQ0lKaXVMaS9PYitJT0UKamJQMkV2enJZREZVWTVZc3JNV29xTVRxN2kxeXdTQWR1N005RFUxWlgvREZtRExKaklvNlFXbW5QRzZGVHowQwpWODV6YXUrU29JUXBKVmJ5S0c2Uy85TVJ2Wkdqc0E1UVlKeUdqYUJzSjBvclFsdFZ1Y2xvWXJVYkI4dzVSSEtUCnZra0VoSzlaRVFmbVp0ei8vSFQxdEViQ1B1dU52RFhMdTkycWtUTmRTSGVYaEgyaG5MbkpRQ1Mzc2V5RVdTeFgKbUNHRHBRS0NBUUVBaTNyYVIzR2t1VExvaXFoZFNDNlh6MmJQMUtiMW9VdDFhNUw3QVNCZXNjTGJZL3gxLzlQVQpPWFBkb1ZBM0NyUnJBNHhIKzJidDNMQ2Mwa0FNS0FRYTR0N1RJSHBGVWVDa1dYTVRiR29UZUV5L3lzN0R3NUcwCktFRkoxL2lZQ2JjRlNTYStFOHlQTXluWjVrejlleDh2ZUNvd0FSbGk4aExCWEZJQ2ZEUHZkdldTMjBFY2FRTzMKczRyYTRoMC9RMytqUkluOHlwNEtnTGNCOS9ZY0Uyd0syRjB0M2lPZTNkdDY3bnhMcWpLN0I4WFlST2ROeFBYUApxSWo5VzhESGhoeTFPb0twTVBod3VzejdUR21OaE9lYjRPQ1F2RjQwMEl6Z05aSWJmdlhWVlBqMHhLeFU4dFBQCk5XT1VnN2ZTbjg5OUFmTmU1bmt5cWw3bWU4SVNQb0ozR1FLQ0FRQlBhOVZwM0pkOE9CZUlQelhsc2lPS2haSFIKYS94a1BqbnBVMkJvZzAzYllEcXdjWFRnOWtOWTc2U3FteXNaSFlkTDFCc2NLUFplZzc2SS9hbXhxYTZib29wWgpzTHAzOEQ0dk1NNk9ZeVhzaC9zUk82NDVuQjhIMGpzcGN4eXhySzF1MlBiK3M0MWZXTGVGNC9NWWdiMFFtbFFDCkYyZEJwVTkvZHJSQjhRNmd6NnFHeFQ5THk0SUxnazFhU05HMTRTTCtQWGg5amlPem92c05IY1FSbWI1eldJVkgKbURrdGdoMFh0S3NKVStBbEtrMHVoaUxZeGZNd3U3ZDhreENEdmhGYmRUQ3BIWjdNbzEzdW1udmNJRjllbnRoZApRc1BiYm5TK2JFVHZYckVWajA3R01rU0FJcjBkY0ZONldyTjRuTTVDcmpmMk1NQmJTSDFnR2J6QjZmTzEKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K", - "external_public_key": "", - "enable_jwt_provider": true, - "jwk_url": "", - "jwt_user_path": "email", - "issuer_id": "d515ef82-5579-44d0-9919-59d273dca7c0" - }, - "email_config": { - "smtp_host": "smtp.gmail.com", - "smtp_port": "587", - "auth_required": true, - "sender": "", - "password": "" - }, - "tls_config": { - "enable_ssl": false, - "server_key": "", - "server_cert": "" - }, - "websocket_config": { - "write_wait": 10, - "pong_wait": 60 - } + { + "app_name": "datagate", + "debug_message": false, + "tombstone_ttl": 0, + "auth_config": { + "init_user": "test", + "init_password": "test", + "internal_public_key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE3TlJVL2FNWnF6Sm8vdnEyREFwMgpyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDZk9FU1NEek5oZG1jTURnWUZ6MVpFd2loVUFDRDFKMzdXM0xRCkpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWZFNlBLY2pYSHU0NVBISTlRb1JDd1VzcDdGNE5PZElIWm12UjYKeFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUDdaL28wcXBGTmRDa2k3aDV6Vjd1TmlqMFFHbU5rN0hRa3FGMQpWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRUDhyT0hYQUFjYkt0THRpSzlOM2FVWVhkM2Y0VnBWR0FaSlExCms1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWthUHAzci9LNUVXNUg2VUVCcEs2elRibU1JYUFJcVdMUnZ3ZjIKN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yM3JCMCtCWW1ta2VPcUt1OVpEdlUyV292VnRGeFdVQmU1TXNqSApPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjWVgrR3A3Qmo5amtrTEpoRS9OTHZ0akFQOFpQR1lCWXhZTHhNCjNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG1yR1MxaDZSbEFITE1ISUtSenZiYVRPRFZFdGtzUDNSQVUvVzEKZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWE1RZWo2QnZlOGIzZWozaHozQ2ZydnUwNnNFcjJqREF0UXZSNApZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNMW1VODJhL09aYjRyZklISGFQTjR0TGJTZW1LZWJVZGliVzd0ClFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=", + "internal_private_key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBN05SVS9hTVpxekpvL3ZxMkRBcDJyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDCmZPRVNTRHpOaGRtY01EZ1lGejFaRXdpaFVBQ0QxSjM3VzNMUUpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWYKRTZQS2NqWEh1NDVQSEk5UW9SQ3dVc3A3RjROT2RJSFptdlI2eFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUAo3Wi9vMHFwRk5kQ2tpN2g1elY3dU5pajBRR21OazdIUWtxRjFWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRClA4ck9IWEFBY2JLdEx0aUs5TjNhVVlYZDNmNFZwVkdBWkpRMWs1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWsKYVBwM3IvSzVFVzVINlVFQnBLNnpUYm1NSWFBSXFXTFJ2d2YyN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yMwpyQjArQlltbWtlT3FLdTlaRHZVMldvdlZ0RnhXVUJlNU1zakhPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjCllYK0dwN0JqOWpra0xKaEUvTkx2dGpBUDhaUEdZQll4WUx4TTNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG0KckdTMWg2UmxBSExNSElLUnp2YmFUT0RWRXRrc1AzUkFVL1cxZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWApNUWVqNkJ2ZThiM2VqM2h6M0NmcnZ1MDZzRXIyakRBdFF2UjRZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNCjFtVTgyYS9PWmI0cmZJSEhhUE40dExiU2VtS2ViVWRpYlc3dFFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUEKQVFLQ0FnRUF4YnlYWmRxTE1ReDY3QUhxZyswc29TMkZYU29DUmJXT2wvVSt1T0cxaWdyZDdSbkdkRHY3NXNLVgpteDlSTUZWMWo5blNDSGxaTHBIdmdGT1RyZ3dUekVoaXRKRjZsWnVEWjJOQjBRa0xLaWNMNVdKR2IwQi9aSktRCnZJR2FmaThPMUJ3NkREWXhSaldGQ1BQdCsxb0xNN2Z2Q2pHYUNpNUtsSFJxZU1GTytBc3lEWHZMM2t4NHVZUWYKRzBxa1NHQnpta3lidWk3Qm1SSkllanVwK1BQRUlpcno0UklOdlcyelJjLzhOYlh3TXNvTjM4RWY5NU5lT3VmcwpCd0ozWkxpNmRLN1RmT08zbG9WeUQwRVlZV0g1eGRORmRuNTdvNEs5SGZibjBXQTV2ZGdJVUxCTUxiYUt2aVo3CjdSaldBK1FaK2lVL1lqTDgxSXBwRVB5SVFlYmxmVlRqQitQa3pKSWQ1R2dac0x6MlFka09xRnZWVE9pT0hNYlQKbDBIS3RmaFpod2pBRG1FeVdQaXIxOHNuVGs3SHoxbmh0eHV3ZGRlREZsZFZ2cU1xZlEyK25tTVBYRjdXbk9uWAppTGcyQlE0ZDFHS3c0WTBMR3FPdWl3ckFtOThlMEJLN2swdTN5WlVtRzRQRE91bzZFV2pyM0pCQ1p1YmViSVBvCkhhVnpBWllhTlZsbHN2dm9LOGp6eWN5aHo3cTBjK1lwRUJHLzNHTGFFbmdaSzVMUzRTZnhxU1FXc25aZjBmeE0KL0VBdWpEMUVjanZaTXJ6ejZ3SWxkd2I3Qkh6N3NBb1dXdmh1aUdZcEdWSzZSTUZhUDNmdXFvMkkwRVUrdEV5QQpOaW5mVDA3SkFGNnUwT1Zpc0dndVdiMHNGR2VKWis3UlVTc3RUbkYxQWh6dTgzdUM5N1VDZ2dFQkFQZzl4MXJqCjl5ZGhEaGFYWXQxNk9sQWU5NFR6eW41N2NzUlBlRlRSZjV4ZjF1MHVyQzJYZ2IvYnZ3MHI2SGc3bVBIRUV1NjMKU1ZxNUx2QzFEVHo5QzNJR1o3MkpyM1RWVTRsNlI2clVtNVBUYXExaE10M25WWlVLK2tUa0NWNE5wOVE3Vm0wMgpWT1IrSjZpM2JZOFU1bkZEemU1ZEk2QjRzWTFoeGQweW1wQW5Xc3dPSG5HMnFlclVEaHFHU2lJOFpzNmo0NTZJCmJZSkFkZ1BQRVZHNVY1V3owbnp2RG1YK3RQM05RRXF3NEE2MW9tQW9tVS8zODNQSVJ4MnJJSjRTT3JlUXNyd1QKdXBHTVJ0QWZ4bVV1aVJvRHI2eFNiczJ6SWRZcVgwcloxSnlBaXdqTUpNa2RBWWZmMGw1UUlwK1VVOTNYQ01nawpuSmFoc3JzRDdpcVluanNDZ2dFQkFQUTdQcVFTay9wY2ltdS9LSVhRSHhEN0N2Q2txY3I4UGpWTVo0NG5jMk5vCkZROEU2UzgxQmNFVEVDWmx4MVRyVkFVVmdFajhwTG9rTytHcUlzMGtHMHduQUNITVZLMS9nbE4vRCtSbm9zYXYKR1hmbnpnRGJzVVlNcDRCcDAwK1Uyc1ByWGFyOFU3ZWRZSnM2ZmhKcTAvbC85Y3BHcTByUHlncVZiWUd5KzBsaQpiR2NkMkxvTEhOdmdRS2dLZys1Zmg2WHFMU3lXWnJLcWQxVnhmSGVyZ01GUis4RXhodXNXRlpLTzFHZ1FPMzc2CjRsV0ZuUDBtMnJLMjlzSDJ4bWE4UVE0STFjRS95VEhBTDNOTUoyTCtTQjlHUy91ZGc5WGczdFI4dytZdW1EN3EKR09GeWdtTmZzR1NYYXpkRWxBTEFHRElENUEwd0UvRWdselNCanpxM3VsOENnZ0VBWTE2SGdMQ2tiTlVEQ0xRTQoxVTlxTEV4WkZKVnFSM3N2RTdva0Z2L05yMUVGL2VlaThKVW5VUitydUtBTTdLUWVzeGlqNDM3bkZEUHd3RllaCk9JS3FwRGhBS3JVRTBTWGJ6THB3R2NnRmh3VW9QTU1kMDRvWXpoS1k0QjdRU1IvNlFKQ0lKaXVMaS9PYitJT0UKamJQMkV2enJZREZVWTVZc3JNV29xTVRxN2kxeXdTQWR1N005RFUxWlgvREZtRExKaklvNlFXbW5QRzZGVHowQwpWODV6YXUrU29JUXBKVmJ5S0c2Uy85TVJ2Wkdqc0E1UVlKeUdqYUJzSjBvclFsdFZ1Y2xvWXJVYkI4dzVSSEtUCnZra0VoSzlaRVFmbVp0ei8vSFQxdEViQ1B1dU52RFhMdTkycWtUTmRTSGVYaEgyaG5MbkpRQ1Mzc2V5RVdTeFgKbUNHRHBRS0NBUUVBaTNyYVIzR2t1VExvaXFoZFNDNlh6MmJQMUtiMW9VdDFhNUw3QVNCZXNjTGJZL3gxLzlQVQpPWFBkb1ZBM0NyUnJBNHhIKzJidDNMQ2Mwa0FNS0FRYTR0N1RJSHBGVWVDa1dYTVRiR29UZUV5L3lzN0R3NUcwCktFRkoxL2lZQ2JjRlNTYStFOHlQTXluWjVrejlleDh2ZUNvd0FSbGk4aExCWEZJQ2ZEUHZkdldTMjBFY2FRTzMKczRyYTRoMC9RMytqUkluOHlwNEtnTGNCOS9ZY0Uyd0syRjB0M2lPZTNkdDY3bnhMcWpLN0I4WFlST2ROeFBYUApxSWo5VzhESGhoeTFPb0twTVBod3VzejdUR21OaE9lYjRPQ1F2RjQwMEl6Z05aSWJmdlhWVlBqMHhLeFU4dFBQCk5XT1VnN2ZTbjg5OUFmTmU1bmt5cWw3bWU4SVNQb0ozR1FLQ0FRQlBhOVZwM0pkOE9CZUlQelhsc2lPS2haSFIKYS94a1BqbnBVMkJvZzAzYllEcXdjWFRnOWtOWTc2U3FteXNaSFlkTDFCc2NLUFplZzc2SS9hbXhxYTZib29wWgpzTHAzOEQ0dk1NNk9ZeVhzaC9zUk82NDVuQjhIMGpzcGN4eXhySzF1MlBiK3M0MWZXTGVGNC9NWWdiMFFtbFFDCkYyZEJwVTkvZHJSQjhRNmd6NnFHeFQ5THk0SUxnazFhU05HMTRTTCtQWGg5amlPem92c05IY1FSbWI1eldJVkgKbURrdGdoMFh0S3NKVStBbEtrMHVoaUxZeGZNd3U3ZDhreENEdmhGYmRUQ3BIWjdNbzEzdW1udmNJRjllbnRoZApRc1BiYm5TK2JFVHZYckVWajA3R01rU0FJcjBkY0ZONldyTjRuTTVDcmpmMk1NQmJTSDFnR2J6QjZmTzEKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K", + "external_public_key": "", + "enable_jwt_provider": true, + "jwk_url": "", + "jwt_user_path": "email", + "issuer_id": "d515ef82-5579-44d0-9919-59d273dca7c0" + }, + "websocket_config": { + "write_wait": 10, + "pong_wait": 60 } + }, + { + "app_name": "integration-test", + "debug_message": true, + "tombstone_ttl": 180, + "auth_config": { + "init_user": "test", + "init_password": "test", + "internal_public_key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE3TlJVL2FNWnF6Sm8vdnEyREFwMgpyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDZk9FU1NEek5oZG1jTURnWUZ6MVpFd2loVUFDRDFKMzdXM0xRCkpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWZFNlBLY2pYSHU0NVBISTlRb1JDd1VzcDdGNE5PZElIWm12UjYKeFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUDdaL28wcXBGTmRDa2k3aDV6Vjd1TmlqMFFHbU5rN0hRa3FGMQpWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRUDhyT0hYQUFjYkt0THRpSzlOM2FVWVhkM2Y0VnBWR0FaSlExCms1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWthUHAzci9LNUVXNUg2VUVCcEs2elRibU1JYUFJcVdMUnZ3ZjIKN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yM3JCMCtCWW1ta2VPcUt1OVpEdlUyV292VnRGeFdVQmU1TXNqSApPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjWVgrR3A3Qmo5amtrTEpoRS9OTHZ0akFQOFpQR1lCWXhZTHhNCjNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG1yR1MxaDZSbEFITE1ISUtSenZiYVRPRFZFdGtzUDNSQVUvVzEKZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWE1RZWo2QnZlOGIzZWozaHozQ2ZydnUwNnNFcjJqREF0UXZSNApZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNMW1VODJhL09aYjRyZklISGFQTjR0TGJTZW1LZWJVZGliVzd0ClFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=", + "internal_private_key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBN05SVS9hTVpxekpvL3ZxMkRBcDJyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDCmZPRVNTRHpOaGRtY01EZ1lGejFaRXdpaFVBQ0QxSjM3VzNMUUpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWYKRTZQS2NqWEh1NDVQSEk5UW9SQ3dVc3A3RjROT2RJSFptdlI2eFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUAo3Wi9vMHFwRk5kQ2tpN2g1elY3dU5pajBRR21OazdIUWtxRjFWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRClA4ck9IWEFBY2JLdEx0aUs5TjNhVVlYZDNmNFZwVkdBWkpRMWs1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWsKYVBwM3IvSzVFVzVINlVFQnBLNnpUYm1NSWFBSXFXTFJ2d2YyN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yMwpyQjArQlltbWtlT3FLdTlaRHZVMldvdlZ0RnhXVUJlNU1zakhPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjCllYK0dwN0JqOWpra0xKaEUvTkx2dGpBUDhaUEdZQll4WUx4TTNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG0KckdTMWg2UmxBSExNSElLUnp2YmFUT0RWRXRrc1AzUkFVL1cxZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWApNUWVqNkJ2ZThiM2VqM2h6M0NmcnZ1MDZzRXIyakRBdFF2UjRZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNCjFtVTgyYS9PWmI0cmZJSEhhUE40dExiU2VtS2ViVWRpYlc3dFFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUEKQVFLQ0FnRUF4YnlYWmRxTE1ReDY3QUhxZyswc29TMkZYU29DUmJXT2wvVSt1T0cxaWdyZDdSbkdkRHY3NXNLVgpteDlSTUZWMWo5blNDSGxaTHBIdmdGT1RyZ3dUekVoaXRKRjZsWnVEWjJOQjBRa0xLaWNMNVdKR2IwQi9aSktRCnZJR2FmaThPMUJ3NkREWXhSaldGQ1BQdCsxb0xNN2Z2Q2pHYUNpNUtsSFJxZU1GTytBc3lEWHZMM2t4NHVZUWYKRzBxa1NHQnpta3lidWk3Qm1SSkllanVwK1BQRUlpcno0UklOdlcyelJjLzhOYlh3TXNvTjM4RWY5NU5lT3VmcwpCd0ozWkxpNmRLN1RmT08zbG9WeUQwRVlZV0g1eGRORmRuNTdvNEs5SGZibjBXQTV2ZGdJVUxCTUxiYUt2aVo3CjdSaldBK1FaK2lVL1lqTDgxSXBwRVB5SVFlYmxmVlRqQitQa3pKSWQ1R2dac0x6MlFka09xRnZWVE9pT0hNYlQKbDBIS3RmaFpod2pBRG1FeVdQaXIxOHNuVGs3SHoxbmh0eHV3ZGRlREZsZFZ2cU1xZlEyK25tTVBYRjdXbk9uWAppTGcyQlE0ZDFHS3c0WTBMR3FPdWl3ckFtOThlMEJLN2swdTN5WlVtRzRQRE91bzZFV2pyM0pCQ1p1YmViSVBvCkhhVnpBWllhTlZsbHN2dm9LOGp6eWN5aHo3cTBjK1lwRUJHLzNHTGFFbmdaSzVMUzRTZnhxU1FXc25aZjBmeE0KL0VBdWpEMUVjanZaTXJ6ejZ3SWxkd2I3Qkh6N3NBb1dXdmh1aUdZcEdWSzZSTUZhUDNmdXFvMkkwRVUrdEV5QQpOaW5mVDA3SkFGNnUwT1Zpc0dndVdiMHNGR2VKWis3UlVTc3RUbkYxQWh6dTgzdUM5N1VDZ2dFQkFQZzl4MXJqCjl5ZGhEaGFYWXQxNk9sQWU5NFR6eW41N2NzUlBlRlRSZjV4ZjF1MHVyQzJYZ2IvYnZ3MHI2SGc3bVBIRUV1NjMKU1ZxNUx2QzFEVHo5QzNJR1o3MkpyM1RWVTRsNlI2clVtNVBUYXExaE10M25WWlVLK2tUa0NWNE5wOVE3Vm0wMgpWT1IrSjZpM2JZOFU1bkZEemU1ZEk2QjRzWTFoeGQweW1wQW5Xc3dPSG5HMnFlclVEaHFHU2lJOFpzNmo0NTZJCmJZSkFkZ1BQRVZHNVY1V3owbnp2RG1YK3RQM05RRXF3NEE2MW9tQW9tVS8zODNQSVJ4MnJJSjRTT3JlUXNyd1QKdXBHTVJ0QWZ4bVV1aVJvRHI2eFNiczJ6SWRZcVgwcloxSnlBaXdqTUpNa2RBWWZmMGw1UUlwK1VVOTNYQ01nawpuSmFoc3JzRDdpcVluanNDZ2dFQkFQUTdQcVFTay9wY2ltdS9LSVhRSHhEN0N2Q2txY3I4UGpWTVo0NG5jMk5vCkZROEU2UzgxQmNFVEVDWmx4MVRyVkFVVmdFajhwTG9rTytHcUlzMGtHMHduQUNITVZLMS9nbE4vRCtSbm9zYXYKR1hmbnpnRGJzVVlNcDRCcDAwK1Uyc1ByWGFyOFU3ZWRZSnM2ZmhKcTAvbC85Y3BHcTByUHlncVZiWUd5KzBsaQpiR2NkMkxvTEhOdmdRS2dLZys1Zmg2WHFMU3lXWnJLcWQxVnhmSGVyZ01GUis4RXhodXNXRlpLTzFHZ1FPMzc2CjRsV0ZuUDBtMnJLMjlzSDJ4bWE4UVE0STFjRS95VEhBTDNOTUoyTCtTQjlHUy91ZGc5WGczdFI4dytZdW1EN3EKR09GeWdtTmZzR1NYYXpkRWxBTEFHRElENUEwd0UvRWdselNCanpxM3VsOENnZ0VBWTE2SGdMQ2tiTlVEQ0xRTQoxVTlxTEV4WkZKVnFSM3N2RTdva0Z2L05yMUVGL2VlaThKVW5VUitydUtBTTdLUWVzeGlqNDM3bkZEUHd3RllaCk9JS3FwRGhBS3JVRTBTWGJ6THB3R2NnRmh3VW9QTU1kMDRvWXpoS1k0QjdRU1IvNlFKQ0lKaXVMaS9PYitJT0UKamJQMkV2enJZREZVWTVZc3JNV29xTVRxN2kxeXdTQWR1N005RFUxWlgvREZtRExKaklvNlFXbW5QRzZGVHowQwpWODV6YXUrU29JUXBKVmJ5S0c2Uy85TVJ2Wkdqc0E1UVlKeUdqYUJzSjBvclFsdFZ1Y2xvWXJVYkI4dzVSSEtUCnZra0VoSzlaRVFmbVp0ei8vSFQxdEViQ1B1dU52RFhMdTkycWtUTmRTSGVYaEgyaG5MbkpRQ1Mzc2V5RVdTeFgKbUNHRHBRS0NBUUVBaTNyYVIzR2t1VExvaXFoZFNDNlh6MmJQMUtiMW9VdDFhNUw3QVNCZXNjTGJZL3gxLzlQVQpPWFBkb1ZBM0NyUnJBNHhIKzJidDNMQ2Mwa0FNS0FRYTR0N1RJSHBGVWVDa1dYTVRiR29UZUV5L3lzN0R3NUcwCktFRkoxL2lZQ2JjRlNTYStFOHlQTXluWjVrejlleDh2ZUNvd0FSbGk4aExCWEZJQ2ZEUHZkdldTMjBFY2FRTzMKczRyYTRoMC9RMytqUkluOHlwNEtnTGNCOS9ZY0Uyd0syRjB0M2lPZTNkdDY3bnhMcWpLN0I4WFlST2ROeFBYUApxSWo5VzhESGhoeTFPb0twTVBod3VzejdUR21OaE9lYjRPQ1F2RjQwMEl6Z05aSWJmdlhWVlBqMHhLeFU4dFBQCk5XT1VnN2ZTbjg5OUFmTmU1bmt5cWw3bWU4SVNQb0ozR1FLQ0FRQlBhOVZwM0pkOE9CZUlQelhsc2lPS2haSFIKYS94a1BqbnBVMkJvZzAzYllEcXdjWFRnOWtOWTc2U3FteXNaSFlkTDFCc2NLUFplZzc2SS9hbXhxYTZib29wWgpzTHAzOEQ0dk1NNk9ZeVhzaC9zUk82NDVuQjhIMGpzcGN4eXhySzF1MlBiK3M0MWZXTGVGNC9NWWdiMFFtbFFDCkYyZEJwVTkvZHJSQjhRNmd6NnFHeFQ5THk0SUxnazFhU05HMTRTTCtQWGg5amlPem92c05IY1FSbWI1eldJVkgKbURrdGdoMFh0S3NKVStBbEtrMHVoaUxZeGZNd3U3ZDhreENEdmhGYmRUQ3BIWjdNbzEzdW1udmNJRjllbnRoZApRc1BiYm5TK2JFVHZYckVWajA3R01rU0FJcjBkY0ZONldyTjRuTTVDcmpmMk1NQmJTSDFnR2J6QjZmTzEKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K", + "external_public_key": "", + "enable_jwt_provider": true, + "jwk_url": "", + "jwt_user_path": "email", + "issuer_id": "d515ef82-5579-44d0-9919-59d273dca7c0" + }, + "websocket_config": { + "write_wait": 10, + "pong_wait": 60 + } + }, + { + "app_name": "junit-test", + "debug_message": true, + "tombstone_ttl": 180, + "auth_config": { + "init_user": "test", + "init_password": "test", + "internal_public_key": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE3TlJVL2FNWnF6Sm8vdnEyREFwMgpyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDZk9FU1NEek5oZG1jTURnWUZ6MVpFd2loVUFDRDFKMzdXM0xRCkpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWZFNlBLY2pYSHU0NVBISTlRb1JDd1VzcDdGNE5PZElIWm12UjYKeFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUDdaL28wcXBGTmRDa2k3aDV6Vjd1TmlqMFFHbU5rN0hRa3FGMQpWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRUDhyT0hYQUFjYkt0THRpSzlOM2FVWVhkM2Y0VnBWR0FaSlExCms1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWthUHAzci9LNUVXNUg2VUVCcEs2elRibU1JYUFJcVdMUnZ3ZjIKN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yM3JCMCtCWW1ta2VPcUt1OVpEdlUyV292VnRGeFdVQmU1TXNqSApPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjWVgrR3A3Qmo5amtrTEpoRS9OTHZ0akFQOFpQR1lCWXhZTHhNCjNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG1yR1MxaDZSbEFITE1ISUtSenZiYVRPRFZFdGtzUDNSQVUvVzEKZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWE1RZWo2QnZlOGIzZWozaHozQ2ZydnUwNnNFcjJqREF0UXZSNApZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNMW1VODJhL09aYjRyZklISGFQTjR0TGJTZW1LZWJVZGliVzd0ClFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=", + "internal_private_key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBN05SVS9hTVpxekpvL3ZxMkRBcDJyenlWY1RPVHh0Mnh3VitnUUdDaGNWWlpsNVhDCmZPRVNTRHpOaGRtY01EZ1lGejFaRXdpaFVBQ0QxSjM3VzNMUUpWVzZVdHN1YTRMZG1COXgzci9teWV3OUMrRWYKRTZQS2NqWEh1NDVQSEk5UW9SQ3dVc3A3RjROT2RJSFptdlI2eFU0SkR0M09hcksvazVyaVpVVEd6WTJWbGxLUAo3Wi9vMHFwRk5kQ2tpN2g1elY3dU5pajBRR21OazdIUWtxRjFWSnIvU01oRStnTitUNmJiWVYrTUtqOWdtcmlRClA4ck9IWEFBY2JLdEx0aUs5TjNhVVlYZDNmNFZwVkdBWkpRMWs1KzdvTWl5V2cxeTVGWWhFL1FyTTN3NlFtaWsKYVBwM3IvSzVFVzVINlVFQnBLNnpUYm1NSWFBSXFXTFJ2d2YyN3JFLy9mb0xhQ2ZEa1dFUGl5bGluKzFWOU4yMwpyQjArQlltbWtlT3FLdTlaRHZVMldvdlZ0RnhXVUJlNU1zakhPN2hGYzErUHEzcWU3bVZPNlVTdEdueUhJVEJjCllYK0dwN0JqOWpra0xKaEUvTkx2dGpBUDhaUEdZQll4WUx4TTNndm9pN2RaaDFGU00vdUpGRjkzblRNNU0xWG0KckdTMWg2UmxBSExNSElLUnp2YmFUT0RWRXRrc1AzUkFVL1cxZVZRYTVJUUd3a3FsZzdiNmYwcjNOZEcxbmhBWApNUWVqNkJ2ZThiM2VqM2h6M0NmcnZ1MDZzRXIyakRBdFF2UjRZdGdUcDZ1Z1lxOVFqdENuRmR4RTNrdlVqNWxNCjFtVTgyYS9PWmI0cmZJSEhhUE40dExiU2VtS2ViVWRpYlc3dFFWTktuMzNVLzdpbW5TQ2xQQ3ZHbGVVQ0F3RUEKQVFLQ0FnRUF4YnlYWmRxTE1ReDY3QUhxZyswc29TMkZYU29DUmJXT2wvVSt1T0cxaWdyZDdSbkdkRHY3NXNLVgpteDlSTUZWMWo5blNDSGxaTHBIdmdGT1RyZ3dUekVoaXRKRjZsWnVEWjJOQjBRa0xLaWNMNVdKR2IwQi9aSktRCnZJR2FmaThPMUJ3NkREWXhSaldGQ1BQdCsxb0xNN2Z2Q2pHYUNpNUtsSFJxZU1GTytBc3lEWHZMM2t4NHVZUWYKRzBxa1NHQnpta3lidWk3Qm1SSkllanVwK1BQRUlpcno0UklOdlcyelJjLzhOYlh3TXNvTjM4RWY5NU5lT3VmcwpCd0ozWkxpNmRLN1RmT08zbG9WeUQwRVlZV0g1eGRORmRuNTdvNEs5SGZibjBXQTV2ZGdJVUxCTUxiYUt2aVo3CjdSaldBK1FaK2lVL1lqTDgxSXBwRVB5SVFlYmxmVlRqQitQa3pKSWQ1R2dac0x6MlFka09xRnZWVE9pT0hNYlQKbDBIS3RmaFpod2pBRG1FeVdQaXIxOHNuVGs3SHoxbmh0eHV3ZGRlREZsZFZ2cU1xZlEyK25tTVBYRjdXbk9uWAppTGcyQlE0ZDFHS3c0WTBMR3FPdWl3ckFtOThlMEJLN2swdTN5WlVtRzRQRE91bzZFV2pyM0pCQ1p1YmViSVBvCkhhVnpBWllhTlZsbHN2dm9LOGp6eWN5aHo3cTBjK1lwRUJHLzNHTGFFbmdaSzVMUzRTZnhxU1FXc25aZjBmeE0KL0VBdWpEMUVjanZaTXJ6ejZ3SWxkd2I3Qkh6N3NBb1dXdmh1aUdZcEdWSzZSTUZhUDNmdXFvMkkwRVUrdEV5QQpOaW5mVDA3SkFGNnUwT1Zpc0dndVdiMHNGR2VKWis3UlVTc3RUbkYxQWh6dTgzdUM5N1VDZ2dFQkFQZzl4MXJqCjl5ZGhEaGFYWXQxNk9sQWU5NFR6eW41N2NzUlBlRlRSZjV4ZjF1MHVyQzJYZ2IvYnZ3MHI2SGc3bVBIRUV1NjMKU1ZxNUx2QzFEVHo5QzNJR1o3MkpyM1RWVTRsNlI2clVtNVBUYXExaE10M25WWlVLK2tUa0NWNE5wOVE3Vm0wMgpWT1IrSjZpM2JZOFU1bkZEemU1ZEk2QjRzWTFoeGQweW1wQW5Xc3dPSG5HMnFlclVEaHFHU2lJOFpzNmo0NTZJCmJZSkFkZ1BQRVZHNVY1V3owbnp2RG1YK3RQM05RRXF3NEE2MW9tQW9tVS8zODNQSVJ4MnJJSjRTT3JlUXNyd1QKdXBHTVJ0QWZ4bVV1aVJvRHI2eFNiczJ6SWRZcVgwcloxSnlBaXdqTUpNa2RBWWZmMGw1UUlwK1VVOTNYQ01nawpuSmFoc3JzRDdpcVluanNDZ2dFQkFQUTdQcVFTay9wY2ltdS9LSVhRSHhEN0N2Q2txY3I4UGpWTVo0NG5jMk5vCkZROEU2UzgxQmNFVEVDWmx4MVRyVkFVVmdFajhwTG9rTytHcUlzMGtHMHduQUNITVZLMS9nbE4vRCtSbm9zYXYKR1hmbnpnRGJzVVlNcDRCcDAwK1Uyc1ByWGFyOFU3ZWRZSnM2ZmhKcTAvbC85Y3BHcTByUHlncVZiWUd5KzBsaQpiR2NkMkxvTEhOdmdRS2dLZys1Zmg2WHFMU3lXWnJLcWQxVnhmSGVyZ01GUis4RXhodXNXRlpLTzFHZ1FPMzc2CjRsV0ZuUDBtMnJLMjlzSDJ4bWE4UVE0STFjRS95VEhBTDNOTUoyTCtTQjlHUy91ZGc5WGczdFI4dytZdW1EN3EKR09GeWdtTmZzR1NYYXpkRWxBTEFHRElENUEwd0UvRWdselNCanpxM3VsOENnZ0VBWTE2SGdMQ2tiTlVEQ0xRTQoxVTlxTEV4WkZKVnFSM3N2RTdva0Z2L05yMUVGL2VlaThKVW5VUitydUtBTTdLUWVzeGlqNDM3bkZEUHd3RllaCk9JS3FwRGhBS3JVRTBTWGJ6THB3R2NnRmh3VW9QTU1kMDRvWXpoS1k0QjdRU1IvNlFKQ0lKaXVMaS9PYitJT0UKamJQMkV2enJZREZVWTVZc3JNV29xTVRxN2kxeXdTQWR1N005RFUxWlgvREZtRExKaklvNlFXbW5QRzZGVHowQwpWODV6YXUrU29JUXBKVmJ5S0c2Uy85TVJ2Wkdqc0E1UVlKeUdqYUJzSjBvclFsdFZ1Y2xvWXJVYkI4dzVSSEtUCnZra0VoSzlaRVFmbVp0ei8vSFQxdEViQ1B1dU52RFhMdTkycWtUTmRTSGVYaEgyaG5MbkpRQ1Mzc2V5RVdTeFgKbUNHRHBRS0NBUUVBaTNyYVIzR2t1VExvaXFoZFNDNlh6MmJQMUtiMW9VdDFhNUw3QVNCZXNjTGJZL3gxLzlQVQpPWFBkb1ZBM0NyUnJBNHhIKzJidDNMQ2Mwa0FNS0FRYTR0N1RJSHBGVWVDa1dYTVRiR29UZUV5L3lzN0R3NUcwCktFRkoxL2lZQ2JjRlNTYStFOHlQTXluWjVrejlleDh2ZUNvd0FSbGk4aExCWEZJQ2ZEUHZkdldTMjBFY2FRTzMKczRyYTRoMC9RMytqUkluOHlwNEtnTGNCOS9ZY0Uyd0syRjB0M2lPZTNkdDY3bnhMcWpLN0I4WFlST2ROeFBYUApxSWo5VzhESGhoeTFPb0twTVBod3VzejdUR21OaE9lYjRPQ1F2RjQwMEl6Z05aSWJmdlhWVlBqMHhLeFU4dFBQCk5XT1VnN2ZTbjg5OUFmTmU1bmt5cWw3bWU4SVNQb0ozR1FLQ0FRQlBhOVZwM0pkOE9CZUlQelhsc2lPS2haSFIKYS94a1BqbnBVMkJvZzAzYllEcXdjWFRnOWtOWTc2U3FteXNaSFlkTDFCc2NLUFplZzc2SS9hbXhxYTZib29wWgpzTHAzOEQ0dk1NNk9ZeVhzaC9zUk82NDVuQjhIMGpzcGN4eXhySzF1MlBiK3M0MWZXTGVGNC9NWWdiMFFtbFFDCkYyZEJwVTkvZHJSQjhRNmd6NnFHeFQ5THk0SUxnazFhU05HMTRTTCtQWGg5amlPem92c05IY1FSbWI1eldJVkgKbURrdGdoMFh0S3NKVStBbEtrMHVoaUxZeGZNd3U3ZDhreENEdmhGYmRUQ3BIWjdNbzEzdW1udmNJRjllbnRoZApRc1BiYm5TK2JFVHZYckVWajA3R01rU0FJcjBkY0ZONldyTjRuTTVDcmpmMk1NQmJTSDFnR2J6QjZmTzEKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K", + "external_public_key": "", + "enable_jwt_provider": true, + "jwk_url": "", + "jwt_user_path": "email", + "issuer_id": "d515ef82-5579-44d0-9919-59d273dca7c0" + }, + "websocket_config": { + "write_wait": 10, + "pong_wait": 60 + } + } ] \ No newline at end of file diff --git a/nitrite-replication/src/test/resources/mongo-seed/serverConfig.json b/nitrite-replication/src/test/resources/mongo-seed/serverConfig.json new file mode 100644 index 000000000..5d5ca6c68 --- /dev/null +++ b/nitrite-replication/src/test/resources/mongo-seed/serverConfig.json @@ -0,0 +1,29 @@ +[ + { + "_id": "serverConfig", + "alert_config": { + "log_format": "text", + "response_time_threshold": 20, + "sentry_dsn": "", + "enable_syslog": false, + "syslog_address": "", + "slack_channel": "", + "email_address": "", + "air_brake_id": -1, + "air_brake_key": "", + "hide_sensitive_data": true + }, + "email_config": { + "smtp_host": "smtp.gmail.com", + "smtp_port": "587", + "auth_required": true, + "sender": "", + "password": "" + }, + "tls_config": { + "enable_ssl": false, + "server_key": "", + "server_cert": "" + } + } +] \ No newline at end of file From 7218a3e782d35078ca770ee8c856b05182c1c852 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sat, 23 Oct 2021 14:35:43 +0530 Subject: [PATCH 23/78] change roles --- .../src/test/java/org/dizitart/no2/integration/UserClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java index eff7705eb..1ce2848bb 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java @@ -55,7 +55,7 @@ public static void createUser(String host, Integer port, String user) throws Exc "\"password\":\"chang3me\"," + "\"firstName\":\"Anindya\"," + "\"lastName\":\"Chatterjee\"," + - "\"roles\": [\"admin\"]" + + "\"roles\": [\"1\"]" + "}"; RequestBody body = RequestBody.create( From d4a9423e98f685741e5a94833a1ca7c7646cb519 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 25 Oct 2021 02:23:37 +0530 Subject: [PATCH 24/78] minor changes in server response --- .../org/dizitart/no2/sync/ReplicaBuilder.java | 19 ----------- .../no2/integration/BooleanResponse.java | 28 ++++++++++++++++ .../no2/integration/DataGateResponse.java | 32 +++++++++++++++++++ .../org/dizitart/no2/integration/Token.java | 29 +++++++++++++++++ .../dizitart/no2/integration/UserClient.java | 18 +++++------ 5 files changed, 97 insertions(+), 29 deletions(-) create mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/BooleanResponse.java create mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateResponse.java create mode 100644 nitrite-replication/src/test/java/org/dizitart/no2/integration/Token.java diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java index 6d58543cf..f109bc14d 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java @@ -25,9 +25,7 @@ import org.dizitart.no2.sync.event.ReplicationEventListener; import org.dizitart.no2.sync.module.DocumentModule; -import java.math.BigInteger; import java.net.Proxy; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -150,19 +148,6 @@ public ReplicaBuilder jwtAuth(String userName, String authToken) { return this; } - /** - * Sets the basic auth token. - * - * @param userName the username - * @param password the password - * @return the replica builder - */ - public ReplicaBuilder basicAuth(String userName, String password) { - this.authToken = toHex(userName + ":" + password); - this.userName = userName; - return this; - } - /** * Sets the connection timeout. * @@ -290,10 +275,6 @@ private Request.Builder createRequestBuilder() { return builder; } - private String toHex(String arg) { - return String.format("%040x", new BigInteger(1, arg.getBytes(StandardCharsets.UTF_8))); - } - private int getTimeoutInMillis(TimeSpan connectTimeout) { return Math.toIntExact(connectTimeout.getTimeUnit().toMillis(connectTimeout.getTime())); } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/BooleanResponse.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/BooleanResponse.java new file mode 100644 index 000000000..3df6c7a0c --- /dev/null +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/BooleanResponse.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import lombok.Data; + +/** + * @author Anindya Chatterjee + */ +@Data +public class BooleanResponse { + private Boolean result; +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateResponse.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateResponse.java new file mode 100644 index 000000000..b2f4b3b00 --- /dev/null +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateResponse.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * @author Anindya Chatterjee + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class DataGateResponse { + private T data; + private Integer code; + private String message; +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/Token.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/Token.java new file mode 100644 index 000000000..5b1e222ab --- /dev/null +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/Token.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import lombok.Data; + +/** + * @author Anindya Chatterjee + */ +@Data +public class Token { + private String token; + private Long expiresAt; +} diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java index 1ce2848bb..d19f3dc8c 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java @@ -17,7 +17,7 @@ package org.dizitart.no2.integration; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import okhttp3.*; @@ -39,11 +39,10 @@ public static void createUser(String host, Integer port, String user) throws Exc response = call.execute(); ObjectMapper mapper = new ObjectMapper(); assert response.body() != null; - JsonNode jsonNode = mapper.readValue(response.body().string(), JsonNode.class); - if (jsonNode.has("exists")) { - if (jsonNode.get("exists").asBoolean()) { - return; - } + DataGateResponse dataGateResponse = mapper.readValue(response.body().string(), + new TypeReference>() {}); + if (dataGateResponse.getData().getResult()) { + return; } } catch (Exception e) { log.error("Error checking user " + user, e); @@ -96,11 +95,10 @@ public static String getToken(String host, Integer port, String user) throws Exc assert response.body() != null; json = response.body().string(); ObjectMapper mapper = new ObjectMapper(); - JsonNode jsonNode = mapper.readValue(json, JsonNode.class); + DataGateResponse dataGateResponse = mapper.readValue(json, + new TypeReference>() {}); - if (jsonNode.has("token")) { - return jsonNode.get("token").asText(); - } + return dataGateResponse.getData().getToken(); } throw new Exception("failed to login"); From ccae3fbf4759acbda2ef14f3ddc3d70b5c0df18f Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sat, 5 Feb 2022 14:17:19 +0530 Subject: [PATCH 25/78] permission changed --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From 8ab3be6387f160c0ab64812474296a4ba8bc1cbc Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sat, 5 Feb 2022 19:26:19 +0530 Subject: [PATCH 26/78] test fixed for mac --- .../org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java index 0f4d3087d..751439194 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java @@ -78,11 +78,12 @@ public void testConstructor2() { @Test public void testFilePath() { + String tempPath = System.getProperty("java.io.tmpdir"); MVStoreModuleBuilder withConfigResult = MVStoreModule.withConfig(); MVStoreModuleBuilder actualFilePathResult = withConfigResult - .filePath(Paths.get(System.getProperty("java.io.tmpdir"), "test.txt").toFile()); + .filePath(Paths.get(tempPath, "test.txt").toFile()); assertSame(withConfigResult, actualFilePathResult); - assertEquals("/tmp/test.txt", actualFilePathResult.filePath()); + assertEquals(tempPath + "test.txt", actualFilePathResult.filePath()); } @Test From 04879ecae1b0e051a054ea398b0188dd20095d5a Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sat, 5 Feb 2022 20:22:04 +0530 Subject: [PATCH 27/78] replication int test ignored --- .../org/dizitart/no2/integration/DataGateIntegrationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index 1ec0af948..f19431be6 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -32,6 +32,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.Ignore; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.containers.Network; @@ -56,6 +57,7 @@ * @author Anindya Chatterjee */ @Slf4j +@Ignore public class DataGateIntegrationTest { private final Network network = Network.newNetwork(); private String dbFile1, dbFile2; From 540045a83f142ae8575922d7d0943a243e828f79 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sat, 5 Feb 2022 23:11:59 +0530 Subject: [PATCH 28/78] Update MVStoreModuleBuilderTest.java --- .../java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java index 751439194..ff388a2ad 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/MVStoreModuleBuilderTest.java @@ -83,7 +83,7 @@ public void testFilePath() { MVStoreModuleBuilder actualFilePathResult = withConfigResult .filePath(Paths.get(tempPath, "test.txt").toFile()); assertSame(withConfigResult, actualFilePathResult); - assertEquals(tempPath + "test.txt", actualFilePathResult.filePath()); + assertEquals(Paths.get(tempPath, "test.txt").toString(), actualFilePathResult.filePath()); } @Test From 451f3a5e3407b9f4c0f742e4d38800c80e71d351 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 29 Mar 2022 17:38:00 +0530 Subject: [PATCH 29/78] rocksdb mac m1 support --- nitrite-bom/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitrite-bom/build.gradle b/nitrite-bom/build.gradle index f4773571c..c08d8a80b 100644 --- a/nitrite-bom/build.gradle +++ b/nitrite-bom/build.gradle @@ -31,7 +31,7 @@ dependencies { api "org.mapdb:mapdb:3.0.8" api "com.h2database:h2-mvstore:1.4.200" api "com.squareup.okhttp3:okhttp:4.9.3" - api "org.rocksdb:rocksdbjni:6.26.1" + api "org.rocksdb:rocksdbjni:7.0.3" api "com.esotericsoftware.kryo:kryo5:5.2.1" api "org.locationtech.jts:jts-core:1.18.2" api "commons-codec:commons-codec:1.15" From 4b0cace93a607c9fa9f8fe6ab2098fe1c042ffb9 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 29 Mar 2022 18:40:51 +0530 Subject: [PATCH 30/78] android gradle plugin upgrade --- nitrite-android-example/build.gradle | 6 +++--- nitrite-android-example/src/main/AndroidManifest.xml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/nitrite-android-example/build.gradle b/nitrite-android-example/build.gradle index 7b97e1c43..008fb4204 100644 --- a/nitrite-android-example/build.gradle +++ b/nitrite-android-example/build.gradle @@ -21,7 +21,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.android.tools.build:gradle:7.0.4' } } @@ -46,12 +46,12 @@ android { exclude 'META-INF/LGPL2.1' } - compileSdkVersion 30 + compileSdkVersion 32 defaultConfig { applicationId "org.dizitart.no2.example.android" minSdkVersion 19 - targetSdkVersion 30 + targetSdkVersion 32 versionCode 1 versionName "1.0" diff --git a/nitrite-android-example/src/main/AndroidManifest.xml b/nitrite-android-example/src/main/AndroidManifest.xml index 4289a215c..1294547f6 100644 --- a/nitrite-android-example/src/main/AndroidManifest.xml +++ b/nitrite-android-example/src/main/AndroidManifest.xml @@ -29,7 +29,8 @@ + android:theme="@style/AppTheme.NoActionBar" + android:exported="false"> From e2772a0b15d10df86116c26a38e1544b59010771 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 29 Mar 2022 18:58:07 +0530 Subject: [PATCH 31/78] default build is java 11 --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ae828f073..e6b8f9a70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '8', '11' ] + java: [ '11', '17' ] env: PGP_KEY_PASSWORD: ${{ secrets.PGP_KEY_PASSWORD }} MAVEN_USERNAME: ${{ secrets.OSSRH_USER }} @@ -53,7 +53,7 @@ jobs: uses: actions/setup-java@v2.5.0 with: distribution: 'zulu' - java-version: '8' + java-version: '11' java-package: jdk architecture: x64 From fc6e90a15d70d1af59a5688c2e1fc2c7d655f9e3 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 29 Mar 2022 19:00:52 +0530 Subject: [PATCH 32/78] java 11 only build --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e6b8f9a70..dffb648eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '11', '17' ] + java: [ '11' ] env: PGP_KEY_PASSWORD: ${{ secrets.PGP_KEY_PASSWORD }} MAVEN_USERNAME: ${{ secrets.OSSRH_USER }} From 1f945d1bbe8a4b6f9900bf0703b3a22c81af8914 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 5 May 2022 01:20:12 +0530 Subject: [PATCH 33/78] processor changes --- .../repository/FieldProcessorTest.java | 21 +++--------- .../repository/RepositoryFactoryTest.java | 5 --- .../collection/FieldProcessorTest.java | 12 +------ .../repository/FieldProcessorTest.java | 21 +++--------- .../repository/RepositoryFactoryTest.java | 5 --- .../collection/FieldProcessorTest.java | 12 +------ .../repository/FieldProcessorTest.java | 21 +++--------- .../repository/RepositoryFactoryTest.java | 5 --- .../collection/DefaultNitriteCollection.java | 13 -------- .../operation/CollectionOperations.java | 32 ++----------------- .../no2/common/PersistentCollection.java | 7 ---- .../no2/common/processors/Processor.java | 31 ++++++++++++++++++ .../repository/DefaultObjectRepository.java | 6 ---- .../DefaultTransactionalCollection.java | 20 ------------ .../DefaultTransactionalRepository.java | 5 --- .../collection/FieldProcessorTest.java | 12 +------ .../repository/FieldProcessorTest.java | 21 +++--------- .../repository/RepositoryFactoryTest.java | 5 --- .../DefaultObjectRepositoryTest.java | 12 ------- .../DefaultTransactionalRepositoryTest.java | 10 ------ 20 files changed, 52 insertions(+), 224 deletions(-) diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java index 397876740..ad53f1c91 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java @@ -63,6 +63,10 @@ public void setUp() { persons.insert(person); + // process existing data + fieldProcessor.process(persons); + + // add for further changes persons.addProcessor(fieldProcessor); person = new EncryptedPerson(); @@ -187,21 +191,4 @@ public void testIndexOnEncryptedField() { EncryptedPerson person = persons.find(where("cvv").eq("008")).firstOrNull(); assertNull(person); } - - @Test - public void testRemoveProcessor() { - EncryptedPerson person = persons.find(where("cvv").eq("008")).firstOrNull(); - assertNull(person); - - person = persons.find(where("creditCardNumber").eq("5548960345687452")).firstOrNull(); - assertNull(person); - - persons.removeProcessor(fieldProcessor); - - person = persons.find(where("cvv").eq("008")).firstOrNull(); - assertNotNull(person); - - person = persons.find(where("creditCardNumber").eq("5548960345687452")).firstOrNull(); - assertNotNull(person); - } } diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 2de2e8915..8dad35a5c 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -134,11 +134,6 @@ public void addProcessor(Processor processor) { } - @Override - public void removeProcessor(Processor processor) { - - } - @Override public void createIndex(IndexOptions indexOptions, String... fields) { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java index 0055f97ac..08cf8fc9f 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java @@ -88,6 +88,7 @@ public Document processAfterRead(Document document) { .put("expiryDate", new Date()); collection.insert(document); + cvvProcessor.process(collection); collection.addProcessor(cvvProcessor); } @@ -198,15 +199,4 @@ public void testIndexOnEncryptedField() { Document document = collection.find(where("cvv").eq("008")).firstOrNull(); assertNull(document); } - - @Test - public void testRemoveProcessor() { - Document document = collection.find(where("cvv").eq("008")).firstOrNull(); - assertNull(document); - - collection.removeProcessor(cvvProcessor); - - document = collection.find(where("cvv").eq("008")).firstOrNull(); - assertNotNull(document); - } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java index 73a093082..8f31d6408 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java @@ -63,6 +63,10 @@ public void setUp() { persons.insert(person); + // process existing data + fieldProcessor.process(persons); + + // add for further changes persons.addProcessor(fieldProcessor); person = new EncryptedPerson(); @@ -187,21 +191,4 @@ public void testIndexOnEncryptedField() { EncryptedPerson person = persons.find(where("cvv").eq("008")).firstOrNull(); assertNull(person); } - - @Test - public void testRemoveProcessor() { - EncryptedPerson person = persons.find(where("cvv").eq("008")).firstOrNull(); - assertNull(person); - - person = persons.find(where("creditCardNumber").eq("5548960345687452")).firstOrNull(); - assertNull(person); - - persons.removeProcessor(fieldProcessor); - - person = persons.find(where("cvv").eq("008")).firstOrNull(); - assertNotNull(person); - - person = persons.find(where("creditCardNumber").eq("5548960345687452")).firstOrNull(); - assertNotNull(person); - } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index d617767c0..b0acc5ee2 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -136,11 +136,6 @@ public void addProcessor(Processor processor) { } - @Override - public void removeProcessor(Processor processor) { - - } - @Override public void createIndex(IndexOptions indexOptions, String... fields) { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java index 0055f97ac..08cf8fc9f 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java @@ -88,6 +88,7 @@ public Document processAfterRead(Document document) { .put("expiryDate", new Date()); collection.insert(document); + cvvProcessor.process(collection); collection.addProcessor(cvvProcessor); } @@ -198,15 +199,4 @@ public void testIndexOnEncryptedField() { Document document = collection.find(where("cvv").eq("008")).firstOrNull(); assertNull(document); } - - @Test - public void testRemoveProcessor() { - Document document = collection.find(where("cvv").eq("008")).firstOrNull(); - assertNull(document); - - collection.removeProcessor(cvvProcessor); - - document = collection.find(where("cvv").eq("008")).firstOrNull(); - assertNotNull(document); - } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java index 73a093082..8f31d6408 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java @@ -63,6 +63,10 @@ public void setUp() { persons.insert(person); + // process existing data + fieldProcessor.process(persons); + + // add for further changes persons.addProcessor(fieldProcessor); person = new EncryptedPerson(); @@ -187,21 +191,4 @@ public void testIndexOnEncryptedField() { EncryptedPerson person = persons.find(where("cvv").eq("008")).firstOrNull(); assertNull(person); } - - @Test - public void testRemoveProcessor() { - EncryptedPerson person = persons.find(where("cvv").eq("008")).firstOrNull(); - assertNull(person); - - person = persons.find(where("creditCardNumber").eq("5548960345687452")).firstOrNull(); - assertNull(person); - - persons.removeProcessor(fieldProcessor); - - person = persons.find(where("cvv").eq("008")).firstOrNull(); - assertNotNull(person); - - person = persons.find(where("creditCardNumber").eq("5548960345687452")).firstOrNull(); - assertNotNull(person); - } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 3c89260ac..f4ed0c831 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -136,11 +136,6 @@ public void addProcessor(Processor processor) { } - @Override - public void removeProcessor(Processor processor) { - - } - @Override public void createIndex(IndexOptions indexOptions, String... fields) { diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 403feae0e..200f12e2e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -90,19 +90,6 @@ public void addProcessor(Processor processor) { } } - @Override - public void removeProcessor(Processor processor) { - notNull(processor, "a null processor cannot be removed"); - - try { - writeLock.lock(); - checkOpened(); - collectionOperations.removeProcessor(processor); - } finally { - writeLock.unlock(); - } - } - public WriteResult insert(Document[] documents) { notNull(documents, "a null document cannot be inserted"); containsNull(documents, "a null document cannot be inserted"); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java index 836bdca44..385243eb8 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java @@ -24,18 +24,15 @@ import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.event.EventBus; -import org.dizitart.no2.filters.Filter; -import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.common.processors.ProcessorChain; +import org.dizitart.no2.filters.Filter; +import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.StoreCatalog; import java.util.Collection; -import static org.dizitart.no2.collection.UpdateOptions.updateOptions; -import static org.dizitart.no2.common.util.DocumentUtils.createUniqueFilter; - /** * The collection operations. * @@ -77,20 +74,9 @@ public CollectionOperations(String collectionName, * @param processor the processor */ public void addProcessor(Processor processor) { - doProcess(processor); processorChain.add(processor); } - /** - * Removes a document processor. - * - * @param processor the processor - */ - public void removeProcessor(Processor processor) { - processorChain.remove(processor); - undoProcess(processor); - } - /** * Creates index. * @@ -290,18 +276,4 @@ private void dropNitriteMap() { // drop the map nitriteMap.drop(); } - - private void doProcess(Processor processor) { - for (Document document : find(Filter.ALL, null)) { - Document processed = processor.processBeforeWrite(document); - update(createUniqueFilter(document), processed, updateOptions(false)); - } - } - - private void undoProcess(Processor processor) { - for (Document document : find(Filter.ALL, null)) { - Document processed = processor.processAfterRead(document); - update(createUniqueFilter(document), processed, updateOptions(false)); - } - } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java index 601657824..928e7b691 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java @@ -55,13 +55,6 @@ public interface PersistentCollection extends EventAware, MetadataAware, Auto */ void addProcessor(Processor processor); - /** - * Removes a data processor from this collection. - * - * @param processor the processor - */ - void removeProcessor(Processor processor); - /** * Creates an unique index on the {@code fields}, if not already exists. * diff --git a/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java b/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java index abad37dc6..829de5888 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java @@ -18,6 +18,13 @@ package org.dizitart.no2.common.processors; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.PersistentCollection; +import org.dizitart.no2.filters.Filter; +import org.dizitart.no2.repository.ObjectRepository; + +import static org.dizitart.no2.collection.UpdateOptions.updateOptions; +import static org.dizitart.no2.common.util.DocumentUtils.createUniqueFilter; /** * Represents a document processor. @@ -26,6 +33,7 @@ * @since 4.0 */ public interface Processor { + /** * Processes a document before writing it into database. * @@ -41,4 +49,27 @@ public interface Processor { * @return the document */ Document processAfterRead(Document document); + + /** + * Processes all documents of a {@link PersistentCollection}. + * + * @param collection the collection to process + */ + default void process(PersistentCollection collection) { + NitriteCollection nitriteCollection = null; + if (collection instanceof NitriteCollection) { + nitriteCollection = (NitriteCollection) collection; + } else if (collection instanceof ObjectRepository) { + ObjectRepository repository = (ObjectRepository) collection; + nitriteCollection = repository.getDocumentCollection(); + } + + if (nitriteCollection != null) { + for (Document document : nitriteCollection.find(Filter.ALL, null)) { + Document processed = processBeforeWrite(document); + nitriteCollection.update(createUniqueFilter(document), processed, updateOptions(false)); + } + } + } } + diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java b/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java index f6bebc643..9094249ed 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java @@ -60,12 +60,6 @@ public void addProcessor(Processor processor) { collection.addProcessor(processor); } - @Override - public void removeProcessor(Processor processor) { - notNull(processor, "a null processor cannot be removed"); - collection.removeProcessor(processor); - } - @Override public void createIndex(IndexOptions indexOptions, String... fields) { collection.createIndex(indexOptions, fields); diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index 30b01c4ac..4d8ff5109 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -46,7 +46,6 @@ class DefaultTransactionalCollection implements NitriteCollection { private final NitriteCollection primary; private final TransactionContext transactionContext; private final Nitrite nitrite; - private String collectionName; private NitriteMap nitriteMap; private NitriteStore nitriteStore; @@ -283,25 +282,6 @@ public void addProcessor(Processor processor) { JournalEntry journalEntry = new JournalEntry(); journalEntry.setChangeType(ChangeType.AddProcessor); journalEntry.setCommit(() -> primary.addProcessor(processor)); - journalEntry.setRollback(() -> primary.removeProcessor(processor)); - transactionContext.getJournal().add(journalEntry); - } - - @Override - public void removeProcessor(Processor processor) { - notNull(processor, "a null processor cannot be removed"); - try { - writeLock.lock(); - checkOpened(); - collectionOperations.addProcessor(processor); - } finally { - writeLock.unlock(); - } - - JournalEntry journalEntry = new JournalEntry(); - journalEntry.setChangeType(ChangeType.RemoveProcessor); - journalEntry.setCommit(() -> primary.removeProcessor(processor)); - journalEntry.setRollback(() -> primary.addProcessor(processor)); transactionContext.getJournal().add(journalEntry); } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java index 54bb8c62a..a2be48685 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java @@ -50,11 +50,6 @@ public void addProcessor(Processor processor) { backingCollection.addProcessor(processor); } - @Override - public void removeProcessor(Processor processor) { - backingCollection.removeProcessor(processor); - } - @Override public void createIndex(IndexOptions indexOptions, String... fieldNames) { backingCollection.createIndex(indexOptions, fieldNames); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java index 0055f97ac..08cf8fc9f 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/FieldProcessorTest.java @@ -88,6 +88,7 @@ public Document processAfterRead(Document document) { .put("expiryDate", new Date()); collection.insert(document); + cvvProcessor.process(collection); collection.addProcessor(cvvProcessor); } @@ -198,15 +199,4 @@ public void testIndexOnEncryptedField() { Document document = collection.find(where("cvv").eq("008")).firstOrNull(); assertNull(document); } - - @Test - public void testRemoveProcessor() { - Document document = collection.find(where("cvv").eq("008")).firstOrNull(); - assertNull(document); - - collection.removeProcessor(cvvProcessor); - - document = collection.find(where("cvv").eq("008")).firstOrNull(); - assertNotNull(document); - } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java index be50bff32..5f5297a50 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/FieldProcessorTest.java @@ -63,6 +63,10 @@ public void setUp() { persons.insert(person); + // process existing data + fieldProcessor.process(persons); + + // add for further changes persons.addProcessor(fieldProcessor); person = new EncryptedPerson(); @@ -187,21 +191,4 @@ public void testIndexOnEncryptedField() { EncryptedPerson person = persons.find(where("cvv").eq("008")).firstOrNull(); assertNull(person); } - - @Test - public void testRemoveProcessor() { - EncryptedPerson person = persons.find(where("cvv").eq("008")).firstOrNull(); - assertNull(person); - - person = persons.find(where("creditCardNumber").eq("5548960345687452")).firstOrNull(); - assertNull(person); - - persons.removeProcessor(fieldProcessor); - - person = persons.find(where("cvv").eq("008")).firstOrNull(); - assertNotNull(person); - - person = persons.find(where("creditCardNumber").eq("5548960345687452")).firstOrNull(); - assertNotNull(person); - } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index d29b4cf22..3aaac1d5c 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -129,11 +129,6 @@ public void addProcessor(Processor processor) { } - @Override - public void removeProcessor(Processor processor) { - - } - @Override public void createIndex(IndexOptions indexOptions, String... fields) { diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java index 726cce323..25de0ba32 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java @@ -55,18 +55,6 @@ public void testAddProcessor() { assertNull(defaultObjectRepository.getAttributes()); } - @Test - public void testRemoveProcessor() { - NitriteCollection nitriteCollection = mock(NitriteCollection.class); - doNothing().when(nitriteCollection).removeProcessor(any()); - Class type = Object.class; - DefaultObjectRepository defaultObjectRepository = new DefaultObjectRepository<>(type, - nitriteCollection, new NitriteConfig()); - defaultObjectRepository.removeProcessor(new ProcessorChain()); - verify(nitriteCollection).removeProcessor(any()); - assertNull(defaultObjectRepository.getAttributes()); - } - @Test public void testCreateIndex() { NitriteCollection nitriteCollection = mock(NitriteCollection.class); diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java index efa732946..1e44b49fa 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java @@ -39,16 +39,6 @@ public void testAddProcessor() { verify(defaultTransactionalRepository).addProcessor(any()); } - @Test - public void testRemoveProcessor() { - DefaultTransactionalRepository defaultTransactionalRepository = (DefaultTransactionalRepository) mock( - DefaultTransactionalRepository.class); - doNothing().when(defaultTransactionalRepository) - .removeProcessor(any()); - defaultTransactionalRepository.removeProcessor(new ProcessorChain()); - verify(defaultTransactionalRepository).removeProcessor(any()); - } - @Test public void testCreateIndex() { DefaultTransactionalRepository defaultTransactionalRepository = (DefaultTransactionalRepository) mock( From 26b47b552415f86c6828239b808f1eb67a8cb902 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 19 May 2022 23:04:57 +0530 Subject: [PATCH 34/78] refactoring --- .../integration/migrate/MigrationTest.java | 2 +- .../repository/ObjectRepositoryTest.java | 6 ++--- .../integration/migration/MigrationTest.java | 2 +- .../repository/ObjectRepositoryTest.java | 6 ++--- .../integration/migrate/MigrationTest.java | 2 +- .../repository/ObjectRepositoryTest.java | 6 ++--- .../no2/support/NitriteJsonExporter.java | 2 +- .../main/java/org/dizitart/no2/Nitrite.java | 6 ++--- .../java/org/dizitart/no2/NitriteBuilder.java | 21 +++++---------- .../java/org/dizitart/no2/NitriteConfig.java | 10 +++---- .../org/dizitart/no2/NitriteDatabase.java | 2 +- .../no2/common/mapper/NitriteMapper.java | 2 +- .../org/dizitart/no2/migration/Migration.java | 14 +++++----- .../org/dizitart/no2/NitriteBuilderTest.java | 8 +++--- .../org/dizitart/no2/NitriteConfigTest.java | 26 +++++++++---------- .../repository/ObjectRepositoryTest.java | 6 ++--- 16 files changed, 57 insertions(+), 64 deletions(-) diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java index 16b83e0df..f44092187 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java @@ -129,7 +129,7 @@ public void migrate(Instructions instruction) { ObjectRepository newRepo = db.getRepository(NewClass.class); assertEquals(newRepo.size(), 10); assertTrue(db.listCollectionNames().isEmpty()); - assertTrue(db.listKeyedRepository().isEmpty()); + assertTrue(db.listKeyedRepositories().isEmpty()); assertEquals((int) db.getDatabaseMetaData().getSchemaVersion(), 2); } diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 85af75e2a..5424f06bb 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -277,7 +277,7 @@ public void testKeyedRepository() { assertTrue(db.hasRepository(Employee.class, "developers")); assertEquals(db.listRepositories().size(), 1); - assertEquals(db.listKeyedRepository().size(), 2); + assertEquals(db.listKeyedRepositories().size(), 2); assertEquals(employeeRepo.find(where("address").text("abcd")).size(), 1); assertEquals(employeeRepo.find(where("address").text("xyz")).size(), 1); @@ -306,7 +306,7 @@ public void testEntityRepository() { assertTrue(errored); assertTrue(db.listRepositories().contains("entity.employee")); - assertEquals(db.listKeyedRepository().size(), 2); + assertEquals(db.listKeyedRepositories().size(), 2); assertEquals(db.listCollectionNames().size(), 0); assertTrue(managerRepo.hasIndex("firstName")); @@ -315,7 +315,7 @@ public void testEntityRepository() { assertTrue(employeeRepo.hasIndex("lastName")); managerRepo.drop(); - assertEquals(db.listKeyedRepository().size(), 1); + assertEquals(db.listKeyedRepositories().size(), 1); } @Test diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java index 0a3fa50ee..1190ee868 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java @@ -124,7 +124,7 @@ public void migrate(Instructions instruction) { ObjectRepository newRepo = db.getRepository(NewClass.class); assertEquals(newRepo.size(), 10); assertTrue(db.listCollectionNames().isEmpty()); - assertTrue(db.listKeyedRepository().isEmpty()); + assertTrue(db.listKeyedRepositories().isEmpty()); assertEquals((int) db.getDatabaseMetaData().getSchemaVersion(), 2); } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 376d4b9c4..b2b0677d3 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -293,7 +293,7 @@ public void testKeyedRepository() { assertTrue(db.hasRepository(Employee.class, "developers")); assertEquals(db.listRepositories().size(), 1); - assertEquals(db.listKeyedRepository().size(), 2); + assertEquals(db.listKeyedRepositories().size(), 2); assertEquals(employeeRepo.find(where("address").text("abcd")).size(), 1); assertEquals(employeeRepo.find(where("address").text("xyz")).size(), 1); @@ -322,7 +322,7 @@ public void testEntityRepository() { assertTrue(errored); assertTrue(db.listRepositories().contains("entity.employee")); - assertEquals(db.listKeyedRepository().size(), 2); + assertEquals(db.listKeyedRepositories().size(), 2); assertEquals(db.listCollectionNames().size(), 0); assertTrue(managerRepo.hasIndex("firstName")); @@ -331,7 +331,7 @@ public void testEntityRepository() { assertTrue(employeeRepo.hasIndex("lastName")); managerRepo.drop(); - assertEquals(db.listKeyedRepository().size(), 1); + assertEquals(db.listKeyedRepositories().size(), 1); } @Test diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java index cb6128ba7..6e95491ad 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java @@ -123,7 +123,7 @@ public void migrate(Instructions instruction) { ObjectRepository newRepo = db.getRepository(NewClass.class); assertEquals(newRepo.size(), 10); assertTrue(db.listCollectionNames().isEmpty()); - assertTrue(db.listKeyedRepository().isEmpty()); + assertTrue(db.listKeyedRepositories().isEmpty()); assertEquals((int) db.getDatabaseMetaData().getSchemaVersion(), 2); } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 6b2cdc55a..740449af7 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -293,7 +293,7 @@ public void testKeyedRepository() { assertTrue(db.hasRepository(Employee.class, "developers")); assertEquals(db.listRepositories().size(), 1); - assertEquals(db.listKeyedRepository().size(), 2); + assertEquals(db.listKeyedRepositories().size(), 2); assertEquals(employeeRepo.find(where("address").text("abcd")).size(), 1); assertEquals(employeeRepo.find(where("address").text("xyz")).size(), 1); @@ -322,7 +322,7 @@ public void testEntityRepository() { assertTrue(errored); assertTrue(db.listRepositories().contains("entity.employee")); - assertEquals(db.listKeyedRepository().size(), 2); + assertEquals(db.listKeyedRepositories().size(), 2); assertEquals(db.listCollectionNames().size(), 0); assertTrue(managerRepo.hasIndex("firstName")); @@ -331,7 +331,7 @@ public void testEntityRepository() { assertTrue(employeeRepo.hasIndex("lastName")); managerRepo.drop(); - assertEquals(db.listKeyedRepository().size(), 1); + assertEquals(db.listKeyedRepositories().size(), 1); } @Test diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonExporter.java b/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonExporter.java index cb2fcff73..7b77e86e2 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonExporter.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonExporter.java @@ -60,7 +60,7 @@ public void exportData() throws IOException, ClassNotFoundException { if (collections.isEmpty()) { collectionNames = db.listCollectionNames(); repositoryNames = db.listRepositories(); - keyedRepositoryNames = db.listKeyedRepository(); + keyedRepositoryNames = db.listKeyedRepositories(); } else { collectionNames = new HashSet<>(); repositoryNames = new HashSet<>(); diff --git a/nitrite/src/main/java/org/dizitart/no2/Nitrite.java b/nitrite/src/main/java/org/dizitart/no2/Nitrite.java index d012d5bc4..a4f4be47c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/Nitrite.java +++ b/nitrite/src/main/java/org/dizitart/no2/Nitrite.java @@ -157,7 +157,7 @@ static NitriteBuilder builder() { * * @return the set of all registered classes' names. */ - Map> listKeyedRepository(); + Map> listKeyedRepositories(); /** * Checks whether the store has any unsaved changes. @@ -239,8 +239,8 @@ default boolean hasRepository(Class type) { */ default boolean hasRepository(Class type, String key) { checkOpened(); - return listKeyedRepository().containsKey(key) - && listKeyedRepository().get(key).contains(type.getName()); + return listKeyedRepositories().containsKey(key) + && listKeyedRepositories().get(key).contains(type.getName()); } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java b/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java index 18512c0b9..62ac8f134 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java @@ -85,19 +85,15 @@ public NitriteBuilder addMigrations(Migration... migrations) { * @return the nitrite builder */ public NitriteBuilder schemaVersion(Integer version) { - this.nitriteConfig.schemaVersion(version); + this.nitriteConfig.currentSchemaVersion(version); return this; } /** - * Opens or creates a new nitrite database backed by mvstore. If it is an in-memory store, + * Opens or creates a new nitrite database. If it is an in-memory store, * then it will create a new one. If it is a file based store, and if the file does not - * exists, then it will create a new file store and open; otherwise it will + * exist, then it will create a new file store and open; otherwise it will * open the existing file store. - *

- * NOTE: If the database is corrupted somehow then at the time of opening, it will - * try to repair it using the last known good version. If still it fails to - * recover, then it will throw a {@link org.dizitart.no2.exceptions.NitriteIOException}. * * @return the nitrite database instance. * @throws org.dizitart.no2.exceptions.NitriteIOException if unable to create a new in-memory database. @@ -106,28 +102,25 @@ public NitriteBuilder schemaVersion(Integer version) { */ public Nitrite openOrCreate() { this.nitriteConfig.autoConfigure(); + Runtime.getRuntime().addShutdownHook(new Thread(ThreadPoolManager::shutdownAllThreadPools)); return new NitriteDatabase(nitriteConfig); } /** - * Opens or creates a new nitrite database backed by mvstore. If it is an in-memory store, + * Opens or creates a new nitrite database. If it is an in-memory store, * then it will create a new one. If it is a file based store, and if the file does not - * exists, then it will create a new file store and open; otherwise it will + * exist, then it will create a new file store and open; otherwise it will * open the existing file store. *

* While creating a new database, it will use the specified user credentials. * While opening an existing database, it will use the specified credentials * to open it. *

- *

- * NOTE: If the database is corrupted somehow then at the time of opening, it will - * try to repair it using the last known good version. If still it fails to - * recover, then it will throw a {@link org.dizitart.no2.exceptions.NitriteIOException}. * * @param username the username * @param password the password * @return the nitrite database instance. - * @throws NitriteSecurityException if the user credentials are wrong or one of them is empty string. + * @throws NitriteSecurityException if the user credentials are wrong or one of them is empty string. * @throws org.dizitart.no2.exceptions.NitriteIOException if unable to create a new in-memory database. * @throws org.dizitart.no2.exceptions.NitriteIOException if the database is corrupt and recovery fails. * @throws org.dizitart.no2.exceptions.NitriteIOException if the directory does not exist. diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java b/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java index a432ae80e..3de5bf873 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java @@ -115,8 +115,8 @@ public NitriteConfig addMigration(Migration migration) { } if (migration != null) { - final int start = migration.getStartVersion(); - final int end = migration.getEndVersion(); + final int start = migration.getFromVersion(); + final int end = migration.getToVersion(); TreeMap targetMap = migrations.get(start); if (targetMap == null) { targetMap = new TreeMap<>(); @@ -137,7 +137,7 @@ public NitriteConfig addMigration(Migration migration) { * @param version the version * @return the nitrite config */ - public NitriteConfig schemaVersion(Integer version) { + public NitriteConfig currentSchemaVersion(Integer version) { if (configured) { throw new InvalidOperationException("cannot add schema version info after database" + " initialization"); @@ -147,7 +147,7 @@ public NitriteConfig schemaVersion(Integer version) { } /** - * Auto configures nitrite database with default configuration values and + * Autoconfigures nitrite database with default configuration values and * default built-in plugins. */ public void autoConfigure() { @@ -159,7 +159,7 @@ public void autoConfigure() { } /** - * Finds an {@link NitriteIndexer} by indexType. + * Finds a {@link NitriteIndexer} by indexType. * * @param indexType the type of {@link NitriteIndexer} to find. * @return the {@link NitriteIndexer} diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java index f355623ac..001288bc0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java @@ -124,7 +124,7 @@ public Set listRepositories() { } @Override - public Map> listKeyedRepository() { + public Map> listKeyedRepositories() { checkOpened(); return store.getKeyedRepositoryRegistry(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/NitriteMapper.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/NitriteMapper.java index 1f6e606ce..04aa1961b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/NitriteMapper.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/NitriteMapper.java @@ -19,7 +19,7 @@ import org.dizitart.no2.common.module.NitritePlugin; /** - * Represents a mapper which will convert a object of one type to an object of another type. + * Represents a mapper which will convert an object of one type to an object of another type. * * @author Anindya Chatterjee. * @since 4.0 diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/Migration.java b/nitrite/src/main/java/org/dizitart/no2/migration/Migration.java index 92af007be..05177f507 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/Migration.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/Migration.java @@ -15,22 +15,22 @@ public abstract class Migration { private final Queue migrationSteps; @Getter - private final Integer startVersion; + private final Integer fromVersion; @Getter - private final Integer endVersion; + private final Integer toVersion; private boolean executed = false; /** * Instantiates a new {@link Migration}. * - * @param startVersion the start version - * @param endVersion the end version + * @param fromVersion the start version + * @param toVersion the end version */ - public Migration(Integer startVersion, Integer endVersion) { - this.startVersion = startVersion; - this.endVersion = endVersion; + public Migration(Integer fromVersion, Integer toVersion) { + this.fromVersion = fromVersion; + this.toVersion = toVersion; this.migrationSteps = new LinkedList<>(); } diff --git a/nitrite/src/test/java/org/dizitart/no2/NitriteBuilderTest.java b/nitrite/src/test/java/org/dizitart/no2/NitriteBuilderTest.java index 59d877268..2eaec3571 100644 --- a/nitrite/src/test/java/org/dizitart/no2/NitriteBuilderTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/NitriteBuilderTest.java @@ -117,11 +117,11 @@ public void testAddMigrations() { public void testAddMigrations2() { NitriteBuilder builderResult = Nitrite.builder(); Migration migration = mock(Migration.class); - when(migration.getEndVersion()).thenReturn(1); - when(migration.getStartVersion()).thenReturn(1); + when(migration.getToVersion()).thenReturn(1); + when(migration.getFromVersion()).thenReturn(1); assertSame(builderResult, builderResult.addMigrations(migration, null, null)); - verify(migration).getEndVersion(); - verify(migration).getStartVersion(); + verify(migration).getToVersion(); + verify(migration).getFromVersion(); } @Test diff --git a/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java b/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java index 684d4840e..0da47a27a 100644 --- a/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java @@ -75,39 +75,39 @@ public void testAddMigration() { public void testAddMigration2() { NitriteConfig nitriteConfig = new NitriteConfig(); Migration migration = mock(Migration.class); - when(migration.getEndVersion()).thenReturn(1); - when(migration.getStartVersion()).thenReturn(1); + when(migration.getToVersion()).thenReturn(1); + when(migration.getFromVersion()).thenReturn(1); assertSame(nitriteConfig, nitriteConfig.addMigration(migration)); - verify(migration).getEndVersion(); - verify(migration).getStartVersion(); + verify(migration).getToVersion(); + verify(migration).getFromVersion(); } @Test public void testAddMigration3() { NitriteConfig nitriteConfig = new NitriteConfig(); Migration migration = mock(Migration.class); - when(migration.getEndVersion()).thenReturn(4); - when(migration.getStartVersion()).thenReturn(1); + when(migration.getToVersion()).thenReturn(4); + when(migration.getFromVersion()).thenReturn(1); assertSame(nitriteConfig, nitriteConfig.addMigration(migration)); - verify(migration).getEndVersion(); - verify(migration).getStartVersion(); + verify(migration).getToVersion(); + verify(migration).getFromVersion(); } @Test public void testAddMigration4() { NitriteConfig nitriteConfig = new NitriteConfig(); Migration migration = mock(Migration.class); - when(migration.getEndVersion()).thenReturn(1); - when(migration.getStartVersion()).thenReturn(4); + when(migration.getToVersion()).thenReturn(1); + when(migration.getFromVersion()).thenReturn(4); assertSame(nitriteConfig, nitriteConfig.addMigration(migration)); - verify(migration).getEndVersion(); - verify(migration).getStartVersion(); + verify(migration).getToVersion(); + verify(migration).getFromVersion(); } @Test public void testSchemaVersion() { NitriteConfig nitriteConfig = new NitriteConfig(); - NitriteConfig actualSchemaVersionResult = nitriteConfig.schemaVersion(1); + NitriteConfig actualSchemaVersionResult = nitriteConfig.currentSchemaVersion(1); assertSame(nitriteConfig, actualSchemaVersionResult); assertEquals(1, actualSchemaVersionResult.getSchemaVersion().intValue()); } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 0443fadae..bcc328549 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -282,7 +282,7 @@ public void testKeyedRepository() { assertTrue(db.hasRepository(Employee.class, "developers")); assertEquals(db.listRepositories().size(), 1); - assertEquals(db.listKeyedRepository().size(), 2); + assertEquals(db.listKeyedRepositories().size(), 2); assertEquals(employeeRepo.find(where("address").text("abcd")).size(), 1); assertEquals(employeeRepo.find(where("address").text("xyz")).size(), 1); @@ -311,7 +311,7 @@ public void testEntityRepository() { assertTrue(errored); assertTrue(db.listRepositories().contains("entity.employee")); - assertEquals(db.listKeyedRepository().size(), 2); + assertEquals(db.listKeyedRepositories().size(), 2); assertEquals(db.listCollectionNames().size(), 0); assertTrue(managerRepo.hasIndex("firstName")); @@ -320,7 +320,7 @@ public void testEntityRepository() { assertTrue(employeeRepo.hasIndex("lastName")); managerRepo.drop(); - assertEquals(db.listKeyedRepository().size(), 1); + assertEquals(db.listKeyedRepositories().size(), 1); } @Test From 9632c415dab17da8a111fb867525ed1df0c2fc87 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 20 May 2022 10:01:32 +0530 Subject: [PATCH 35/78] refactoring --- .../repository/ObjectRepositoryTest.java | 2 +- .../repository/RepositoryFactoryTest.java | 2 +- .../TransactionRepositoryTest.java | 2 +- .../dizitart/no2/mvstore/MVStoreUtils.java | 2 +- .../no2/mvstore/compat/v3/MigrationUtil.java | 2 +- .../collection/NitriteCollectionTest.java | 2 +- .../repository/ObjectRepositoryTest.java | 2 +- .../repository/RepositoryFactoryTest.java | 2 +- .../TransactionCollectionTest.java | 2 +- .../TransactionRepositoryTest.java | 2 +- .../org/dizitart/no2/sync/FeedLedger.java | 4 +-- .../no2/sync/ReplicatedCollection.java | 4 +-- .../crdt/ConflictFreeReplicatedDataType.java | 4 +-- .../test/java/org/dizitart/no2/TestUtils.java | 2 +- .../rocksdb/formatter/NitriteSerializers.java | 2 +- .../collection/NitriteCollectionTest.java | 2 +- .../repository/ObjectRepositoryTest.java | 2 +- .../repository/RepositoryFactoryTest.java | 2 +- .../TransactionCollectionTest.java | 2 +- .../TransactionRepositoryTest.java | 2 +- .../java/org/dizitart/no2/NitriteBuilder.java | 8 +++-- .../org/dizitart/no2/NitriteDatabase.java | 17 ++++------ .../collection/DefaultNitriteCollection.java | 2 +- .../operation/CollectionOperations.java | 2 +- .../no2/common/PersistentCollection.java | 4 +-- .../meta/Attributes.java | 12 ++++--- .../meta/AttributesAware.java} | 11 +++--- .../repository/DefaultObjectRepository.java | 2 +- .../org/dizitart/no2/store/NitriteMap.java | 6 ++-- .../DefaultTransactionalCollection.java | 2 +- .../DefaultTransactionalRepository.java | 2 +- .../org/dizitart/no2/NitriteDatabaseTest.java | 34 ++----------------- .../no2/collection/NitriteCollectionTest.java | 2 +- ...wareTest.java => AttributesAwareTest.java} | 12 ++++--- .../no2/collection/meta/AttributesTest.java | 1 + .../repository/ObjectRepositoryTest.java | 2 +- .../repository/RepositoryFactoryTest.java | 2 +- .../TransactionCollectionTest.java | 2 +- .../TransactionRepositoryTest.java | 2 +- .../DefaultObjectRepositoryTest.java | 2 +- .../DefaultTransactionalRepositoryTest.java | 2 +- .../org/dizitart/kno2/TransactionTest.kt | 2 +- 42 files changed, 76 insertions(+), 101 deletions(-) rename nitrite/src/main/java/org/dizitart/no2/{collection => common}/meta/Attributes.java (93%) rename nitrite/src/main/java/org/dizitart/no2/{collection/meta/MetadataAware.java => common/meta/AttributesAware.java} (81%) rename nitrite/src/test/java/org/dizitart/no2/collection/meta/{MetadataAwareTest.java => AttributesAwareTest.java} (69%) diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 5424f06bb..5239a9228 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -21,7 +21,7 @@ import lombok.Data; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.mapper.JacksonMapperModule; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexType; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 8dad35a5c..e3da6b307 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -20,7 +20,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.concurrent.LockService; import org.dizitart.no2.common.mapper.JacksonMapper; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 50af4f042..4f4cb996c 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -20,7 +20,7 @@ import com.github.javafaker.Faker; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.TransactionException; import org.dizitart.no2.index.IndexType; diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java index 48f387817..56923266f 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java @@ -17,7 +17,7 @@ package org.dizitart.no2.mvstore; import lombok.extern.slf4j.Slf4j; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.mvstore.compat.v3.MigrationUtil; diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java index 53300ab0b..08ba4791b 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java @@ -18,7 +18,7 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.DBNull; import org.dizitart.no2.common.Fields; import org.dizitart.no2.exceptions.NitriteIOException; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/NitriteCollectionTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/NitriteCollectionTest.java index ad3534435..ee6963f91 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/NitriteCollectionTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/NitriteCollectionTest.java @@ -18,7 +18,7 @@ package org.dizitart.no2.integration.collection; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.junit.Test; import static org.junit.Assert.assertEquals; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index b2b0677d3..0f9344987 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -24,7 +24,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.MappableMapper; import org.dizitart.no2.common.mapper.NitriteMapper; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index b0acc5ee2..695d62f53 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -20,7 +20,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.concurrent.LockService; import org.dizitart.no2.common.processors.Processor; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java index 05a2b095b..816ce394e 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java @@ -21,7 +21,7 @@ import org.dizitart.no2.integration.collection.BaseCollectionTest; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.TransactionException; import org.dizitart.no2.index.IndexType; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 5e93303ed..52e8bc641 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -22,7 +22,7 @@ import org.dizitart.no2.integration.repository.data.SubEmployee; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.TransactionException; import org.dizitart.no2.index.IndexType; diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java index e913c7621..e68ce7571 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/FeedLedger.java @@ -20,7 +20,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.sync.crdt.DeltaStates; import org.dizitart.no2.sync.message.Receipt; @@ -29,7 +29,7 @@ import java.util.Map; import java.util.Set; -import static org.dizitart.no2.collection.meta.Attributes.FEED_LEDGER; +import static org.dizitart.no2.common.meta.Attributes.FEED_LEDGER; /** * @author Anindya Chatterjee diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java index cc0e6efd2..65613c9ee 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicatedCollection.java @@ -21,7 +21,7 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.WebSocket; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.sync.crdt.ConflictFreeReplicatedDataType; import org.dizitart.no2.sync.crdt.LastWriteWinMap; @@ -36,7 +36,7 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import static org.dizitart.no2.collection.meta.Attributes.REPLICA; +import static org.dizitart.no2.common.meta.Attributes.REPLICA; /** * @author Anindya Chatterjee diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java index 36b37695b..2773e31e2 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/crdt/ConflictFreeReplicatedDataType.java @@ -21,7 +21,7 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.filters.Filter; @@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.dizitart.no2.collection.FindOptions.orderBy; -import static org.dizitart.no2.collection.meta.Attributes.*; +import static org.dizitart.no2.common.meta.Attributes.*; import static org.dizitart.no2.common.Constants.DOC_MODIFIED; import static org.dizitart.no2.filters.FluentFilter.where; import static org.dizitart.no2.index.IndexOptions.indexOptions; diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java b/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java index 4f7af2d40..4ea16e259 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/TestUtils.java @@ -22,7 +22,7 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.Constants; import org.dizitart.no2.mvstore.MVStoreModule; import org.dizitart.no2.store.NitriteMap; diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java index 32e0267ef..d97e2bbf9 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java @@ -9,7 +9,7 @@ import com.esotericsoftware.kryo.kryo5.serializers.MapSerializer; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.index.DBValue; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/NitriteCollectionTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/NitriteCollectionTest.java index ad3534435..ee6963f91 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/NitriteCollectionTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/NitriteCollectionTest.java @@ -18,7 +18,7 @@ package org.dizitart.no2.integration.collection; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.junit.Test; import static org.junit.Assert.assertEquals; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 740449af7..546ae627a 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -22,7 +22,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.MappableMapper; import org.dizitart.no2.common.mapper.NitriteMapper; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index f4ed0c831..5078a31e9 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -20,7 +20,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.concurrent.LockService; import org.dizitart.no2.common.processors.Processor; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java index 05a2b095b..816ce394e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java @@ -21,7 +21,7 @@ import org.dizitart.no2.integration.collection.BaseCollectionTest; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.TransactionException; import org.dizitart.no2.index.IndexType; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 5e93303ed..52e8bc641 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -22,7 +22,7 @@ import org.dizitart.no2.integration.repository.data.SubEmployee; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.TransactionException; import org.dizitart.no2.index.IndexType; diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java b/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java index 62ac8f134..8aafb1913 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteBuilder.java @@ -103,7 +103,9 @@ public NitriteBuilder schemaVersion(Integer version) { public Nitrite openOrCreate() { this.nitriteConfig.autoConfigure(); Runtime.getRuntime().addShutdownHook(new Thread(ThreadPoolManager::shutdownAllThreadPools)); - return new NitriteDatabase(nitriteConfig); + NitriteDatabase db = new NitriteDatabase(nitriteConfig); + db.initialize(null, null); + return db; } /** @@ -128,6 +130,8 @@ public Nitrite openOrCreate() { public Nitrite openOrCreate(String username, String password) { this.nitriteConfig.autoConfigure(); Runtime.getRuntime().addShutdownHook(new Thread(ThreadPoolManager::shutdownAllThreadPools)); - return new NitriteDatabase(username, password, nitriteConfig); + NitriteDatabase db = new NitriteDatabase(nitriteConfig); + db.initialize(username, password); + return db; } } diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java index 001288bc0..8ec4dca52 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java @@ -60,16 +60,6 @@ class NitriteDatabase implements Nitrite { this.lockService = new LockService(); this.collectionFactory = new CollectionFactory(lockService); this.repositoryFactory = new RepositoryFactory(collectionFactory); - this.initialize(null, null); - } - - NitriteDatabase(String username, String password, NitriteConfig config) { - validateUserCredentials(username, password); - this.nitriteConfig = config; - this.lockService = new LockService(); - this.collectionFactory = new CollectionFactory(lockService); - this.repositoryFactory = new RepositoryFactory(collectionFactory); - this.initialize(username, password); } @Override @@ -206,6 +196,10 @@ public Session createSession() { } private void validateUserCredentials(String username, String password) { + if (isNullOrEmpty(username) && isNullOrEmpty(password)) { + return; + } + if (isNullOrEmpty(username)) { throw new NitriteSecurityException("username cannot be empty"); } @@ -214,7 +208,8 @@ private void validateUserCredentials(String username, String password) { } } - private void initialize(String username, String password) { + public void initialize(String username, String password) { + validateUserCredentials(username, password); try { nitriteConfig.initialize(); store = nitriteConfig.getNitriteStore(); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 200f12e2e..f87679616 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -20,7 +20,7 @@ import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.events.CollectionEventInfo; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.collection.operation.CollectionOperations; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.WriteResult; diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java index 385243eb8..3d91916f2 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java @@ -20,7 +20,7 @@ import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventInfo; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.event.EventBus; diff --git a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java index 928e7b691..564b259f4 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java @@ -21,7 +21,7 @@ import org.dizitart.no2.collection.events.CollectionEventListener; import org.dizitart.no2.collection.events.EventAware; import org.dizitart.no2.collection.events.EventType; -import org.dizitart.no2.collection.meta.MetadataAware; +import org.dizitart.no2.common.meta.AttributesAware; import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.common.util.Iterables; import org.dizitart.no2.index.IndexDescriptor; @@ -46,7 +46,7 @@ * @see ObjectRepository * @since 1.0 */ -public interface PersistentCollection extends EventAware, MetadataAware, AutoCloseable { +public interface PersistentCollection extends EventAware, AttributesAware, AutoCloseable { /** * Adds a data processor to this collection. diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java b/nitrite/src/main/java/org/dizitart/no2/common/meta/Attributes.java similarity index 93% rename from nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java rename to nitrite/src/main/java/org/dizitart/no2/common/meta/Attributes.java index 9a654648e..d338be0c3 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/meta/Attributes.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/meta/Attributes.java @@ -1,20 +1,21 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2022 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.collection.meta; +package org.dizitart.no2.common.meta; import lombok.Data; @@ -23,6 +24,7 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; /** @@ -108,7 +110,7 @@ public class Attributes implements Serializable { public Attributes() { attributes = new ConcurrentHashMap<>(); set(CREATED_TIME, Long.toString(System.currentTimeMillis())); - set(UNIQUE_ID, java.util.UUID.randomUUID().toString()); + set(UNIQUE_ID, UUID.randomUUID().toString()); } /** @@ -120,7 +122,7 @@ public Attributes(String collection) { attributes = new ConcurrentHashMap<>(); set(OWNER, collection); set(CREATED_TIME, Long.toString(System.currentTimeMillis())); - set(UNIQUE_ID, java.util.UUID.randomUUID().toString()); + set(UNIQUE_ID, UUID.randomUUID().toString()); } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/meta/MetadataAware.java b/nitrite/src/main/java/org/dizitart/no2/common/meta/AttributesAware.java similarity index 81% rename from nitrite/src/main/java/org/dizitart/no2/collection/meta/MetadataAware.java rename to nitrite/src/main/java/org/dizitart/no2/common/meta/AttributesAware.java index cf9ef2545..8ae092dd4 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/meta/MetadataAware.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/meta/AttributesAware.java @@ -1,29 +1,30 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2022 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.collection.meta; +package org.dizitart.no2.common.meta; /** * Interface to be implemented by database objects that wish to be - * aware of their metadata. + * aware of their metadata attributes. * * @author Anindya Chatterjee. * @since 1.0 */ -public interface MetadataAware { +public interface AttributesAware { /** * Returns the metadata attributes of an object. * diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java b/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java index 9094249ed..6a9ea6483 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java @@ -21,7 +21,7 @@ import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexDescriptor; diff --git a/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java b/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java index d273804ba..d77274bed 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java @@ -16,8 +16,8 @@ package org.dizitart.no2.store; -import org.dizitart.no2.collection.meta.Attributes; -import org.dizitart.no2.collection.meta.MetadataAware; +import org.dizitart.no2.common.meta.Attributes; +import org.dizitart.no2.common.meta.AttributesAware; import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.common.tuples.Pair; @@ -33,7 +33,7 @@ * @author Anindya Chatterjee. * @since 1.0 */ -public interface NitriteMap extends MetadataAware, AutoCloseable { +public interface NitriteMap extends AttributesAware, AutoCloseable { /** * Determines if the map contains a mapping for the * specified key. diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index 4d8ff5109..0c24ee4c5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -8,7 +8,7 @@ import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventInfo; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.collection.operation.CollectionOperations; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.WriteResult; diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java index a2be48685..466e5a420 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java @@ -5,7 +5,7 @@ import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexDescriptor; diff --git a/nitrite/src/test/java/org/dizitart/no2/NitriteDatabaseTest.java b/nitrite/src/test/java/org/dizitart/no2/NitriteDatabaseTest.java index ea64480e6..0611d5590 100644 --- a/nitrite/src/test/java/org/dizitart/no2/NitriteDatabaseTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/NitriteDatabaseTest.java @@ -17,48 +17,18 @@ package org.dizitart.no2; -import org.dizitart.no2.exceptions.NitriteIOException; -import org.dizitart.no2.exceptions.NitriteSecurityException; import org.dizitart.no2.store.memory.InMemoryStoreModule; import org.junit.Test; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; public class NitriteDatabaseTest { @Test public void testConstructor() { NitriteConfig nitriteConfig = new NitriteConfig(); nitriteConfig.loadModule(new InMemoryStoreModule()); - new NitriteDatabase("janedoe", "iloveyou", nitriteConfig); - assertTrue(nitriteConfig.configured); - } - - @Test - public void testConstructor2() { - assertThrows(NitriteSecurityException.class, () -> new NitriteDatabase("", "iloveyou", new NitriteConfig())); - } - - @Test - public void testConstructor3() { - assertThrows(NitriteSecurityException.class, () -> new NitriteDatabase("janedoe", "", new NitriteConfig())); - } - - @Test - public void testConstructor4() { - assertThrows(NitriteIOException.class, () -> new NitriteDatabase("janedoe", "iloveyou", null)); - } - - @Test(expected = NitriteIOException.class) - public void testConstructor5() { - NitriteConfig nitriteConfig = new NitriteConfig(); new NitriteDatabase(nitriteConfig); - assertTrue(nitriteConfig.configured); - } - - @Test - public void testConstructor6() { - assertThrows(NitriteIOException.class, () -> new NitriteDatabase(null)); + assertFalse(nitriteConfig.configured); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/NitriteCollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/NitriteCollectionTest.java index 37ec3a5dd..915803581 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/NitriteCollectionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/NitriteCollectionTest.java @@ -18,7 +18,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.integration.Retry; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.junit.After; import org.junit.Rule; import org.junit.Test; diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/meta/MetadataAwareTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesAwareTest.java similarity index 69% rename from nitrite/src/test/java/org/dizitart/no2/collection/meta/MetadataAwareTest.java rename to nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesAwareTest.java index 5fdfb97bf..75c2c61f1 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/meta/MetadataAwareTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesAwareTest.java @@ -1,14 +1,16 @@ package org.dizitart.no2.collection.meta; +import org.dizitart.no2.common.meta.Attributes; +import org.dizitart.no2.common.meta.AttributesAware; import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertNull; -public class MetadataAwareTest { +public class AttributesAwareTest { @Test public void testGetAttributes() { - MetadataAware metadataAware = new MetadataAware() { + AttributesAware attributesAware = new AttributesAware() { @Override public Attributes getAttributes() { return new Attributes(); @@ -19,12 +21,12 @@ public void setAttributes(Attributes attributes) { } }; - Assert.assertNotNull("This is a boilerplate assert on the result.", metadataAware.getAttributes()); + Assert.assertNotNull("This is a boilerplate assert on the result.", attributesAware.getAttributes()); } @Test public void testSetAttributes() { - MetadataAware metadataAware = new MetadataAware() { + AttributesAware attributesAware = new AttributesAware() { @Override public Attributes getAttributes() { return null; @@ -35,7 +37,7 @@ public void setAttributes(Attributes attributes) { assertNull(attributes); } }; - metadataAware.setAttributes(null); + attributesAware.setAttributes(null); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesTest.java index 4689d2aeb..5f651dbb7 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesTest.java @@ -1,5 +1,6 @@ package org.dizitart.no2.collection.meta; +import org.dizitart.no2.common.meta.Attributes; import org.junit.Test; import java.util.Map; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index bcc328549..efdb91cc2 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -23,7 +23,7 @@ import org.dizitart.no2.integration.Retry; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.MappableMapper; import org.dizitart.no2.common.mapper.NitriteMapper; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 3aaac1d5c..2e6273d05 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -22,7 +22,7 @@ import org.dizitart.no2.integration.TestUtil; import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.concurrent.LockService; import org.dizitart.no2.exceptions.ValidationException; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java index 05a2b095b..816ce394e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java @@ -21,7 +21,7 @@ import org.dizitart.no2.integration.collection.BaseCollectionTest; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.TransactionException; import org.dizitart.no2.index.IndexType; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 4e506726d..2e98a4561 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -20,7 +20,7 @@ import com.github.javafaker.Faker; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.TransactionException; import org.dizitart.no2.index.IndexType; diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java index 25de0ba32..7064c0b33 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java @@ -23,7 +23,7 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.processors.ProcessorChain; diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java index 1e44b49fa..bfbca34ee 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java @@ -19,7 +19,7 @@ import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.collection.meta.Attributes; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.processors.ProcessorChain; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexOptions; diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/TransactionTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/TransactionTest.kt index 8ec173dc8..b6f174100 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/TransactionTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/TransactionTest.kt @@ -20,7 +20,7 @@ package org.dizitart.kno2 import com.github.javafaker.Faker import org.dizitart.no2.collection.Document import org.dizitart.no2.collection.UpdateOptions -import org.dizitart.no2.collection.meta.Attributes +import org.dizitart.no2.common.meta.Attributes import org.dizitart.no2.exceptions.NitriteIOException import org.dizitart.no2.exceptions.TransactionException import org.dizitart.no2.filters.FluentFilter From 173abc228d0bf0a2b0adf5c9f69fdecac17e8544 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 23 May 2022 10:01:48 +0530 Subject: [PATCH 36/78] refactoring --- .../java/org/dizitart/no2/collection/NitriteDocument.java | 4 ++-- .../main/java/org/dizitart/no2/collection/NitriteId.java | 4 ++-- .../org/dizitart/no2/collection/SnowflakeIdGenerator.java | 6 +++--- .../src/main/java/org/dizitart/no2/common/Constants.java | 2 +- .../java/org/dizitart/no2/repository/RepositoryFactory.java | 2 +- .../src/main/java/org/dizitart/no2/store/StoreCatalog.java | 2 +- .../test/java/org/dizitart/no2/store/StoreCatalogTest.java | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java index dc2c1db44..088ce56f0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java @@ -59,10 +59,10 @@ class NitriteDocument extends LinkedHashMap implements Document public Document put(String field, Object value) { // field name cannot be empty or null if (isNullOrEmpty(field)) { - throw new InvalidOperationException("document does not support empty or null key"); + throw new InvalidOperationException("Document does not support empty or null key"); } - // _id field can not be set manually + // field name cannot be empty or null if (DOC_ID.contentEquals(field) && !validId(value)) { throw new InvalidOperationException("_id is an auto generated value and cannot be set"); } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java index 2405785c8..53e511fe5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java @@ -43,7 +43,7 @@ @EqualsAndHashCode public final class NitriteId implements Comparable, Serializable { private static final long serialVersionUID = 1477462375L; - private transient static final SnowflakeIdGenerator generator = new SnowflakeIdGenerator(); + private static final SnowflakeIdGenerator generator = new SnowflakeIdGenerator(); private String idValue; @@ -83,7 +83,7 @@ public static boolean validId(Object value) { Long.parseLong(value.toString()); return true; } catch (Exception e) { - throw new InvalidIdException("id must be a string representation of 64bit decimal number " + value); + throw new InvalidIdException("id must be a string representation of 64bit integer number " + value); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java b/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java index 9ef5c1b73..109a6292c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java @@ -128,11 +128,11 @@ public synchronized long getId() { } lastTimestamp = timestamp; long timestampLeftShift = sequenceBits + nodeIdBits; - long twepoch = 1288834974657L; - long id = ((timestamp - twepoch) << timestampLeftShift) | (nodeId << sequenceBits) | sequence; + long no2epoch = 1288834974657L; + long id = ((timestamp - no2epoch) << timestampLeftShift) | (nodeId << sequenceBits) | sequence; if (id < 0) { - log.warn("Id is smaller than 0: {}", id); + log.warn("Generated id is negative: {}", id); } return id; } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/Constants.java b/nitrite/src/main/java/org/dizitart/no2/common/Constants.java index dbae6f970..8037a687c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/Constants.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/Constants.java @@ -138,7 +138,7 @@ private Constants() {} /** * The constant TAG_COLLECTION_METADATA. */ - public static final String TAG_MAP_METADATA = "mapNames"; + public static final String TAG_MAP_METADATA = "mapNames"; /** * The constant TAG_TYPE. diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java index 2fe8de751..6a3ef8a93 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java @@ -148,7 +148,7 @@ private void writeCatalog(NitriteStore store, String name, String key) { if (StringUtils.isNullOrEmpty(key)) { storeCatalog.writeRepositoryEntry(name); } else { - storeCatalog.writeKeyedRepositoryEntries(name); + storeCatalog.writeKeyedRepositoryEntry(name); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java b/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java index 106c8c849..fc7e24e1f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java @@ -91,7 +91,7 @@ public void writeRepositoryEntry(String name) { * * @param name the name */ - public void writeKeyedRepositoryEntries(String name) { + public void writeKeyedRepositoryEntry(String name) { Document document = catalogMap.get(TAG_KEYED_REPOSITORIES); if (document == null) { document = Document.createDocument(); diff --git a/nitrite/src/test/java/org/dizitart/no2/store/StoreCatalogTest.java b/nitrite/src/test/java/org/dizitart/no2/store/StoreCatalogTest.java index ded81e974..025b41aa1 100644 --- a/nitrite/src/test/java/org/dizitart/no2/store/StoreCatalogTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/store/StoreCatalogTest.java @@ -48,7 +48,7 @@ public void testWriteRepositoryEntry() { @Test public void testWriteKeyedRepositoryEntries() { StoreCatalog storeCatalog = new StoreCatalog(new InMemoryStore()); - storeCatalog.writeKeyedRepositoryEntries("Name"); + storeCatalog.writeKeyedRepositoryEntry("Name"); assertTrue(storeCatalog.getCollectionNames().isEmpty()); } From e197dad9cb70523023e983a42a8abdbd6ba9c34f Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 23 May 2022 22:30:56 +0530 Subject: [PATCH 37/78] refactoring --- .../java/org/dizitart/no2/collection/NitriteDocument.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java index 088ce56f0..81ae192de 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java @@ -170,6 +170,8 @@ public Document clone() { public Document merge(Document document) { if (document instanceof NitriteDocument) { super.putAll((NitriteDocument) document); + } else { + throw new InvalidOperationException("Document merge only supports NitriteDocument"); } return this; } @@ -222,6 +224,11 @@ public boolean equals(Object other) { return true; } + @Override + public int hashCode() { + return super.hashCode(); + } + @Override public Iterator> iterator() { return new PairIterator(super.entrySet().iterator()); From 6c843416c0789e0db176b973f2ffad47b70102c9 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 25 May 2022 19:31:02 +0530 Subject: [PATCH 38/78] deep remove --- .../no2/collection/NitriteDocument.java | 66 ++++++++++++------- .../dizitart/no2/collection/DocumentTest.java | 40 ++++++++--- 2 files changed, 75 insertions(+), 31 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java index 81ae192de..4fef19539 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java @@ -279,7 +279,7 @@ private Object deepGet(String field) { private void deepPut(String[] splits, Object value) { if (splits.length == 0) { - throw new ValidationException("invalid key provided"); + throw new ValidationException("Invalid key provided"); } String key = splits[0]; if (splits.length == 1) { @@ -309,7 +309,7 @@ private void deepPut(String[] splits, Object value) { private void deepRemove(String[] splits) { if (splits.length == 0) { - throw new ValidationException("invalid key provided"); + throw new ValidationException("Invalid key provided"); } String key = splits[0]; if (splits.length == 1) { @@ -331,8 +331,42 @@ private void deepRemove(String[] splits) { // remove the current level document also super.remove(key); } - } else if (val == null) { - // if current level value is null, remove the key + } else if (val instanceof List && isInteger(splits[1])) { + // if the current level value is an iterable, + // remove the element at the next level + List list = (List) val; + int index = Integer.parseInt(splits[1]); + Object item = list.get(index); + if (splits.length > 2 && item instanceof NitriteDocument) { + // if there are more splits, then this is an embedded document + // so remove the element at the next level + ((NitriteDocument) item).deepRemove(Arrays.copyOfRange(splits, 2, splits.length)); + } else { + // if there are no more splits, then this is a primitive value + // so remove the element at the next level + list.remove(index); + this.put(key, list); + } + } else if (val != null && val.getClass().isArray()) { + // if the current level value is an array, + // remove the element at the next level + Object[] array = convertToObjectArray(val); + int index = Integer.parseInt(splits[1]); + Object item = array[index]; + if (splits.length > 2 && item instanceof NitriteDocument) { + // if there are more splits, then this is an embedded document + // so remove the element at the next level + ((NitriteDocument) item).deepRemove(Arrays.copyOfRange(splits, 2, splits.length)); + } else { + // if there are no more splits, then this is a primitive value + // so remove the element at the next level + List list = Arrays.asList(array); + list.remove(index); + this.put(key, list.toArray()); + } + } else { + // if current level value is not an iterable, + // remove the key super.remove(key); } } @@ -382,15 +416,9 @@ private Object recursiveGet(Object object, String[] remainingPath) { // convert the key as an integer index int index = asInteger(accessor); - // check index lower bound - if (index < 0) { - throw new ValidationException("invalid array index " + index + " to access item inside a document"); - } - - // check index upper bound - if (index >= array.length) { - throw new ValidationException("index " + index + - " is not less than the size of the array " + array.length); + // check index bound + if (index < 0 || index >= array.length) { + throw new ValidationException("Invalid index " + index + " to access item inside a document"); } // get the value at the index from the array @@ -423,15 +451,9 @@ private Object recursiveGet(Object object, String[] remainingPath) { // convert the key as an integer index int index = asInteger(accessor); - // check index lower bound - if (index < 0) { - throw new ValidationException("invalid collection index " + index + " to access item inside a document"); - } - - // check index upper bound - if (index >= collection.size()) { - throw new ValidationException("index " + accessor + - " is not less than the size of the list " + collection.size()); + // check index bound + if (index < 0 || index >= collection.size()) { + throw new ValidationException("Invalid index " + index + " to access item inside a document"); } // get the value at the index from the list diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/DocumentTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/DocumentTest.java index 772d71d73..195621c25 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/DocumentTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/DocumentTest.java @@ -18,7 +18,6 @@ package org.dizitart.no2.collection; import org.dizitart.no2.NitriteConfig; -import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.exceptions.InvalidIdException; import org.dizitart.no2.exceptions.ValidationException; import org.junit.After; @@ -26,12 +25,15 @@ import org.junit.Test; import java.io.*; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; -import static org.dizitart.no2.integration.TestUtil.parse; import static org.dizitart.no2.collection.Document.createDocument; import static org.dizitart.no2.common.Constants.DOC_ID; import static org.dizitart.no2.common.util.Iterables.listOf; +import static org.dizitart.no2.integration.TestUtil.parse; import static org.junit.Assert.*; public class DocumentTest { @@ -171,15 +173,35 @@ public void testGet() { @Test public void testRemove() { - Iterator> iterator = doc.iterator(); assertEquals(doc.size(), 4); - if (iterator.hasNext()) { - iterator.next(); - iterator.remove(); - } + doc.remove("score"); assertEquals(doc.size(), 3); } + @Test + public void testRemoveWithCustomFieldSeparator() { + NitriteConfig config = new NitriteConfig(); + config.fieldSeparator(":"); + assertEquals(((Document)doc.get("location:address")).size(), 3); + doc.remove("location:address:line1"); + assertEquals(((Document)doc.get("location:address")).size(), 2); + config.fieldSeparator("."); + } + @Test + public void testRemoveFromArray() { + NitriteConfig config = new NitriteConfig(); + config.fieldSeparator(":"); + + assertEquals(((List)doc.get("location:address:house")).size(), 3); + doc.remove("location:address:house:0"); + assertEquals(((List)doc.get("location:address:house")).size(), 2); + + assertEquals(((List) doc.get("objArray")).size(), 2); + doc.remove("objArray:0:value"); + assertEquals(((List) doc.get("objArray")).size(), 2); + assertEquals(((Document) doc.get("objArray:0")).size(), 0); + } + @Test public void getFields() { Set fields = doc.getFields(); @@ -193,7 +215,7 @@ public void getFields() { @Test @SuppressWarnings("unchecked") - public void getEmbeddedArrayFields() { + public void testGetEmbeddedArrayFields() { Document document = createDocument("first", "value") .put("seconds", new String[]{"1", "2"}) .put("third", null) From e84216ba579e85c842686a9bc9a88cebf3b626cb Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 27 May 2022 01:12:27 +0530 Subject: [PATCH 39/78] minor refactoring --- .../no2/collection/CollectionFactory.java | 10 +++---- .../java/org/dizitart/no2/common/Fields.java | 28 ++++++++++--------- .../no2/common/PersistentCollection.java | 2 +- .../no2/common/processors/Processor.java | 3 +- .../org/dizitart/no2/index/IndexOptions.java | 4 +-- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java index 1eb51af12..d493524ca 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java @@ -60,8 +60,8 @@ public CollectionFactory(LockService lockService) { * @return the collection */ public NitriteCollection getCollection(String name, NitriteConfig nitriteConfig, boolean writeCatalogue) { - notNull(nitriteConfig, "configuration is null while creating collection"); - notEmpty(name, "collection name is null or empty"); + notNull(nitriteConfig, "Configuration is null while creating collection"); + notEmpty(name, "Collection name is null or empty"); Lock lock = lockService.getWriteLock(this.getClass().getName()); try { @@ -91,14 +91,14 @@ private NitriteCollection createCollection(String name, NitriteConfig nitriteCon if (store.getRepositoryRegistry().contains(name)) { nitriteMap.close(); collection.close(); - throw new ValidationException("a repository with same name already exists"); + throw new ValidationException("A repository with same name already exists"); } for (Set set : store.getKeyedRepositoryRegistry().values()) { if (set.contains(name)) { nitriteMap.close(); collection.close(); - throw new ValidationException("a keyed repository with same name already exists"); + throw new ValidationException("A keyed repository with same name already exists"); } } @@ -122,7 +122,7 @@ public void clear() { } collectionMap.clear(); } catch (Exception e) { - throw new NitriteIOException("failed to close a collection", e); + throw new NitriteIOException("Failed to close a collection", e); } finally { lock.unlock(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/Fields.java b/nitrite/src/main/java/org/dizitart/no2/common/Fields.java index e5f380868..66ba15317 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/Fields.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/Fields.java @@ -64,6 +64,9 @@ public static Fields withNames(String... fields) { * @return the fields */ public Fields addField(String field) { + notNull(field, "field cannot be null"); + notEmpty(field, "field cannot be empty"); + fieldNames.add(field); return this; } @@ -78,28 +81,27 @@ public List getFieldNames() { } /** - * Starts with boolean. + * Check if a {@link Fields} is a subset of the current {@link Fields}. * * @param other the other * @return the boolean */ public boolean startsWith(Fields other) { - if (other != null) { - int length = Math.min(fieldNames.size(), other.fieldNames.size()); + notNull(other, "other cannot be null"); - // if other is greater then it is not a prefix of this field - if (other.fieldNames.size() > length) return false; + int length = Math.min(fieldNames.size(), other.fieldNames.size()); - for (int i = 0; i < length; i++) { - String thisField = fieldNames.get(i); - String otherField = other.fieldNames.get(i); - if (!thisField.equals(otherField)) { - return false; - } + // if other is greater then it is not a prefix of this field + if (other.fieldNames.size() > length) return false; + + for (int i = 0; i < length; i++) { + String thisField = fieldNames.get(i); + String otherField = other.fieldNames.get(i); + if (!thisField.equals(otherField)) { + return false; } - return true; } - return false; + return true; } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java index 564b259f4..649610a22 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java @@ -91,7 +91,7 @@ default void createIndex(String... fields) { void createIndex(IndexOptions indexOptions, String... fields); /** - * Rebuilds index on the {@code field} if it exists. + * Rebuilds index on the {@code fields} if it exists. * * @param fields the fields to be indexed. * @throws org.dizitart.no2.exceptions.IndexingException if the {@code field} is not indexed. diff --git a/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java b/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java index 829de5888..9474cf7ad 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java @@ -20,7 +20,6 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.common.PersistentCollection; -import org.dizitart.no2.filters.Filter; import org.dizitart.no2.repository.ObjectRepository; import static org.dizitart.no2.collection.UpdateOptions.updateOptions; @@ -65,7 +64,7 @@ default void process(PersistentCollection collection) { } if (nitriteCollection != null) { - for (Document document : nitriteCollection.find(Filter.ALL, null)) { + for (Document document : nitriteCollection.find()) { Document processed = processBeforeWrite(document); nitriteCollection.update(createUniqueFilter(document), processed, updateOptions(false)); } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexOptions.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexOptions.java index 75bf1d6b6..64b0c4424 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexOptions.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexOptions.java @@ -40,8 +40,8 @@ public class IndexOptions { private String indexType; /** - * Creates an {@link IndexOptions} with the specified `indexType`. Index creation - * will be synchronous with this option. + * Creates an {@link IndexOptions} with the specified indexType. + * Index creation will be synchronous with this option. * * @param indexType the type of index to be created. * @return a new synchronous index creation option. From 16eb6c54fc971025deeee05bf3094888e5aa534d Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 30 May 2022 23:37:37 +0530 Subject: [PATCH 40/78] few refactoring --- .../no2/common/util/ValidationUtils.java | 4 ++-- .../java/org/dizitart/no2/filters/AndFilter.java | 16 ++++++++-------- .../org/dizitart/no2/filters/BetweenFilter.java | 6 +++--- .../dizitart/no2/filters/ComparableFilter.java | 2 +- .../dizitart/no2/filters/ElementMatchFilter.java | 4 ++-- .../dizitart/no2/filters/FieldBasedFilter.java | 4 ++-- .../java/org/dizitart/no2/filters/Filter.java | 2 +- .../dizitart/no2/filters/GreaterEqualFilter.java | 8 ++++---- .../dizitart/no2/filters/GreaterThanFilter.java | 8 ++++---- .../dizitart/no2/filters/LesserEqualFilter.java | 8 ++++---- .../dizitart/no2/filters/LesserThanFilter.java | 8 ++++---- .../java/org/dizitart/no2/filters/OrFilter.java | 14 +++++++------- .../org/dizitart/no2/filters/TextFilter.java | 15 ++++++++------- .../java/org/dizitart/no2/index/TextIndex.java | 2 +- .../no2/index/fulltext/TextTokenizer.java | 2 +- .../index/fulltext/UniversalTextTokenizer.java | 2 ++ .../org/dizitart/no2/filters/TextFilterTest.java | 4 ++-- 17 files changed, 56 insertions(+), 53 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java index 60afea147..df3cc8546 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java @@ -163,11 +163,11 @@ public static void validateFilterIterableField(Iterable fieldValue, String fi private static void validateArrayIndexItem(Object value, String field) { if (value instanceof Iterable || value.getClass().isArray()) { - throw new InvalidOperationException("nested array index on iterable field " + field + " is not supported"); + throw new InvalidOperationException("Nested iterables are not supported"); } if (!(value instanceof Comparable)) { - throw new IndexingException("cannot index on an array field containing non comparable values " + field); + throw new IndexingException("Each value for the iterable field " + field + " must implement Comparable"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/AndFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/AndFilter.java index 9e646111b..4a345de20 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/AndFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/AndFilter.java @@ -41,18 +41,19 @@ public class AndFilter extends LogicalFilter { for (int i = 1; i < filters.length; i++) { if (filters[i] instanceof TextFilter) { - throw new FilterException("text filter must be the first filter in AND operation"); + throw new FilterException("Text filter must be the first filter in AND operation"); } } } @Override public boolean apply(Pair element) { - boolean result = true; for (Filter filter : getFilters()) { - result = result && filter.apply(element); + if (!filter.apply(element)) { + return false; + } } - return result; + return true; } @Override @@ -61,11 +62,10 @@ public String toString() { stringBuilder.append("("); for (int i = 0; i < getFilters().size(); i++) { Filter filter = getFilters().get(i); - if (i == 0) { - stringBuilder.append(filter.toString()); - } else { - stringBuilder.append(" && ").append(filter.toString()); + if (i > 0) { + stringBuilder.append(" && "); } + stringBuilder.append(filter.toString()); } stringBuilder.append(")"); return stringBuilder.toString(); diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/BetweenFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/BetweenFilter.java index a8693caf6..903b32121 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/BetweenFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/BetweenFilter.java @@ -17,7 +17,7 @@ package org.dizitart.no2.filters; import lombok.Data; -import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.exceptions.FilterException; /** * @author Anindya Chatterjee @@ -50,11 +50,11 @@ private static Filter getLhs(String field, Bound bound) { private static void validateBound(Bound bound) { if (bound == null) { - throw new ValidationException("bound cannot be null"); + throw new FilterException("Bound cannot be null"); } if (!(bound.upperBound instanceof Comparable) || !(bound.lowerBound instanceof Comparable)) { - throw new ValidationException("upper bound or lower bound value must be comparable"); + throw new FilterException("Upper bound or lower bound value must be comparable"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/ComparableFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/ComparableFilter.java index e2d7c64fc..c9bf74e54 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/ComparableFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/ComparableFilter.java @@ -73,7 +73,7 @@ protected void processIndexValue(Object value, List, Object>> subMap, List nitriteIds) { if (value instanceof List) { - // if its is list then add it directly to nitrite ids + // if it is list then add it directly to nitrite ids List result = (List) value; nitriteIds.addAll(result); } diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/ElementMatchFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/ElementMatchFilter.java index 842d303fd..2a80e1526 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/ElementMatchFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/ElementMatchFilter.java @@ -47,11 +47,11 @@ class ElementMatchFilter extends NitriteFilter { @SuppressWarnings({"rawtypes", "unchecked"}) public boolean apply(Pair element) { if (elementFilter instanceof ElementMatchFilter) { - throw new FilterException("nested elemMatch filter is not supported"); + throw new FilterException("Nested elemMatch filter is not supported"); } if (elementFilter instanceof TextFilter) { - throw new FilterException("text filter is not supported in elemMatch filter"); + throw new FilterException("Text filter is not supported in elemMatch filter"); } Document document = element.getSecond(); diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java index 9112d515e..0e4f99194 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java @@ -20,8 +20,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; -import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.exceptions.FilterException; import static org.dizitart.no2.common.util.ValidationUtils.notEmpty; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -82,7 +82,7 @@ protected void validateSearchTerm(NitriteMapper nitriteMapper, String field, Obj if (value != null) { if (!nitriteMapper.isValue(value) && !(value instanceof Comparable)) { - throw new ValidationException("search term is not comparable " + value); + throw new FilterException("The value for field '" + field + "' is not a valid search term"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java b/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java index bb884ff63..57c877ce3 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java @@ -64,7 +64,7 @@ static Filter byId(NitriteId nitriteId) { * @return the filter */ static Filter and(Filter... filters) { - notEmpty(filters, "at least two filters must be specified"); + notEmpty(filters, "At least two filters must be specified"); if (filters.length < 2) { throw new FilterException("at least two filters must be specified"); } diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/GreaterEqualFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/GreaterEqualFilter.java index f3c8374bb..93b5c4e2b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/GreaterEqualFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/GreaterEqualFilter.java @@ -60,7 +60,7 @@ public boolean apply(Pair element) { @SuppressWarnings({"unchecked", "rawtypes"}) public List applyOnIndex(IndexMap indexMap) { Comparable comparable = getComparable(); - List, Object>> subMap = new ArrayList<>(); + List, Object>> subMaps = new ArrayList<>(); // maintain the find sorting order List nitriteIds = new ArrayList<>(); @@ -70,14 +70,14 @@ public List applyOnIndex(IndexMap indexMap) { // get the starting value, it can be a navigable-map (compound index) // or list (single field index) Object value = indexMap.get(ceilingKey); - processIndexValue(value, subMap, nitriteIds); + processIndexValue(value, subMaps, nitriteIds); ceilingKey = indexMap.higherKey(ceilingKey); } - if (!subMap.isEmpty()) { + if (!subMaps.isEmpty()) { // if sub-map is populated then filtering on compound index, return sub-map - return subMap; + return subMaps; } else { // else it is filtering on either single field index, // or it is a terminal filter on compound index, return only nitrite-ids diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/GreaterThanFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/GreaterThanFilter.java index a41b71a92..089b34133 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/GreaterThanFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/GreaterThanFilter.java @@ -61,7 +61,7 @@ public boolean apply(Pair element) { @SuppressWarnings({"unchecked", "rawtypes"}) public List applyOnIndex(IndexMap indexMap) { Comparable comparable = getComparable(); - List, Object>> subMap = new ArrayList<>(); + List, Object>> subMaps = new ArrayList<>(); List nitriteIds = new ArrayList<>(); Comparable ceilingKey = indexMap.higherKey(comparable); @@ -69,14 +69,14 @@ public List applyOnIndex(IndexMap indexMap) { // get the starting value, it can be a navigable-map (compound index) // or list (single field index) Object value = indexMap.get(ceilingKey); - processIndexValue(value, subMap, nitriteIds); + processIndexValue(value, subMaps, nitriteIds); ceilingKey = indexMap.higherKey(ceilingKey); } - if (!subMap.isEmpty()) { + if (!subMaps.isEmpty()) { // if sub-map is populated then filtering on compound index, return sub-map - return subMap; + return subMaps; } else { // else it is filtering on either single field index, // or it is a terminal filter on compound index, return only nitrite-ids diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/LesserEqualFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/LesserEqualFilter.java index 0a87b0dbc..c248d6294 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/LesserEqualFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/LesserEqualFilter.java @@ -63,14 +63,14 @@ public List applyOnIndex(IndexMap indexMap) { List, Object>> subMap = new ArrayList<>(); List nitriteIds = new ArrayList<>(); - Comparable ceilingKey = indexMap.floorKey(comparable); - while (ceilingKey != null) { + Comparable floorKey = indexMap.floorKey(comparable); + while (floorKey != null) { // get the starting value, it can be a navigable-map (compound index) // or list (single field index) - Object value = indexMap.get(ceilingKey); + Object value = indexMap.get(floorKey); processIndexValue(value, subMap, nitriteIds); - ceilingKey = indexMap.lowerKey(ceilingKey); + floorKey = indexMap.lowerKey(floorKey); } if (!subMap.isEmpty()) { diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/LesserThanFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/LesserThanFilter.java index e522dbca8..93e35a839 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/LesserThanFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/LesserThanFilter.java @@ -63,14 +63,14 @@ public List applyOnIndex(IndexMap indexMap) { List, Object>> subMap = new ArrayList<>(); List nitriteIds = new ArrayList<>(); - Comparable ceilingKey = indexMap.lowerKey(comparable); - while (ceilingKey != null) { + Comparable floorKey = indexMap.lowerKey(comparable); + while (floorKey != null) { // get the starting value, it can be a navigable-map (compound index) // or list (single field index) - Object value = indexMap.get(ceilingKey); + Object value = indexMap.get(floorKey); processIndexValue(value, subMap, nitriteIds); - ceilingKey = indexMap.lowerKey(ceilingKey); + floorKey = indexMap.lowerKey(floorKey); } if (!subMap.isEmpty()) { diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/OrFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/OrFilter.java index 7ad22a6e1..96ecfbb89 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/OrFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/OrFilter.java @@ -40,11 +40,12 @@ public class OrFilter extends LogicalFilter { @Override public boolean apply(Pair element) { - boolean result = false; for (Filter filter : getFilters()) { - result = result || filter.apply(element); + if (filter.apply(element)) { + return true; + } } - return result; + return false; } @Override @@ -53,11 +54,10 @@ public String toString() { stringBuilder.append("("); for (int i = 0; i < getFilters().size(); i++) { Filter filter = getFilters().get(i); - if (i == 0) { - stringBuilder.append(filter.toString()); - } else { - stringBuilder.append(" || ").append(filter.toString()); + if (i > 0) { + stringBuilder.append(" || "); } + stringBuilder.append(filter.toString()); } stringBuilder.append(")"); return stringBuilder.toString(); diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/TextFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/TextFilter.java index 154e797f7..b1d02f4de 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/TextFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/TextFilter.java @@ -58,7 +58,7 @@ public boolean apply(Pair element) { Object docValue = element.getSecond().get(getField()); if (!(docValue instanceof String)) { - throw new FilterException("text filter can not be applied on non string field " + getField()); + throw new FilterException("Text filter can not be applied on non string field " + getField()); } String docString = (String) docValue; @@ -76,12 +76,12 @@ public String toString() { } /** - * Apply on index linked hash set. + * Apply this filter on text index. * * @param indexMap the index map * @return the linked hash set */ - public LinkedHashSet applyOnIndex(NitriteMap> indexMap) { + public LinkedHashSet applyOnTextIndex(NitriteMap> indexMap) { notNull(getField(), "field cannot be null"); notNull(getStringValue(), "search term cannot be null"); String searchString = getStringValue(); @@ -117,12 +117,13 @@ private LinkedHashSet searchExactByIndex(NitriteMap> private LinkedHashSet searchByWildCard(NitriteMap> indexMap, String searchString) { if (searchString.contentEquals("*")) { - throw new FilterException("* is not a valid search string"); + throw new FilterException("* is not a valid search term"); } StringTokenizer stringTokenizer = stringTokenizer(searchString); if (stringTokenizer.countTokens() > 1) { - throw new FilterException("multiple words with wildcard is not supported"); + throw new FilterException("Wild card search can not be applied on " + + "multiple words"); } if (searchString.startsWith("*") && !searchString.endsWith("*")) { @@ -138,7 +139,7 @@ private LinkedHashSet searchByWildCard(NitriteMap> in @SuppressWarnings("unchecked") private LinkedHashSet searchByLeadingWildCard(NitriteMap> indexMap, String searchString) { if (searchString.equalsIgnoreCase("*")) { - throw new FilterException("invalid search term '*'"); + throw new FilterException("* is not a valid search term"); } LinkedHashSet idSet = new LinkedHashSet<>(); @@ -156,7 +157,7 @@ private LinkedHashSet searchByLeadingWildCard(NitriteMap searchByTrailingWildCard(NitriteMap> indexMap, String searchString) { if (searchString.equalsIgnoreCase("*")) { - throw new FilterException("invalid search term '*'"); + throw new FilterException("* is not a valid search term"); } LinkedHashSet idSet = new LinkedHashSet<>(); diff --git a/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java b/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java index 3c6a4aba4..7a35435bf 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java @@ -150,7 +150,7 @@ public LinkedHashSet findNitriteIds(FindPlan findPlan) { if (filters.size() == 1 && filters.get(0) instanceof TextFilter) { TextFilter textFilter = (TextFilter) filters.get(0); textFilter.setTextTokenizer(textTokenizer); - return textFilter.applyOnIndex(indexMap); + return textFilter.applyOnTextIndex(indexMap); } throw new FilterException("invalid filter found for full-text index"); } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/fulltext/TextTokenizer.java b/nitrite/src/main/java/org/dizitart/no2/index/fulltext/TextTokenizer.java index 5b7876d5f..9c9b1e07e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/fulltext/TextTokenizer.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/fulltext/TextTokenizer.java @@ -35,7 +35,7 @@ public interface TextTokenizer { Languages getLanguage(); /** - * Tokenize a `text` and discards all stop-words from it. + * Tokenize a text and discards all stop-words from it. * * @param text the text to tokenize * @return the set of tokens. diff --git a/nitrite/src/main/java/org/dizitart/no2/index/fulltext/UniversalTextTokenizer.java b/nitrite/src/main/java/org/dizitart/no2/index/fulltext/UniversalTextTokenizer.java index 975d03288..c9e302cd4 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/fulltext/UniversalTextTokenizer.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/fulltext/UniversalTextTokenizer.java @@ -236,6 +236,8 @@ private void loadLanguage(Languages... languages) { case Zulu: registerLanguage(new Zulu()); break; + default: + break; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/filters/TextFilterTest.java b/nitrite/src/test/java/org/dizitart/no2/filters/TextFilterTest.java index c942d0e33..a9066378f 100644 --- a/nitrite/src/test/java/org/dizitart/no2/filters/TextFilterTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/filters/TextFilterTest.java @@ -52,7 +52,7 @@ public void testToString2() { public void testApplyOnIndex() { TextFilter textFilter = new TextFilter("Field", "42"); textFilter.setTextTokenizer(new EnglishTextTokenizer()); - assertTrue(textFilter.applyOnIndex(new InMemoryMap<>("Map Name", null)).isEmpty()); + assertTrue(textFilter.applyOnTextIndex(new InMemoryMap<>("Map Name", null)).isEmpty()); assertEquals("42", textFilter.getStringValue()); } @@ -61,7 +61,7 @@ public void testApplyOnIndex3() { TextFilter textFilter = new TextFilter("Field", "*"); textFilter.setTextTokenizer(new EnglishTextTokenizer()); assertThrows(FilterException.class, - () -> textFilter.applyOnIndex(new InMemoryMap<>("Map Name", null))); + () -> textFilter.applyOnTextIndex(new InMemoryMap<>("Map Name", null))); } } From 27c2c5100d5505791c0f257b6be4bbb683344088 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 2 Jun 2022 11:04:33 +0530 Subject: [PATCH 41/78] doc correction --- .../java/org/dizitart/no2/collection/NitriteCollection.java | 1 - .../java/org/dizitart/no2/common/PersistentCollection.java | 2 +- .../main/java/org/dizitart/no2/filters/ComparableFilter.java | 2 +- .../main/java/org/dizitart/no2/filters/FieldBasedFilter.java | 2 +- nitrite/src/main/java/org/dizitart/no2/filters/Filter.java | 2 +- .../src/main/java/org/dizitart/no2/filters/FluentFilter.java | 4 ++-- .../main/java/org/dizitart/no2/filters/IndexOnlyFilter.java | 5 +++++ .../main/java/org/dizitart/no2/index/IndexDescriptor.java | 2 +- 8 files changed, 12 insertions(+), 8 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteCollection.java index 92bd6dfb8..01a721198 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteCollection.java @@ -46,7 +46,6 @@ *

  * {@code
  * Nitrite db = Nitrite.builder()
- *    .loadModule(MVStoreModule("/tmp/tmp.db"))
  *    .openOrCreate("user", "password");
  *    
  * NitriteCollection collection = db.getCollection("products");
diff --git a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java
index 649610a22..76eb362e3 100644
--- a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java
+++ b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java
@@ -251,7 +251,7 @@ default WriteResult update(T[] elements, boolean insertIfAbsent) {
      * Drops the collection and all of its indices.
      * 

* Any further access to a dropped collection would result into - * a {@link IllegalStateException}. + * an exception. *

*/ void drop(); diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/ComparableFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/ComparableFilter.java index c9bf74e54..4946f95d5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/ComparableFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/ComparableFilter.java @@ -54,7 +54,7 @@ public Comparable getComparable() { } /** - * Apply this filter on an nitrite index. + * Apply this filter on a nitrite index. * * @param indexMap the index scanner * @return the object diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java index 0e4f99194..43b656da4 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java @@ -55,7 +55,7 @@ protected FieldBasedFilter(String field, Object value) { } /** - * Gets the value fo the filter. + * Gets the value of the filter. * * @return the value */ diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java b/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java index 57c877ce3..6f08f9045 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java @@ -97,7 +97,7 @@ static Filter or(Filter... filters) { /** * Creates a not filter which performs a logical NOT operation on a filter and selects - * the documents that *_do not_* satisfy the criteria. This also includes documents + * the documents that do not satisfy the criteria. This also includes documents * that do not contain the value. *

* diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/FluentFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/FluentFilter.java index 98d7d00bb..f222d7e05 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/FluentFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/FluentFilter.java @@ -17,7 +17,7 @@ package org.dizitart.no2.filters; /** - * A fluent api for the {@link Filter}. + * A fluent api for the {@link NitriteFilter}. * * @author Anindya Chatterjee. * @since 4.0 @@ -34,7 +34,7 @@ private FluentFilter() { } /** - * Where clause for fluent filter. + * Where clause for fluent filter api. * * @param field the field * @return the fluent filter diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/IndexOnlyFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/IndexOnlyFilter.java index 5eac4a099..9e5bdc2e8 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/IndexOnlyFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/IndexOnlyFilter.java @@ -35,6 +35,11 @@ public IndexOnlyFilter(String field, Object value) { super(field, value); } + /** + * Gets the supported index type for this filter. + * + * @return the supported index type + */ public abstract String supportedIndexType(); /** diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java index aad8506ee..d49e815ad 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java @@ -113,7 +113,7 @@ public int compareTo(IndexDescriptor other) { } /** - * Is compound index boolean. + * Indicates if this descriptor is for a compound index. * * @return the boolean */ From d9a7c2cd7ef0f5bc25dd1f76c148818639e7285f Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 3 Jun 2022 23:01:32 +0530 Subject: [PATCH 42/78] test fix --- .../java/org/dizitart/no2/collection/DocumentCursor.java | 7 ++----- .../main/java/org/dizitart/no2/collection/FindPlan.java | 4 ++-- .../dizitart/no2/collection/operation/ReadOperations.java | 7 ++----- .../java/org/dizitart/no2/filters/IndexScanFilter.java | 2 +- .../src/test/java/org/dizitart/no2/common/FieldsTest.java | 3 ++- .../java/org/dizitart/no2/filters/BetweenFilterTest.java | 8 ++++---- 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DocumentCursor.java b/nitrite/src/main/java/org/dizitart/no2/collection/DocumentCursor.java index ea0dacca3..919b7ae9f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DocumentCursor.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DocumentCursor.java @@ -20,13 +20,10 @@ import org.dizitart.no2.common.RecordStream; /** - * An interface to iterate over database {@code find()} results. It provides a + * An interface to iterate over {@link NitriteCollection#find()} results. It provides a * mechanism to iterate over all {@link NitriteId}s of the result. *

  * {@code
- * // create/open a database
- * Nitrite db = Nitrite.builder()
- *      .openOrCreate("user", "password");
  *
  * // create/open a database
  * Nitrite db = Nitrite.builder()
@@ -68,7 +65,7 @@ public interface DocumentCursor extends RecordStream {
      * Performs a left outer join with a foreign cursor with the specified lookup parameters.
      * 

* It performs an equality match on the localString to the foreignString from the documents of the foreign cursor. - * If an input document does not contain the localString, the join treats the field as having a value of `null` + * If an input document does not contain the localString, the join treats the field as having a value of null * for matching purposes. * * @param foreignCursor the foreign cursor for the join. diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/FindPlan.java b/nitrite/src/main/java/org/dizitart/no2/collection/FindPlan.java index d2daeec01..aea4fe123 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/FindPlan.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/FindPlan.java @@ -20,7 +20,7 @@ import lombok.Data; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.common.tuples.Pair; -import org.dizitart.no2.filters.EqualsFilter; +import org.dizitart.no2.filters.FieldBasedFilter; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.filters.IndexScanFilter; import org.dizitart.no2.index.IndexDescriptor; @@ -38,7 +38,7 @@ */ @Data public class FindPlan { - private EqualsFilter byIdFilter; + private FieldBasedFilter byIdFilter; private IndexScanFilter indexScanFilter; private Filter collectionScanFilter; diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java index c77e6af3f..52f1d0163 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java @@ -21,10 +21,7 @@ import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.common.streams.*; import org.dizitart.no2.common.tuples.Pair; -import org.dizitart.no2.filters.EqualsFilter; -import org.dizitart.no2.filters.Filter; -import org.dizitart.no2.filters.LogicalFilter; -import org.dizitart.no2.filters.NitriteFilter; +import org.dizitart.no2.filters.*; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.NitriteIndexer; import org.dizitart.no2.common.processors.ProcessorChain; @@ -122,7 +119,7 @@ private RecordStream> findSuitableStream(FindPlan find } else { // and or single filter if (findPlan.getByIdFilter() != null) { - EqualsFilter byIdFilter = findPlan.getByIdFilter(); + FieldBasedFilter byIdFilter = findPlan.getByIdFilter(); NitriteId nitriteId = NitriteId.createId((String) byIdFilter.getValue()); rawStream = RecordStream.single(pair(nitriteId, nitriteMap.get(nitriteId))); } else { diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/IndexScanFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/IndexScanFilter.java index 03282e782..a7c167673 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/IndexScanFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/IndexScanFilter.java @@ -50,6 +50,6 @@ public IndexScanFilter(Collection filters) { @Override public boolean apply(Pair element) { - throw new InvalidOperationException("index scan filter cannot be applied on collection"); + throw new InvalidOperationException("Index scan filter cannot be applied on collection"); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/FieldsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/FieldsTest.java index 34887c467..6bb9b9cc3 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/FieldsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/FieldsTest.java @@ -17,6 +17,7 @@ package org.dizitart.no2.common; +import org.dizitart.no2.exceptions.ValidationException; import org.junit.Test; import java.util.List; @@ -55,7 +56,7 @@ public void testStartsWith() { assertTrue(fields.startsWith(new Fields())); } - @Test + @Test(expected = ValidationException.class) public void testStartsWith2() { assertFalse((new Fields()).startsWith(null)); } diff --git a/nitrite/src/test/java/org/dizitart/no2/filters/BetweenFilterTest.java b/nitrite/src/test/java/org/dizitart/no2/filters/BetweenFilterTest.java index cc1aecc29..2f43255e0 100644 --- a/nitrite/src/test/java/org/dizitart/no2/filters/BetweenFilterTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/filters/BetweenFilterTest.java @@ -17,7 +17,7 @@ package org.dizitart.no2.filters; -import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.exceptions.FilterException; import org.junit.Test; import static org.junit.Assert.*; @@ -84,19 +84,19 @@ public void testConstructor() { @Test public void testConstructor2() { - assertThrows(ValidationException.class, + assertThrows(FilterException.class, () -> new BetweenFilter<>("Field", new BetweenFilter.Bound<>(null, "Upper Bound"))); } @Test public void testConstructor3() { - assertThrows(ValidationException.class, + assertThrows(FilterException.class, () -> new BetweenFilter<>("Field", new BetweenFilter.Bound<>("Lower Bound", null))); } @Test public void testConstructor4() { - assertThrows(ValidationException.class, () -> new BetweenFilter<>("Field", null)); + assertThrows(FilterException.class, () -> new BetweenFilter<>("Field", null)); } } From a880245d2e4a181f1d3be1ea72d968119920e939 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sun, 5 Jun 2022 10:40:00 +0530 Subject: [PATCH 43/78] refactoring --- .../collection/operation/ReadOperations.java | 6 ++- .../no2/common/processors/Processor.java | 4 +- .../streams/ProjectedDocumentStream.java | 13 ++++--- .../streams/ProjectedDocumentStreamTest.java | 23 +++++++++++- .../collection/CollectionFindTest.java | 37 +++++++++++++++++++ 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java index 52f1d0163..58c0e13fe 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java @@ -70,7 +70,11 @@ public DocumentCursor find(Filter filter, FindOptions findOptions) { } Document getById(NitriteId nitriteId) { - return nitriteMap.get(nitriteId); + Document document = nitriteMap.get(nitriteId); + if (processorChain != null) { + document = processorChain.processAfterRead(document); + } + return document; } private void prepareFilter(Filter filter) { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java b/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java index 9474cf7ad..7c266efe8 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/processors/Processor.java @@ -39,7 +39,7 @@ public interface Processor { * @param document the document * @return the document */ - Document processBeforeWrite(Document document); + default Document processBeforeWrite(Document document) { return document; } /** * Processes a document after reading from the database. @@ -47,7 +47,7 @@ public interface Processor { * @param document the document * @return the document */ - Document processAfterRead(Document document); + default Document processAfterRead(Document document) { return document; } /** * Processes all documents of a {@link PersistentCollection}. diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/ProjectedDocumentStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/ProjectedDocumentStream.java index 1905e35d4..adb5203de 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/ProjectedDocumentStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/ProjectedDocumentStream.java @@ -120,17 +120,18 @@ public void remove() { private Document project(Document original) { if (projection == null) return original; - Document result = original.clone(); + Document newDoc = Document.createDocument(); - for (Pair pair : original) { - if (!projection.containsKey(pair.getFirst())) { - result.remove(pair.getFirst()); + for (String field : projection.getFields()) { + if (original.containsField(field)) { + Object value = original.get(field); + newDoc.put(field, value); } } // process the result - result = processorChain.processAfterRead(result); - return result; + newDoc = processorChain.processAfterRead(newDoc); + return newDoc; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/streams/ProjectedDocumentStreamTest.java b/nitrite/src/test/java/org/dizitart/no2/common/streams/ProjectedDocumentStreamTest.java index b07e1c288..1e76c1c74 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/streams/ProjectedDocumentStreamTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/streams/ProjectedDocumentStreamTest.java @@ -18,11 +18,12 @@ package org.dizitart.no2.common.streams; import com.fasterxml.jackson.databind.util.ArrayIterator; +import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.RecordStream; -import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.common.processors.ProcessorChain; +import org.dizitart.no2.common.tuples.Pair; import org.junit.Test; import static org.junit.Assert.assertNull; @@ -65,5 +66,25 @@ public void testIterator2() { verify(recordStream).iterator(); assertTrue(projectedDocumentStream.toList().isEmpty()); } + @Test + public void test() { + try(Nitrite db = Nitrite.builder().openOrCreate()) { + Document document = Document.createDocument("name", "John") + .put("address", Document.createDocument("street", "Main Street") + .put("city", "New York") + .put("state", "NY") + .put("zip", "10001")); + db.getCollection("users").insert(document); + + Document projection = Document.createDocument("name", null) + .put("address.city", null) + .put("address.state", null); + + db.getCollection("users") + .find() + .project(projection) + .forEach(System.out::println); + } + } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java index 25061010c..885a05f2c 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java @@ -21,7 +21,9 @@ import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.common.SortOrder; +import org.dizitart.no2.common.processors.StringFieldEncryptionProcessor; import org.dizitart.no2.exceptions.IndexingException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexOptions; @@ -357,6 +359,41 @@ public void testProject() { } assertEquals(iteration, 3); } + @Test + public void testProjection() { + Document doc1 = Document.createDocument("name", "John") + .put("address", Document.createDocument("street", "Main Street") + .put("city", "New York") + .put("state", "NY") + .put("zip", "10001")); + + Document doc2 = Document.createDocument("name", "Jane") + .put("address", Document.createDocument("street", "Other Street") + .put("city", "New Jersey") + .put("state", "NJ") + .put("zip", "70001")); + + NitriteCollection collection = db.getCollection("person"); + collection.insert(doc1, doc2); + + StringFieldEncryptionProcessor processor = new StringFieldEncryptionProcessor("pass"); + processor.addFields("name"); + processor.process(collection); + collection.addProcessor(processor); + + Document projection = Document.createDocument("name", null) + .put("address.city", null) + .put("address.state", null); + + RecordStream recordStream = collection.find().project(projection); + assertEquals(recordStream.size(), 2); + assertEquals(recordStream.firstOrNull(), Document.createDocument("name", "John") + .put("address", Document.createDocument("city", "New York").put("state", "NY"))); + assertEquals(recordStream.toList().stream().skip(1).findFirst().orElse(null), + Document.createDocument("name", "Jane").put("address", Document.createDocument("city", "New Jersey") + .put("state", "NJ"))); + System.out.println(recordStream.firstOrNull()); + } @Test public void testProjectWithCustomDocument() { From 9c0b65e5b2170d3885e6ece9aa4644925944c20c Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 8 Jun 2022 23:06:44 +0530 Subject: [PATCH 44/78] snowflake id generator logic change --- .../collection/DefaultNitriteCollection.java | 6 +-- .../no2/collection/SnowflakeIdGenerator.java | 50 +++++++------------ .../collection/SnowflakeIdGeneratorTest.java | 15 ++++++ 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index f87679616..17a7e7436 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -142,13 +142,13 @@ public WriteResult remove(Document document) { writeLock.unlock(); } } else { - throw new NotIdentifiableException("remove operation failed as no id value found for the document"); + throw new NotIdentifiableException("Document has no id, cannot remove by document"); } } public WriteResult remove(Filter filter, boolean justOne) { if ((filter == null || filter == Filter.ALL) && justOne) { - throw new InvalidOperationException("remove all cannot be combined with just once"); + throw new InvalidOperationException("Cannot remove all documents with justOne set to true"); } try { @@ -450,7 +450,7 @@ private void validateRebuildIndex(IndexDescriptor indexDescriptor) { String[] indexFields = indexDescriptor.getIndexFields().getFieldNames().toArray(new String[0]); if (isIndexing(indexFields)) { - throw new IndexingException("indexing on value " + indexDescriptor.getIndexFields() + " is currently running"); + throw new IndexingException("Cannot rebuild index, index is currently being built"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java b/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java index 109a6292c..a7b56663c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/SnowflakeIdGenerator.java @@ -19,11 +19,9 @@ import lombok.extern.slf4j.Slf4j; -import java.net.NetworkInterface; -import java.net.SocketException; +import java.nio.ByteBuffer; import java.security.SecureRandom; -import java.util.Enumeration; -import java.util.NoSuchElementException; +import java.util.UUID; /** * Generate unique IDs using the Twitter Snowflake algorithm (see https://github.com/twitter/snowflake). Snowflake IDs @@ -51,23 +49,19 @@ public class SnowflakeIdGenerator { private volatile long lastTimestamp = -1L; private volatile long sequence = 0L; + private static final long no2epoch = 1288834974657L; public SnowflakeIdGenerator() { random = new SecureRandom(); long maxNodeId = ~(-1L << nodeIdBits); - try { - this.nodeId = getNodeId(); - } catch (SocketException | NoSuchElementException | NullPointerException e) { - log.warn("SNOWFLAKE: could not determine machine address; using random node id"); - this.nodeId = random.nextInt((int) maxNodeId) + 1; - } + this.nodeId = getNodeId(); if (this.nodeId > maxNodeId) { - log.warn("SNOWFLAKE: nodeId > maxNodeId; using random node id"); + log.warn("nodeId > maxNodeId; using random node id"); this.nodeId = random.nextInt((int) maxNodeId) + 1; } - log.debug("SNOWFLAKE: initialised with node id {}", this.nodeId); + log.debug("initialised with node id {}", this.nodeId); } protected long tillNextMillis(long lastTimestamp) { @@ -78,27 +72,11 @@ protected long tillNextMillis(long lastTimestamp) { return timestamp; } - protected long getNodeId() throws SocketException { - NetworkInterface network = null; - - Enumeration en = NetworkInterface.getNetworkInterfaces(); - while (en.hasMoreElements()) { - NetworkInterface nint = en.nextElement(); - if (!nint.isLoopback() && nint.getHardwareAddress() != null) { - network = nint; - break; - } - } - - if (network != null) { - byte[] mac = network.getHardwareAddress(); - byte rndByte = (byte) (random.nextInt() & 0x000000FF); + protected long getNodeId() { + byte[] uuid = asBytes(UUID.randomUUID()); + byte rndByte = (byte) (random.nextInt() & 0x000000FF); - // take the last byte of the MAC address and a random byte as node id - return ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) rndByte) << 8))) >> 6; - } else { - throw new NoSuchElementException("no network interface found"); - } + return ((0x000000FF & (long) uuid[uuid.length - 1]) | (0x0000FF00 & (((long) rndByte) << 8))) >> 6; } @@ -128,7 +106,6 @@ public synchronized long getId() { } lastTimestamp = timestamp; long timestampLeftShift = sequenceBits + nodeIdBits; - long no2epoch = 1288834974657L; long id = ((timestamp - no2epoch) << timestampLeftShift) | (nodeId << sequenceBits) | sequence; if (id < 0) { @@ -136,4 +113,11 @@ public synchronized long getId() { } return id; } + + private byte[] asBytes(UUID uuid) { + ByteBuffer bb = ByteBuffer.wrap(new byte[16]); + bb.putLong(uuid.getMostSignificantBits()); + bb.putLong(uuid.getLeastSignificantBits()); + return bb.array(); + } } \ No newline at end of file diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/SnowflakeIdGeneratorTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/SnowflakeIdGeneratorTest.java index 5c0273aa4..659a2cc23 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/SnowflakeIdGeneratorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/SnowflakeIdGeneratorTest.java @@ -19,6 +19,10 @@ import org.junit.Test; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class SnowflakeIdGeneratorTest { @@ -32,5 +36,16 @@ public void testTillNextMillis() { public void testGetId() { assertTrue((new SnowflakeIdGenerator()).getId() != 0); } + + @Test + public void testUniqueness() { + SnowflakeIdGenerator generator = new SnowflakeIdGenerator(); + Set ids = new HashSet<>(); + for (int j = 0; j < 100; j++) { + ids.add(generator.getId()); + } + + assertEquals(100, ids.size()); + } } From b6d3fb6f6d60a8181c7051c34ae190e9050fd4fe Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 14 Jun 2022 08:31:31 +0530 Subject: [PATCH 45/78] refactoring --- .../no2/mvstore/compat/v3/MigrationUtil.java | 2 +- .../CollectionFindByCompoundIndexTest.java | 19 +++++- .../rocksdb/formatter/NitriteSerializers.java | 2 +- .../CollectionFindByCompoundIndexTest.java | 19 +++++- .../collection/DefaultNitriteCollection.java | 2 +- .../dizitart/no2/collection/FindOptions.java | 21 ++++++ .../org/dizitart/no2/collection/FindPlan.java | 1 + .../collection/operation/FindOptimizer.java | 5 +- .../collection/operation/IndexOperations.java | 20 +++--- .../collection/operation/ReadOperations.java | 27 ++++---- .../collection/operation/WriteOperations.java | 64 +++++++++---------- .../java/org/dizitart/no2/common/DBNull.java | 2 - .../no2/{index => common}/DBValue.java | 4 +- .../{UnionStream.java => ConcatStream.java} | 6 +- .../no2/common/streams/DocumentSorter.java | 7 +- .../org/dizitart/no2/index/CompoundIndex.java | 1 + .../java/org/dizitart/no2/index/IndexMap.java | 1 + .../org/dizitart/no2/index/IndexScanner.java | 1 + .../dizitart/no2/index/SingleFieldIndex.java | 2 +- ...nStreamTest.java => ConcatStreamTest.java} | 10 +-- .../org/dizitart/no2/index/IndexMapTest.java | 1 + .../CollectionFindByCompoundIndexTest.java | 52 ++++++++++++++- 22 files changed, 188 insertions(+), 81 deletions(-) rename nitrite/src/main/java/org/dizitart/no2/{index => common}/DBValue.java (95%) rename nitrite/src/main/java/org/dizitart/no2/common/streams/{UnionStream.java => ConcatStream.java} (92%) rename nitrite/src/test/java/org/dizitart/no2/common/streams/{UnionStreamTest.java => ConcatStreamTest.java} (76%) diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java index 08ba4791b..cbc75f723 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java @@ -23,7 +23,7 @@ import org.dizitart.no2.common.Fields; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ValidationException; -import org.dizitart.no2.index.DBValue; +import org.dizitart.no2.common.DBValue; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexMeta; import org.dizitart.no2.store.UserCredential; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java index 8fd30693e..f1c1d927e 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java @@ -19,6 +19,7 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.DocumentCursor; +import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.FindPlan; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.common.tuples.Pair; @@ -82,7 +83,7 @@ public void testFindByOrFilterAndFilter() { where("firstName").eq("fn3"), where("lastName").eq("ln2") ) - ) + ), FindOptions.withDistinct() ); assertEquals(2, cursor.size()); @@ -199,6 +200,22 @@ public void testFindByOrFilter() throws ParseException { FindPlan findPlan = cursor.getFindPlan(); assertEquals(3, findPlan.getSubPlans().size()); + assertEquals(5, cursor.size()); + + // distinct + cursor = collection.find( + or( + or( + where("lastName").eq("ln2"), + where("firstName").notEq("fn1") + ), + where("birthDay").eq(simpleDateFormat.parse("2012-07-01T16:02:48.440Z")), + where("firstName").notEq("fn1") + ), FindOptions.withDistinct() + ); + + findPlan = cursor.getFindPlan(); + assertEquals(3, findPlan.getSubPlans().size()); assertEquals(3, cursor.size()); } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java index d97e2bbf9..88da8c9e8 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java @@ -12,7 +12,7 @@ import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.tuples.Pair; -import org.dizitart.no2.index.DBValue; +import org.dizitart.no2.common.DBValue; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexMeta; import org.dizitart.no2.store.UserCredential; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java index 8fd30693e..f1c1d927e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java @@ -19,6 +19,7 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.DocumentCursor; +import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.FindPlan; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.common.tuples.Pair; @@ -82,7 +83,7 @@ public void testFindByOrFilterAndFilter() { where("firstName").eq("fn3"), where("lastName").eq("ln2") ) - ) + ), FindOptions.withDistinct() ); assertEquals(2, cursor.size()); @@ -199,6 +200,22 @@ public void testFindByOrFilter() throws ParseException { FindPlan findPlan = cursor.getFindPlan(); assertEquals(3, findPlan.getSubPlans().size()); + assertEquals(5, cursor.size()); + + // distinct + cursor = collection.find( + or( + or( + where("lastName").eq("ln2"), + where("firstName").notEq("fn1") + ), + where("birthDay").eq(simpleDateFormat.parse("2012-07-01T16:02:48.440Z")), + where("firstName").notEq("fn1") + ), FindOptions.withDistinct() + ); + + findPlan = cursor.getFindPlan(); + assertEquals(3, findPlan.getSubPlans().size()); assertEquals(3, cursor.size()); } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 17a7e7436..812af5836 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -112,7 +112,7 @@ public WriteResult update(Document document, boolean insertIfAbsent) { if (document.hasId()) { return update(createUniqueFilter(document), document, updateOptions(false)); } else { - throw new NotIdentifiableException("update operation failed as no id value found for the document"); + throw new NotIdentifiableException("Update operation failed as the document does not have id"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/FindOptions.java b/nitrite/src/main/java/org/dizitart/no2/collection/FindOptions.java index 56be8bdcd..50b3d8c1a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/FindOptions.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/FindOptions.java @@ -39,6 +39,7 @@ public class FindOptions { private SortableFields orderBy; private Long skip; private Long limit; + private boolean distinct = false; /** * Specifies the {@link Collator}. @@ -95,6 +96,15 @@ public static FindOptions limitBy(long limit) { return findOptions; } + /** + * Indicates if the find operation should return distinct results. + */ + public static FindOptions withDistinct() { + FindOptions findOptions = new FindOptions(); + findOptions.distinct(true); + return findOptions; + } + /** * Skip find options. * @@ -156,4 +166,15 @@ public FindOptions thenOrderBy(String fieldName, SortOrder sortOrder) { } return this; } + + /** + * Indicates if the find operation should return distinct and unique results. + * + * @param distinct the distinct + * @return the find options + */ + public FindOptions withDistinct(boolean distinct) { + this.distinct = distinct; + return this; + } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/FindPlan.java b/nitrite/src/main/java/org/dizitart/no2/collection/FindPlan.java index aea4fe123..a2f86a27d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/FindPlan.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/FindPlan.java @@ -48,6 +48,7 @@ public class FindPlan { private Long skip; private Long limit; + private boolean distinct; private Collator collator; diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/FindOptimizer.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/FindOptimizer.java index 58b129016..31c209e79 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/FindOptimizer.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/FindOptimizer.java @@ -49,6 +49,7 @@ public FindPlan optimize(Filter filter, if (findOptions != null) { findPlan.setCollator(findOptions.collator()); + findPlan.setDistinct(findOptions.distinct()); } return findPlan; } @@ -178,7 +179,7 @@ private void planForIndexOnlyFilters(FindPlan findPlan, Set in // if filter is compatible with already identified index only filter then add indexOnlyFilters.add(indexScanFilter); } else { - throw new FilterException("a query can not have multiple index only filters"); + throw new FilterException("A query can not have multiple index only filters"); } } } @@ -283,7 +284,7 @@ private void planForCollectionScanningFilters(FindPlan findPlan, Set filters) { for (Filter filter : filters) { if (filter instanceof IndexOnlyFilter) { - throw new FilterException("collection scan is not supported for the filter " + filter); + throw new FilterException("Collection scan is not supported for the filter " + filter); } else if (filter instanceof TextFilter) { throw new FilterException(((TextFilter) filter).getField() + " is not full-text indexed"); } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java index 59249ce8c..5ee63e87d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java @@ -61,7 +61,7 @@ void createIndex(Fields fields, String indexType) { indexDescriptor = indexManager.createIndexDescriptor(fields, indexType); } else { // if index already there throw - throw new IndexingException("index already exists on " + fields); + throw new IndexingException("index already exists on fields: " + fields); } buildIndex(indexDescriptor, false); @@ -75,12 +75,12 @@ void buildIndex(IndexDescriptor indexDescriptor, boolean rebuild) { buildIndexInternal(indexDescriptor, rebuild); return; } - throw new IndexingException("indexing is already running on " + indexDescriptor.getIndexFields()); + throw new IndexingException("Index build already in progress on fields: " + indexDescriptor.getIndexFields()); } void dropIndex(Fields fields) { if (getBuildFlag(fields).get()) { - throw new IndexingException("cannot drop index as indexing is running on " + fields); + throw new IndexingException("Index build already in progress on fields: " + fields); } IndexDescriptor indexDescriptor = findIndexDescriptor(fields); @@ -92,14 +92,14 @@ void dropIndex(Fields fields) { indexManager.dropIndexDescriptor(fields); indexBuildTracker.remove(fields); } else { - throw new IndexingException(fields + " is not indexed"); + throw new IndexingException("Index does not exist on fields: " + fields); } } void dropAllIndices() { for (Map.Entry entry : indexBuildTracker.entrySet()) { if (entry.getValue() != null && entry.getValue().get()) { - throw new IndexingException("cannot drop index as indexing is running on " + entry.getKey()); + throw new IndexingException("Index build already in progress on fields: " + entry.getKey()); } } @@ -144,7 +144,11 @@ IndexDescriptor findIndexDescriptor(Fields field) { return indexManager.findExactIndexDescriptor(field); } - AtomicBoolean getBuildFlag(Fields field) { + boolean shouldRebuildIndex(Fields fields) { + return indexManager.isDirtyIndex(fields) && !getBuildFlag(fields).get(); + } + + private AtomicBoolean getBuildFlag(Fields field) { AtomicBoolean flag = indexBuildTracker.get(field); if (flag != null) return flag; @@ -153,10 +157,6 @@ AtomicBoolean getBuildFlag(Fields field) { return flag; } - boolean shouldRebuildIndex(Fields fields) { - return indexManager.isDirtyIndex(fields) && !getBuildFlag(fields).get(); - } - private void buildIndexInternal(IndexDescriptor indexDescriptor, boolean rebuild) { Fields fields = indexDescriptor.getIndexFields(); try { diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java index 58c0e13fe..364616c52 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java @@ -105,6 +105,13 @@ private void prepareLogicalFilter(LogicalFilter logicalFilter) { } } + private DocumentCursor createCursor(FindPlan findPlan) { + RecordStream> recordStream = findSuitableStream(findPlan); + DocumentStream cursor = new DocumentStream(recordStream, processorChain); + cursor.setFindPlan(findPlan); + return cursor; + } + private RecordStream> findSuitableStream(FindPlan findPlan) { RecordStream> rawStream; @@ -115,17 +122,20 @@ private RecordStream> findSuitableStream(FindPlan find RecordStream> suitableStream = findSuitableStream(subPlan); subStreams.add(suitableStream); } - // union of all suitable stream of all sub plans - rawStream = new UnionStream(subStreams); - // return only distinct items - rawStream = new DistinctStream(rawStream); + // concat all suitable stream of all sub plans + rawStream = new ConcatStream(subStreams); + + if (findPlan.isDistinct()) { + // return only distinct items + rawStream = new DistinctStream(rawStream); + } } else { // and or single filter if (findPlan.getByIdFilter() != null) { FieldBasedFilter byIdFilter = findPlan.getByIdFilter(); NitriteId nitriteId = NitriteId.createId((String) byIdFilter.getValue()); - rawStream = RecordStream.single(pair(nitriteId, nitriteMap.get(nitriteId))); + rawStream = RecordStream.single(pair(nitriteId, getById(nitriteId))); } else { IndexDescriptor indexDescriptor = findPlan.getIndexDescriptor(); if (indexDescriptor != null) { @@ -160,11 +170,4 @@ private RecordStream> findSuitableStream(FindPlan find return rawStream; } - - private DocumentCursor createCursor(FindPlan findPlan) { - RecordStream> recordStream = findSuitableStream(findPlan); - DocumentStream cursor = new DocumentStream(recordStream, processorChain); - cursor.setFindPlan(findPlan); - return cursor; - } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java index 5294a0346..9f491055b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/WriteOperations.java @@ -85,22 +85,20 @@ WriteResult insert(Document... documents) { // run processors Document unprocessed = newDoc.clone(); Document processed = processorChain.processBeforeWrite(unprocessed); - log.debug("Document processed from {} to {} before insert", newDoc, processed); + log.debug("Processed document with id: {}", nitriteId); - log.debug("Inserting processed document {} in {}", processed, nitriteMap.getName()); + log.debug("Inserting processed document with id {}", nitriteId); Document already = nitriteMap.putIfAbsent(nitriteId, processed); if (already != null) { - log.warn("Another document {} already exists with same id {}", already, nitriteId); - - throw new UniqueConstraintException("id constraint violation, " + - "entry with same id already exists in " + nitriteMap.getName()); + throw new UniqueConstraintException("Document with id " + nitriteId + " already exists" + + " in " + nitriteMap.getName()); } else { try { documentIndexWriter.writeIndexEntry(processed); } catch (UniqueConstraintException | IndexingException e) { - log.error("Index operation has failed during insertion for the document " - + document + " in " + nitriteMap.getName(), e); + log.error("Error while writing index entry for document with id : {} in {}", + nitriteId, nitriteMap.getName(), e); nitriteMap.remove(nitriteId); throw e; } @@ -113,7 +111,7 @@ WriteResult insert(Document... documents) { eventInfo.setTimestamp(time); eventInfo.setEventType(EventType.Insert); eventInfo.setOriginator(source); - alert(EventType.Insert, eventInfo); + alert(eventInfo); } WriteResultImpl result = new WriteResultImpl(); @@ -135,7 +133,7 @@ WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) } if (document.size() == 0) { - alert(EventType.Update, new CollectionEventInfo<>()); + log.debug("No fields to update"); return writeResult; } @@ -154,7 +152,7 @@ WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) long time = System.currentTimeMillis(); NitriteId nitriteId = newDoc.getId(); - log.debug("Document to update {} in {}", newDoc, nitriteMap.getName()); + log.debug("Updating document with id {} in {}", nitriteId, nitriteMap.getName()); if (!REPLICATOR.contentEquals(document.getSource())) { document.remove(DOC_SOURCE); @@ -170,21 +168,21 @@ WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) // run processor Document unprocessed = newDoc.clone(); Document processed = processorChain.processBeforeWrite(unprocessed); - log.debug("Document processed from {} to {} before update", newDoc, processed); + log.debug("Processed document with id {}", nitriteId); nitriteMap.put(nitriteId, processed); - log.debug("Document {} updated in {}", processed, nitriteMap.getName()); - - // if 'update' only contains id value, affected count = 0 - if (document.size() > 0) { - writeResult.addToList(nitriteId); - } + log.debug("Updated document with id {} in {}", nitriteId, nitriteMap.getName()); try { documentIndexWriter.updateIndexEntry(oldDocument, processed); + + // if 'update' only contains id value, affected count = 0 + if (document.size() > 0) { + writeResult.addToList(nitriteId); + } } catch (UniqueConstraintException | IndexingException e) { - log.error("Index operation failed during update, reverting changes for the document " - + oldDocument + " in " + nitriteMap.getName(), e); + log.error("Error while writing index entry for document with id : {} in {}", + nitriteId, nitriteMap.getName(), e); nitriteMap.put(nitriteId, oldDocument); documentIndexWriter.updateIndexEntry(processed, oldDocument); throw e; @@ -195,12 +193,12 @@ WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) eventInfo.setEventType(EventType.Update); eventInfo.setTimestamp(time); eventInfo.setOriginator(source); - alert(EventType.Update, eventInfo); + alert(eventInfo); } } if (count == 0) { - log.debug("No document found to update by the filter {} in {}", filter, nitriteMap.getName()); + log.debug("No documents found for update in {}", nitriteMap.getName()); if (updateOptions.isInsertIfAbsent()) { return insert(update); } else { @@ -208,8 +206,7 @@ WriteResult update(Filter filter, Document update, UpdateOptions updateOptions) } } - log.debug("Filter {} updated total {} document(s) with options {} in {}", - filter, count, updateOptions, nitriteMap.getName()); + log.debug("Updated {} documents in {}", count, nitriteMap.getName()); log.debug("Returning write result {} for collection {}", writeResult, nitriteMap.getName()); return writeResult; @@ -227,11 +224,11 @@ WriteResult remove(Filter filter, boolean justOnce) { // run processor Document unprocessed = document.clone(); Document processed = processorChain.processAfterRead(unprocessed); - log.debug("Document processed from {} to {} after remove", document, processed); + log.debug("Processed document with id : {}", processed.getId()); CollectionEventInfo eventInfo = removeAndCreateEvent(processed, result); if (eventInfo != null) { - alert(EventType.Remove, eventInfo); + alert(eventInfo); } if (justOnce) { @@ -241,14 +238,11 @@ WriteResult remove(Filter filter, boolean justOnce) { } if (count == 0) { - log.debug("No document found to remove by the filter {} in {}", filter, nitriteMap.getName()); + log.debug("No documents found for filter {}", filter); return result; } - log.debug("Filter {} removed total {} document(s) with options {} from {}", - filter, count, justOnce, nitriteMap.getName()); - - log.debug("Returning write result {} for collection {}", result, nitriteMap.getName()); + log.debug("Removed {} documents for filter : {}", count, filter); return result; } @@ -257,7 +251,7 @@ WriteResult remove(Document document) { CollectionEventInfo eventInfo = removeAndCreateEvent(document, result); if (eventInfo != null) { eventInfo.setOriginator(document.getSource()); - alert(EventType.Remove, eventInfo); + alert(eventInfo); } return result; } @@ -274,7 +268,7 @@ private CollectionEventInfo removeAndCreateEvent(Document document, Wr document.put(DOC_REVISION, rev + 1); document.put(DOC_MODIFIED, removedAt); - log.debug("Document removed {} from {}", document, nitriteMap.getName()); + log.debug("Removed document with id {} from {}", document, nitriteMap.getName()); CollectionEventInfo eventInfo = new CollectionEventInfo<>(); Document eventDoc = document.clone(); @@ -286,8 +280,8 @@ private CollectionEventInfo removeAndCreateEvent(Document document, Wr return null; } - private void alert(EventType action, CollectionEventInfo changedItem) { - log.debug("Notifying {} event for item {} from {}", action, changedItem, nitriteMap.getName()); + private void alert(CollectionEventInfo changedItem) { + log.debug("Alerting event listeners for action : {} in {}", changedItem.getEventType(), nitriteMap.getName()); if (eventBus != null) { eventBus.post(changedItem); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/DBNull.java b/nitrite/src/main/java/org/dizitart/no2/common/DBNull.java index 48e8b7b37..93d6c143a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/DBNull.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/DBNull.java @@ -1,7 +1,5 @@ package org.dizitart.no2.common; -import org.dizitart.no2.index.DBValue; - import java.io.Serializable; /** diff --git a/nitrite/src/main/java/org/dizitart/no2/index/DBValue.java b/nitrite/src/main/java/org/dizitart/no2/common/DBValue.java similarity index 95% rename from nitrite/src/main/java/org/dizitart/no2/index/DBValue.java rename to nitrite/src/main/java/org/dizitart/no2/common/DBValue.java index 161b488ce..a1f67cd87 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/DBValue.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/DBValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Nitrite author or authors. + * Copyright (c) 2017-2022 Nitrite author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ * */ -package org.dizitart.no2.index; +package org.dizitart.no2.common; import lombok.AccessLevel; import lombok.Data; diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/UnionStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/ConcatStream.java similarity index 92% rename from nitrite/src/main/java/org/dizitart/no2/common/streams/UnionStream.java rename to nitrite/src/main/java/org/dizitart/no2/common/streams/ConcatStream.java index 1be91dc60..faf1e18c2 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/UnionStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/ConcatStream.java @@ -25,12 +25,12 @@ import java.util.*; /** - * Represents an union of multiple distinct nitrite document stream. + * Represents an concatenation of multiple distinct nitrite document stream. * * @author Anindya Chatterjee * @since 4.0 */ -public class UnionStream implements RecordStream> { +public class ConcatStream implements RecordStream> { private final Collection>> streams; /** @@ -38,7 +38,7 @@ public class UnionStream implements RecordStream> { * * @param streams the streams */ - public UnionStream(Collection>> streams) { + public ConcatStream(Collection>> streams) { this.streams = streams; } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentSorter.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentSorter.java index c3b7307f6..d0361277c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentSorter.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentSorter.java @@ -75,9 +75,9 @@ public int compare(Pair pair1, Pair pa } else { // validate comparable - if (value1.getClass().isArray() || value1 instanceof Iterable - || value2.getClass().isArray() || value2 instanceof Iterable) { - throw new ValidationException("cannot sort on an array or collection object"); + if (!(value1 instanceof Comparable) || !(value2 instanceof Comparable)) { + throw new ValidationException("Cannot compare " + value1.getClass() + + " and " + value2.getClass()); } // compare values @@ -95,6 +95,7 @@ public int compare(Pair pair1, Pair pa result *= -1; } + // if both values are equal, continue to next sort order if (result != 0) { return result; } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/CompoundIndex.java b/nitrite/src/main/java/org/dizitart/no2/index/CompoundIndex.java index beec17621..0a8f9744d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/CompoundIndex.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/CompoundIndex.java @@ -21,6 +21,7 @@ import org.dizitart.no2.collection.FindPlan; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.DBNull; +import org.dizitart.no2.common.DBValue; import org.dizitart.no2.common.FieldValues; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.tuples.Pair; diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexMap.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexMap.java index 8eecb4f4f..828c7ca21 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexMap.java @@ -21,6 +21,7 @@ import lombok.Setter; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.DBNull; +import org.dizitart.no2.common.DBValue; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.store.NitriteMap; diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexScanner.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexScanner.java index 0236b8395..5684f8891 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexScanner.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexScanner.java @@ -18,6 +18,7 @@ package org.dizitart.no2.index; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.DBValue; import org.dizitart.no2.exceptions.FilterException; import org.dizitart.no2.filters.ComparableFilter; diff --git a/nitrite/src/main/java/org/dizitart/no2/index/SingleFieldIndex.java b/nitrite/src/main/java/org/dizitart/no2/index/SingleFieldIndex.java index 592caad86..2d6ae64ab 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/SingleFieldIndex.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/SingleFieldIndex.java @@ -21,10 +21,10 @@ import org.dizitart.no2.collection.FindPlan; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.DBNull; +import org.dizitart.no2.common.DBValue; import org.dizitart.no2.common.FieldValues; import org.dizitart.no2.common.Fields; import org.dizitart.no2.filters.ComparableFilter; -import org.dizitart.no2.filters.Filter; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; diff --git a/nitrite/src/test/java/org/dizitart/no2/common/streams/UnionStreamTest.java b/nitrite/src/test/java/org/dizitart/no2/common/streams/ConcatStreamTest.java similarity index 76% rename from nitrite/src/test/java/org/dizitart/no2/common/streams/UnionStreamTest.java rename to nitrite/src/test/java/org/dizitart/no2/common/streams/ConcatStreamTest.java index 4d6724e39..49f870e30 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/streams/UnionStreamTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/streams/ConcatStreamTest.java @@ -23,17 +23,17 @@ import static org.junit.Assert.assertTrue; -public class UnionStreamTest { +public class ConcatStreamTest { @Test public void testConstructor() { - assertTrue((new UnionStream(new ArrayList<>())).toList().isEmpty()); + assertTrue((new ConcatStream(new ArrayList<>())).toList().isEmpty()); } @Test public void testIterator() { - UnionStream unionStream = new UnionStream(new ArrayList<>()); - unionStream.iterator(); - assertTrue(unionStream.toList().isEmpty()); + ConcatStream concatStream = new ConcatStream(new ArrayList<>()); + concatStream.iterator(); + assertTrue(concatStream.toList().isEmpty()); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/index/IndexMapTest.java b/nitrite/src/test/java/org/dizitart/no2/index/IndexMapTest.java index 647b16da4..67ba01f71 100644 --- a/nitrite/src/test/java/org/dizitart/no2/index/IndexMapTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/index/IndexMapTest.java @@ -18,6 +18,7 @@ package org.dizitart.no2.index; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.DBValue; import org.dizitart.no2.store.memory.InMemoryMap; import org.junit.Test; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java index 8fd30693e..3379c1dca 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindByCompoundIndexTest.java @@ -19,6 +19,7 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.DocumentCursor; +import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.FindPlan; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.common.tuples.Pair; @@ -85,7 +86,7 @@ public void testFindByOrFilterAndFilter() { ) ); - assertEquals(2, cursor.size()); + assertEquals(3, cursor.size()); FindPlan findPlan = cursor.getFindPlan(); assertNull(findPlan.getIndexScanFilter()); @@ -96,6 +97,39 @@ public void testFindByOrFilterAndFilter() { assertNotNull(findPlan.getSubPlans().get(0).getIndexScanFilter()); assertNotNull(findPlan.getSubPlans().get(1).getIndexScanFilter()); + assertEquals(1, cursor.toList().stream().filter(d -> + d.get("firstName", String.class).equals("fn2") + && d.get("lastName", String.class).equals("ln2")).count()); + + assertEquals(2, cursor.toList().stream().filter(d -> + d.get("firstName", String.class).equals("fn3") + && d.get("lastName", String.class).equals("ln2")).count()); + + // distinct test + cursor = collection.find( + or( + and( + where("lastName").eq("ln2"), + where("firstName").notEq("fn1") + ), + and( + where("firstName").eq("fn3"), + where("lastName").eq("ln2") + ) + ), FindOptions.withDistinct() + ); + + assertEquals(2, cursor.size()); + + findPlan = cursor.getFindPlan(); + assertNull(findPlan.getIndexScanFilter()); + assertNull(findPlan.getCollectionScanFilter()); + assertNotNull(findPlan.getSubPlans()); + + assertEquals(2, findPlan.getSubPlans().size()); + assertNotNull(findPlan.getSubPlans().get(0).getIndexScanFilter()); + assertNotNull(findPlan.getSubPlans().get(1).getIndexScanFilter()); + assertEquals(1, cursor.toList().stream().filter(d -> d.get("firstName", String.class).equals("fn2") && d.get("lastName", String.class).equals("ln2")).count()); @@ -199,6 +233,22 @@ public void testFindByOrFilter() throws ParseException { FindPlan findPlan = cursor.getFindPlan(); assertEquals(3, findPlan.getSubPlans().size()); + assertEquals(5, cursor.size()); + + // distinct + cursor = collection.find( + or( + or( + where("lastName").eq("ln2"), + where("firstName").notEq("fn1") + ), + where("birthDay").eq(simpleDateFormat.parse("2012-07-01T16:02:48.440Z")), + where("firstName").notEq("fn1") + ), FindOptions.withDistinct() + ); + + findPlan = cursor.getFindPlan(); + assertEquals(3, findPlan.getSubPlans().size()); assertEquals(3, cursor.size()); } From 543f2c6f303ce76086130f1f585a8df59b3f3907 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 16 Jun 2022 11:29:32 +0530 Subject: [PATCH 46/78] refactoring --- .../dizitart/no2/common/mapper/Mappable.java | 4 +-- .../no2/common/mapper/MappableFactory.java | 27 +++++++++++++++++ .../no2/common/mapper/MappableMapper.java | 30 +++++++++++++++---- 3 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/Mappable.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/Mappable.java index d16297757..c0cbf64f0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/Mappable.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/Mappable.java @@ -19,7 +19,7 @@ import org.dizitart.no2.collection.Document; /** - * An object that serializes itself to a {@link Document} + * An object that maps itself to a {@link Document} * and vice versa. * * @author Anindya Chatterjee @@ -35,7 +35,7 @@ public interface Mappable { Document write(NitriteMapper mapper); /** - * Reads the `document` and populate all fields of this instance. + * Reads the document and populate all fields of this instance. * * @param mapper the {@link NitriteMapper} instance used. * @param document the document. diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java new file mode 100644 index 000000000..f333bc2f7 --- /dev/null +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.common.mapper; + +/** + * Represents a factory for creating instances of a type. + * + * @author Anindya Chatterjee + */ +public interface MappableFactory { + T create(); +} diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java index 443fb4666..056639d53 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java @@ -34,6 +34,7 @@ */ public class MappableMapper implements NitriteMapper { private final Set> valueTypes; + private final Map, MappableFactory> mappableFactories; /** * Instantiates a new {@link MappableMapper}. @@ -42,6 +43,7 @@ public class MappableMapper implements NitriteMapper { */ public MappableMapper(Class... valueTypes) { this.valueTypes = new HashSet<>(); + this.mappableFactories = new HashMap<>(); init(listOf(valueTypes)); } @@ -53,20 +55,27 @@ public MappableMapper(Class... valueTypes) { * @param type the type * @return the target */ + @SuppressWarnings("unchecked") protected Target convertFromDocument(Document source, Class type) { if (source == null) { return null; } if (Mappable.class.isAssignableFrom(type)) { - Target item = newInstance(type, false); - if (item == null) return null; + Target item; + if (mappableFactories.containsKey(type)) { + MappableFactory factory = mappableFactories.get(type); + item = (Target) factory.create(); + } else { + item = newInstance(type, false); + } + if (item == null) return null; ((Mappable) item).read(this, source); return item; } - throw new ObjectMappingException("object must implements Mappable"); + throw new ObjectMappingException(type.getName() + " is not a Mappable"); } /** @@ -82,7 +91,7 @@ protected Document convertToDocument(Source source) { return mappable.write(this); } - throw new ObjectMappingException("object must implements Mappable"); + throw new ObjectMappingException("Object of type " + source.getClass().getName() + " is not Mappable"); } /** @@ -94,6 +103,17 @@ protected void addValueType(Class valueType) { this.valueTypes.add(valueType); } + /** + * Register a {@link Mappable} factory to be used when converting a document to an object. + * + * @param the type parameter + * @param factory the factory + * @param type the type + */ + public void registerMappable(MappableFactory factory, Class type) { + mappableFactories.put(type, factory); + } + @Override @SuppressWarnings("unchecked") public Target convert(Source source, Class type) { @@ -111,7 +131,7 @@ public Target convert(Source source, Class type) { } } - throw new ObjectMappingException("object must implements Mappable"); + throw new ObjectMappingException("Can't convert object of type " + source.getClass() + " to type " + type); } @Override From 89ffe205b255bd1fab651345dafd724c08cf949d Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 22 Jun 2022 12:28:47 +0530 Subject: [PATCH 47/78] refactoring --- .../no2/common/mapper/JacksonMapper.java | 4 ++-- .../dizitart/no2/mvstore/MVStoreUtils.java | 20 ++++++++--------- .../no2/mvstore/compat/v3/MigrationUtil.java | 4 ++-- .../no2/sync/DataGateSocketListener.java | 2 +- .../dizitart/no2/sync/MessageTemplate.java | 10 ++++----- .../dizitart/no2/sync/MessageTransformer.java | 2 +- .../java/org/dizitart/no2/sync/Replica.java | 4 ++-- .../org/dizitart/no2/sync/ReplicaBuilder.java | 12 +++++----- .../dizitart/no2/sync/net/DataGateClient.java | 4 ++-- .../dizitart/no2/integration/UserClient.java | 4 ++-- .../org/dizitart/no2/rocksdb/RocksDBMap.java | 10 ++++----- .../no2/rocksdb/RocksDBReference.java | 4 ++-- .../dizitart/no2/rocksdb/RocksDBStore.java | 10 ++++----- .../no2/rocksdb/RocksDBStoreUtils.java | 2 +- .../dizitart/no2/rocksdb/StoreFactory.java | 4 ++-- .../formatter/DefaultTimeKeySerializers.java | 10 ++++----- .../formatter/KryoObjectFormatter.java | 8 +++---- .../java/org/dizitart/no2/NitriteTest.java | 2 +- .../dizitart/no2/integration/TestUtil.java | 2 +- .../dizitart/no2/spatial/SpatialIndex.java | 6 ++--- .../dizitart/no2/spatial/SpatialIndexer.java | 2 +- .../org/dizitart/no2/support/Exporter.java | 2 +- .../org/dizitart/no2/support/Importer.java | 2 +- .../no2/support/NitriteJsonExporter.java | 2 +- .../no2/support/NitriteJsonImporter.java | 2 +- .../main/java/org/dizitart/no2/Nitrite.java | 4 ++-- .../java/org/dizitart/no2/NitriteConfig.java | 12 +++++----- .../org/dizitart/no2/NitriteDatabase.java | 10 ++++----- .../collection/DefaultNitriteCollection.java | 4 ++-- .../no2/collection/NitriteDocument.java | 4 ++-- .../dizitart/no2/collection/NitriteId.java | 2 +- .../collection/operation/IndexOperations.java | 4 ++-- .../collection/operation/ReadOperations.java | 3 ++- .../org/dizitart/no2/common/Constants.java | 2 +- .../no2/common/crypto/AESEncryptor.java | 4 ++-- .../no2/common/mapper/MappableFactory.java | 1 + .../no2/common/module/PluginManager.java | 12 +++++----- .../StringFieldEncryptionProcessor.java | 4 ++-- .../no2/common/streams/BoundedStream.java | 6 ++--- .../no2/common/streams/DocumentStream.java | 4 ++-- .../no2/common/streams/FilteredStream.java | 2 +- .../common/streams/JoinedDocumentStream.java | 2 +- .../streams/ProjectedDocumentStream.java | 2 +- .../org/dizitart/no2/common/util/Base64.java | 2 +- .../org/dizitart/no2/common/util/Numbers.java | 4 ++-- .../dizitart/no2/common/util/ObjectUtils.java | 6 ++--- .../no2/common/util/ValidationUtils.java | 10 ++++----- .../no2/filters/ElementMatchFilter.java | 2 +- .../java/org/dizitart/no2/filters/Filter.java | 6 ++--- .../dizitart/no2/index/ComparableIndexer.java | 5 +++++ .../org/dizitart/no2/index/CompoundIndex.java | 22 +++++++++---------- .../org/dizitart/no2/index/IndexScanner.java | 4 ++-- .../org/dizitart/no2/index/NitriteIndex.java | 11 +++++----- .../no2/index/NitriteTextIndexer.java | 2 +- .../org/dizitart/no2/index/TextIndex.java | 6 ++--- .../no2/index/fulltext/BaseTextTokenizer.java | 10 ++++----- .../no2/migration/MigrationManager.java | 8 +++---- .../no2/repository/AnnotationScanner.java | 2 +- .../no2/repository/IndexValidator.java | 6 ++--- .../no2/repository/MutatedObjectStream.java | 2 +- .../dizitart/no2/repository/ObjectCursor.java | 14 ++++++------ .../no2/repository/ObjectIdField.java | 2 +- .../dizitart/no2/repository/Reflector.java | 6 ++--- .../no2/repository/RepositoryFactory.java | 6 ++--- .../no2/repository/RepositoryOperations.java | 20 ++++++++--------- .../no2/store/UserAuthenticationService.java | 12 +++++----- .../DefaultTransactionalCollection.java | 22 +++++++++---------- .../no2/transaction/NitriteTransaction.java | 12 +++++----- .../org/dizitart/no2/transaction/Session.java | 2 +- .../no2/transaction/TransactionConfig.java | 2 +- .../no2/transaction/TransactionStore.java | 2 +- .../dizitart/no2/collection/FindPlanTest.java | 3 ++- .../streams/SortedDocumentStreamTest.java | 3 ++- .../dizitart/no2/integration/TestUtil.java | 2 +- 74 files changed, 219 insertions(+), 213 deletions(-) diff --git a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapper.java b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapper.java index 0af159b4f..825c1c508 100644 --- a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapper.java +++ b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapper.java @@ -106,7 +106,7 @@ public Target convert(Source source, Class type) { } } - throw new ObjectMappingException("failed to convert using jackson"); + throw new ObjectMappingException("Failed to convert using jackson"); } @Override @@ -124,7 +124,7 @@ public boolean isValue(Object object) { JsonNode node = objectMapper.convertValue(object, JsonNode.class); return node != null && node.isValueNode(); } catch (Exception ex) { - throw new ObjectMappingException("error while checking for value type", ex); + throw new ObjectMappingException("Error while checking for value type", ex); } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java index 56923266f..cc24dc112 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java @@ -48,7 +48,7 @@ static MVStore openOrCreate(MVStoreConfig storeConfig) { testForMigration(store); } catch (IllegalStateException ise) { if (ise.getMessage().contains("file is locked")) { - throw new NitriteIOException("database is already opened in other process"); + throw new NitriteIOException("Database is already opened in other process"); } if (dbFile != null) { @@ -71,24 +71,24 @@ static MVStore openOrCreate(MVStoreConfig storeConfig) { } } else { if (storeConfig.isReadOnly()) { - throw new NitriteIOException("cannot create readonly database", ise); + throw new NitriteIOException("Cannot create readonly database", ise); } } } catch (InvalidOperationException | NitriteIOException ex) { throw ex; } catch (Exception e) { - throw new NitriteIOException("database file is corrupted", e); + throw new NitriteIOException("Database file is corrupted", e); } } else { - throw new NitriteIOException("unable to create in-memory database", ise); + throw new NitriteIOException("Unable to create in-memory database", ise); } } catch (IllegalArgumentException iae) { if (dbFile != null) { if (!dbFile.getParentFile().exists()) { - throw new NitriteIOException("directory " + dbFile.getParent() + " does not exists", iae); + throw new NitriteIOException("Directory " + dbFile.getParent() + " does not exists", iae); } } - throw new NitriteIOException("unable to create database file", iae); + throw new NitriteIOException("Unable to create database file", iae); } finally { if (store != null) { store.setRetentionTime(0); @@ -130,7 +130,7 @@ private static MVStore.Builder createBuilder(MVStoreConfig mvStoreConfig) { if (mvStoreConfig.isReadOnly()) { if (isNullOrEmpty(mvStoreConfig.filePath())) { - throw new InvalidOperationException("unable create readonly in-memory database"); + throw new InvalidOperationException("Unable create readonly in-memory database"); } builder = builder.readOnly(); } @@ -192,14 +192,14 @@ private static void switchFiles(File newFile, File orgFile) { File backupFile = new File(orgFile.getPath() + "_old"); if (orgFile.renameTo(backupFile)) { if (!newFile.renameTo(orgFile)) { - throw new NitriteIOException("could not rename new data file"); + throw new NitriteIOException("Could not rename new data file"); } if (!backupFile.delete()) { - throw new NitriteIOException("could not delete backup data file"); + throw new NitriteIOException("Could not delete backup data file"); } } else { - throw new NitriteIOException("could not create backup copy of old data file"); + throw new NitriteIOException("Could not create backup copy of old data file"); } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java index cbc75f723..b85aaa5e2 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java @@ -67,7 +67,7 @@ public static void migrate(MVStore newStore, MVStore oldStore) { oldStore.commit(); newStore.commit(); } catch (Throwable t) { - throw new NitriteIOException("migration of old data has failed", t); + throw new NitriteIOException("Migration of old data has failed", t); } finally { oldStore.close(); newStore.close(); @@ -220,7 +220,7 @@ private static UserCredential credential(Compat.UserCredential value) { private static void validateOldStore(MVStore store) { if (store.hasMap(STORE_INFO)) { - throw new ValidationException("database file is corrupted"); + throw new ValidationException("Database file is corrupted"); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateSocketListener.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateSocketListener.java index 495d5b5d7..ca58f00ff 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateSocketListener.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/DataGateSocketListener.java @@ -117,7 +117,7 @@ public void sendMessage(WebSocket webSocket, M messa if (!replicatedCollection.isStopped()) { messageTemplate.postMessage(webSocket, message); } else { - throw new IllegalStateException("datagate client is not connected"); + throw new IllegalStateException("Datagate client is not connected"); } } catch (Exception e) { log.error("Failed to send message", e); diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTemplate.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTemplate.java index c7c09e083..a81c405da 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTemplate.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTemplate.java @@ -39,16 +39,16 @@ public MessageTemplate(Config config, ReplicatedCollection replicatedCollection) public void validateMessage(DataGateMessage message) { if (message == null) { - throw new ReplicationException("a null message is received for " + throw new ReplicationException("A null message is received for " + replicatedCollection.getReplicaId(), true); } else if (message.getHeader() == null) { - throw new ReplicationException("a message without header is received for " + throw new ReplicationException("A message without header is received for " + replicatedCollection.getReplicaId(), true); } else if (StringUtils.isNullOrEmpty(message.getHeader().getCollection())) { - throw new ReplicationException("a message without collection info is received for " + throw new ReplicationException("A message without collection info is received for " + replicatedCollection.getReplicaId(), true); } else if (message.getHeader().getMessageType() == null) { - throw new ReplicationException("a message without any type is received for " + throw new ReplicationException("A message without any type is received for " + replicatedCollection.getReplicaId(), true); } } @@ -66,7 +66,7 @@ public void postMessage(WebSocket webSocket, M messa log.debug("Sending message to datagate server {}", text); webSocket.send(text); } catch (JsonProcessingException e) { - throw new ReplicationException("malformed datagate message", e, true); + throw new ReplicationException("Malformed datagate message", e, true); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTransformer.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTransformer.java index 6a6618d52..3f6532222 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTransformer.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/MessageTransformer.java @@ -58,7 +58,7 @@ public DataGateMessage transform(String message) { return objectMapper.treeToValue(jsonNode, DataGateFeedAck.class); } } catch (JsonProcessingException e) { - throw new ReplicationException("failed to transform message from server", e, true); + throw new ReplicationException("Failed to transform message from server", e, true); } return null; } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java index ca045d7da..b9b8a5a06 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/Replica.java @@ -67,7 +67,7 @@ public void connect() { }, 0, config.getPollingRate(), TimeUnit.MILLISECONDS); } } else { - throw new ReplicationException("replica is not configured properly", true); + throw new ReplicationException("Replica is not configured properly", true); } } @@ -96,7 +96,7 @@ private void disconnectInternal(boolean mayInterruptIfRunning) { disconnected.compareAndSet(false, true); } } else { - throw new ReplicationException("replica is not configured properly", true); + throw new ReplicationException("Replica is not configured properly", true); } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java index f109bc14d..cfc59377d 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/ReplicaBuilder.java @@ -281,27 +281,27 @@ private int getTimeoutInMillis(TimeSpan connectTimeout) { private void validateBuilder() { if (isNullOrEmpty(remoteHost)) { - throw new ReplicationException("remote host is a mandatory field"); + throw new ReplicationException("Remote host is a mandatory field"); } if (remotePort == null) { - throw new ReplicationException("remote port is a mandatory field"); + throw new ReplicationException("Remote port is a mandatory field"); } if (isNullOrEmpty(tenant)) { - throw new ReplicationException("tenant id is a mandatory field"); + throw new ReplicationException("Tenant id is a mandatory field"); } if (db == null) { - throw new ReplicationException("database is a mandatory field"); + throw new ReplicationException("Database is a mandatory field"); } if (collection == null || isNullOrEmpty(collection.getName())) { - throw new ReplicationException("collection or repository is a mandatory field"); + throw new ReplicationException("Collection or repository is a mandatory field"); } if (isNullOrEmpty(userName)) { - throw new ReplicationException("username is a mandatory field"); + throw new ReplicationException("Username is a mandatory field"); } } } diff --git a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateClient.java b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateClient.java index 5cf40b7f4..1c207f532 100644 --- a/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateClient.java +++ b/nitrite-replication/src/main/java/org/dizitart/no2/sync/net/DataGateClient.java @@ -50,7 +50,7 @@ public void setListener(WebSocketListener listener) { createWebSocket(listener); } catch (Exception e) { log.error("Failed to connect to remote datagate server", e); - throw new ReplicationException("remote datagate connection failed", e, true); + throw new ReplicationException("Remote datagate connection failed", e, true); } } @@ -108,7 +108,7 @@ public java.security.cert.X509Certificate[] getAcceptedIssuers() { builder.hostnameVerifier((hostname, session) -> true); } catch (Exception e) { - throw new ReplicationException("error while configuring SSLSocketFactory", e, true); + throw new ReplicationException("Error while configuring SSLSocketFactory", e, true); } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java index d19f3dc8c..4dc305d9e 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/UserClient.java @@ -69,7 +69,7 @@ public static void createUser(String host, Integer port, String user) throws Exc response = call.execute(); if (response.code() != 201) { - throw new Exception("user creation failed"); + throw new Exception("User creation failed"); } } @@ -101,7 +101,7 @@ public static String getToken(String host, Integer port, String user) throws Exc return dataGateResponse.getData().getToken(); } - throw new Exception("failed to login"); + throw new Exception("Failed to login"); } private static OkHttpClient getUnsafeOkHttpClient() { diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java index 0f045fab0..a1b5381d4 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java @@ -64,7 +64,7 @@ public boolean containsKey(K k) { return rocksDB.get(columnFamilyHandle, key) != null; } catch (Exception e) { log.error("Error while querying key", e); - throw new NitriteIOException("failed to check key", e); + throw new NitriteIOException("Failed to check key", e); } } @@ -81,7 +81,7 @@ public V get(K k) { return (V) objectFormatter.decode(value, getValueType()); } catch (Exception e) { log.error("Error while querying by key", e); - throw new NitriteIOException("failed to query by key", e); + throw new NitriteIOException("Failed to query by key", e); } } @@ -134,7 +134,7 @@ public V remove(K k) { return (V) objectFormatter.decode(value, getValueType()); } catch (Exception e) { log.error("Error while removing key", e); - throw new NitriteIOException("failed to remove key", e); + throw new NitriteIOException("Failed to remove key", e); } } @@ -162,7 +162,7 @@ public void put(K k, V v) { updateLastModifiedTime(); } catch (Exception e) { log.error("Error while writing key and value for " + mapName, e); - throw new NitriteIOException("failed to write key and value", e); + throw new NitriteIOException("Failed to write key and value", e); } } @@ -204,7 +204,7 @@ public V putIfAbsent(K k, V v) { return (V) objectFormatter.decode(oldValue, getValueType()); } catch (Exception e) { log.error("Error while writing key and value", e); - throw new NitriteIOException("failed to write key and value", e); + throw new NitriteIOException("Failed to write key and value", e); } } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBReference.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBReference.java index 8b1299e08..3bdd886e2 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBReference.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBReference.java @@ -75,7 +75,7 @@ public synchronized ColumnFamilyHandle getOrCreateColumnFamily(String name) { return handle; } catch (RocksDBException e) { log.error("Error while retrieving column family handle", e); - throw new NitriteIOException("failed to obtain column family handle", e); + throw new NitriteIOException("Failed to obtain column family handle", e); } } } @@ -89,7 +89,7 @@ public void dropColumnFamily(String mapName) { columnFamilyHandleRegistry.remove(mapName); } catch (RocksDBException e) { log.error("Error while dropping column family " + mapName, e); - throw new NitriteIOException("failed to drop column family", e); + throw new NitriteIOException("Failed to drop column family", e); } } } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java index 4199fbeb7..5a5b35488 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java @@ -43,7 +43,7 @@ public void openOrCreate() { throw e; } catch (Exception e) { log.error("Error while opening database", e); - throw new NitriteIOException("failed to open database", e); + throw new NitriteIOException("Failed to open database", e); } } @@ -84,7 +84,7 @@ public void close() { eventBus.close(); } catch (Exception e) { log.error("Error while closing the database", e); - throw new NitriteIOException("failed to close database", e); + throw new NitriteIOException("Failed to close database", e); } } @@ -134,17 +134,17 @@ public void removeMap(String mapName) { public NitriteRTree openRTree(String rTreeName, Class keyType, Class valueType) { - throw new InvalidOperationException("rtree not supported on rocksdb store"); + throw new InvalidOperationException("Rtree not supported on rocksdb store"); } @Override public void closeRTree(String rTreeName) { - throw new InvalidOperationException("rtree not supported on rocksdb store"); + throw new InvalidOperationException("Rtree not supported on rocksdb store"); } @Override public void removeRTree(String mapName) { - throw new InvalidOperationException("rtree not supported on rocksdb store"); + throw new InvalidOperationException("Rtree not supported on rocksdb store"); } @Override diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStoreUtils.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStoreUtils.java index 07f20b82e..7b071c64c 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStoreUtils.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStoreUtils.java @@ -15,7 +15,7 @@ public static RocksDBReference openOrCreate(RocksDBConfig storeConfig) { if (!isNullOrEmpty(storeConfig.filePath())) { db = StoreFactory.createDBReference(storeConfig); } else { - throw new InvalidOperationException("nitrite rocksdb store does not support in-memory database"); + throw new InvalidOperationException("Nitrite rocksdb store does not support in-memory database"); } return db; } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/StoreFactory.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/StoreFactory.java index d292ff285..a7f86e175 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/StoreFactory.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/StoreFactory.java @@ -100,7 +100,7 @@ private static void createColumnFamilyDescriptors(RocksDBReference reference, Ro } } catch (RocksDBException e) { log.error("Error while listing column families", e); - throw new NitriteIOException("failed to open database", e); + throw new NitriteIOException("Failed to open database", e); } reference.setColumnFamilyDescriptors(cfDescriptors); } @@ -120,7 +120,7 @@ private static void createRocksDB(RocksDBReference reference, RocksDBConfig dbCo reference.setColumnFamilyHandleRegistry(handleMap); } catch (RocksDBException e) { log.error("Error while opening rocks database", e); - throw new NitriteIOException("failed to open database", e); + throw new NitriteIOException("Failed to open database", e); } } } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/DefaultTimeKeySerializers.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/DefaultTimeKeySerializers.java index 0fa3b24ef..097817233 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/DefaultTimeKeySerializers.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/DefaultTimeKeySerializers.java @@ -30,7 +30,7 @@ public Date readKeyInternal(Kryo kryo, String value, Class type) { try { return format.parse(value); } catch (Exception e) { - throw new NitriteIOException("failed to read java.util.Date", e); + throw new NitriteIOException("Failed to read java.util.Date", e); } } } @@ -47,7 +47,7 @@ public Timestamp readKeyInternal(Kryo kryo, String value, Class type) try { return new Timestamp(format.parse(value).getTime()); } catch (Exception e) { - throw new NitriteIOException("failed to read java.sql.Timestamp", e); + throw new NitriteIOException("Failed to read java.sql.Timestamp", e); } } } @@ -64,7 +64,7 @@ public java.sql.Date readKeyInternal(Kryo kryo, String value, Class type) { try { return new Time(format.parse(value).getTime()); } catch (Exception e) { - throw new NitriteIOException("failed to read java.sql.Time", e); + throw new NitriteIOException("Failed to read java.sql.Time", e); } } } @@ -100,7 +100,7 @@ protected Calendar readKeyInternal(Kryo kryo, String input, Class type cal.setTime(format.parse(input)); return cal; } catch (Exception e) { - throw new NitriteIOException("failed to read java.util.Date", e); + throw new NitriteIOException("Failed to read java.util.Date", e); } } } diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/KryoObjectFormatter.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/KryoObjectFormatter.java index 28893f4db..29d1a392c 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/KryoObjectFormatter.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/KryoObjectFormatter.java @@ -59,7 +59,7 @@ public byte[] encode(T object) { } return byteArrayOutputStream.toByteArray(); } catch (IOException e) { - throw new NitriteIOException("failed to close output stream", e); + throw new NitriteIOException("Failed to close output stream", e); } } @@ -80,7 +80,7 @@ public byte[] encodeKey(T object) { } return byteArrayOutputStream.toByteArray(); } catch (IOException e) { - throw new NitriteIOException("failed to close output stream", e); + throw new NitriteIOException("Failed to close output stream", e); } } @@ -110,7 +110,7 @@ public T decodeKey(byte[] bytes, Class type) { return serializer.readKey(kryo, input, type); } } catch (IOException e) { - throw new NitriteIOException("failed to close output stream", e); + throw new NitriteIOException("Failed to close output stream", e); } } @@ -133,7 +133,7 @@ private void registerInternalSerializers() { DefaultTimeKeySerializers.registerAll(this); } catch (Exception e) { log.error("Error while registering default serializers", e); - throw new NitriteIOException("failed to register default serializers", e); + throw new NitriteIOException("Failed to register default serializers", e); } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java index d22bd411d..b5d6ec9a6 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java @@ -478,4 +478,4 @@ public enum Status { PREPARING, } } -} +} \ No newline at end of file diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/TestUtil.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/TestUtil.java index 91a4101ad..5d1689ef4 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/TestUtil.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/TestUtil.java @@ -124,7 +124,7 @@ public static Document parse(String json) { return loadDocument(node); } catch (IOException e) { log.error("Error while parsing json", e); - throw new ObjectMappingException("failed to parse json " + json); + throw new ObjectMappingException("Failed to parse json " + json); } } diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndex.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndex.java index 12c6b7087..32ab1be97 100644 --- a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndex.java +++ b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndex.java @@ -113,14 +113,14 @@ public LinkedHashSet findNitriteIds(FindPlan findPlan) { if (indexScanFilter == null || indexScanFilter.getFilters() == null || indexScanFilter.getFilters().isEmpty()) { - throw new FilterException("no spatial filter found"); + throw new FilterException("No spatial filter found"); } List filters = indexScanFilter.getFilters(); ComparableFilter filter = filters.get(0); if (!(filter instanceof SpatialFilter)) { - throw new FilterException("spatial filter must be the first filter for index scan"); + throw new FilterException("Spatial filter must be the first filter for index scan"); } RecordStream keys = null; @@ -158,6 +158,6 @@ private Geometry parseGeometry(String field, Object fieldValue) { } else if (fieldValue instanceof Geometry) { return (Geometry) fieldValue; } - throw new IndexingException("field " + field + " does not contain Geometry data"); + throw new IndexingException("Field " + field + " does not contain Geometry data"); } } diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndexer.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndexer.java index 501a394d4..f39c50a1f 100644 --- a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndexer.java +++ b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndexer.java @@ -57,7 +57,7 @@ public String getIndexType() { @Override public void validateIndex(Fields fields) { if (fields.getFieldNames().size() > 1) { - throw new IndexingException("spatial index can only be created on a single field"); + throw new IndexingException("Spatial index can only be created on a single field"); } } diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java b/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java index d3ee617a2..4ebb7de8e 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java @@ -160,7 +160,7 @@ public void exportTo(Writer writer) { try { jsonExporter.exportData(); } catch (IOException | ClassNotFoundException e) { - throw new NitriteIOException("error while exporting data", e); + throw new NitriteIOException("Error while exporting data", e); } } } diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/Importer.java b/nitrite-support/src/main/java/org/dizitart/no2/support/Importer.java index cdfe968fe..20b8f52ef 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/Importer.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/Importer.java @@ -116,7 +116,7 @@ public void importFrom(Reader reader) { try { jsonImporter.importData(); } catch (IOException | ClassNotFoundException e) { - throw new NitriteIOException("error while importing data", e); + throw new NitriteIOException("Error while importing data", e); } } } diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonExporter.java b/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonExporter.java index 7b77e86e2..2ed1ff5f7 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonExporter.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonExporter.java @@ -220,7 +220,7 @@ private String writeEncodedObject(Object object) { return Hex.encodeHexString(data); } } catch (IOException e) { - throw new NitriteIOException("failed to write object", e); + throw new NitriteIOException("Failed to write object", e); } } } diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonImporter.java b/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonImporter.java index e62526647..d049fc08c 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonImporter.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonImporter.java @@ -227,7 +227,7 @@ private Object readEncodedObject(String hexString) { } } } catch (Exception e) { - throw new NitriteIOException("error while reading data", e); + throw new NitriteIOException("Error while reading data", e); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/Nitrite.java b/nitrite/src/main/java/org/dizitart/no2/Nitrite.java index a4f4be47c..a42c458a9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/Nitrite.java +++ b/nitrite/src/main/java/org/dizitart/no2/Nitrite.java @@ -254,7 +254,7 @@ default void validateCollectionName(String name) { for (String reservedName : RESERVED_NAMES) { if (name.contains(reservedName)) { - throw new ValidationException("name cannot contain " + reservedName); + throw new ValidationException("Name cannot contain " + reservedName); } } } @@ -264,7 +264,7 @@ default void validateCollectionName(String name) { */ default void checkOpened() { if (getStore() == null || getStore().isClosed()) { - throw new NitriteIOException("store is closed"); + throw new NitriteIOException("Store is closed"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java b/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java index 3de5bf873..77ed5bfca 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteConfig.java @@ -80,7 +80,7 @@ public NitriteConfig() { */ public void fieldSeparator(String separator) { if (configured) { - throw new InvalidOperationException("cannot change the separator after database" + + throw new InvalidOperationException("Cannot change the separator after database" + " initialization"); } NitriteConfig.fieldSeparator = separator; @@ -94,7 +94,7 @@ public void fieldSeparator(String separator) { */ public NitriteConfig loadModule(NitriteModule module) { if (configured) { - throw new InvalidOperationException("cannot load module after database" + + throw new InvalidOperationException("Cannot load module after database" + " initialization"); } pluginManager.loadModule(module); @@ -110,7 +110,7 @@ public NitriteConfig loadModule(NitriteModule module) { @SuppressWarnings("Java8MapApi") public NitriteConfig addMigration(Migration migration) { if (configured) { - throw new InvalidOperationException("cannot add migration steps after database" + + throw new InvalidOperationException("Cannot add migration steps after database" + " initialization"); } @@ -139,7 +139,7 @@ public NitriteConfig addMigration(Migration migration) { */ public NitriteConfig currentSchemaVersion(Integer version) { if (configured) { - throw new InvalidOperationException("cannot add schema version info after database" + + throw new InvalidOperationException("Cannot add schema version info after database" + " initialization"); } this.schemaVersion = version; @@ -152,7 +152,7 @@ public NitriteConfig currentSchemaVersion(Integer version) { */ public void autoConfigure() { if (configured) { - throw new InvalidOperationException("cannot execute autoconfigure after database" + + throw new InvalidOperationException("Cannot execute autoconfigure after database" + " initialization"); } pluginManager.findAndLoadPlugins(); @@ -170,7 +170,7 @@ public NitriteIndexer findIndexer(String indexType) { nitriteIndexer.initialize(this); return nitriteIndexer; } else { - throw new IndexingException("no indexer found for index type " + indexType); + throw new IndexingException("No indexer found for index type " + indexType); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java index 8ec4dca52..da7c0da9d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java @@ -163,7 +163,7 @@ public synchronized void close() { } catch (NitriteIOException e) { throw e; } catch (Throwable error) { - throw new NitriteIOException("error while shutting down nitrite", error); + throw new NitriteIOException("Error while shutting down nitrite", error); } } @@ -174,7 +174,7 @@ public void commit() { try { store.commit(); } catch (Exception e) { - throw new NitriteIOException("failed to commit changes", e); + throw new NitriteIOException("Failed to commit changes", e); } log.debug("Unsaved changes committed successfully."); } @@ -230,7 +230,7 @@ public void initialize(String username, String password) { store.close(); } catch (Exception ex) { log.error("Error while closing the database", ex); - throw new NitriteIOException("failed to close database", ex); + throw new NitriteIOException("Failed to close database", ex); } } throw e; @@ -241,10 +241,10 @@ public void initialize(String username, String password) { store.close(); } catch (Exception ex) { log.error("Error while closing the database"); - throw new NitriteIOException("failed to close database", ex); + throw new NitriteIOException("Failed to close database", ex); } } - throw new NitriteIOException("failed to initialize database", e); + throw new NitriteIOException("Failed to initialize database", e); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 812af5836..8953ec0fa 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -329,7 +329,7 @@ public boolean isOpen() { readLock.lock(); return nitriteStore != null && !nitriteStore.isClosed() && !isDropped; } catch (Exception e) { - throw new NitriteIOException("failed to close the database", e); + throw new NitriteIOException("Failed to close the database", e); } finally { readLock.unlock(); } @@ -442,7 +442,7 @@ private void initialize() { private void checkOpened() { if (isOpen()) return; - throw new NitriteIOException("collection is closed"); + throw new NitriteIOException("Collection is closed"); } private void validateRebuildIndex(IndexDescriptor indexDescriptor) { diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java index 4fef19539..84e51e43d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteDocument.java @@ -69,7 +69,7 @@ public Document put(String field, Object value) { // value must be serializable if (value != null && !Serializable.class.isAssignableFrom(value.getClass())) { - throw new ValidationException("type " + value.getClass().getName() + throw new ValidationException("Type " + value.getClass().getName() + " does not implement java.io.Serializable"); } @@ -118,7 +118,7 @@ public NitriteId getId() { // create a nitrite id instance from the string value return createId(id); } catch (ClassCastException cce) { - throw new InvalidIdException("invalid _id found " + get(DOC_ID)); + throw new InvalidIdException("Invalid _id found " + get(DOC_ID)); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java index 53e511fe5..302053861 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/NitriteId.java @@ -90,7 +90,7 @@ public static boolean validId(Object value) { @Override public int compareTo(NitriteId other) { if (other.idValue == null) { - throw new InvalidIdException("cannot compare with null id"); + throw new InvalidIdException("Cannot compare with null id"); } return Long.compare(Long.parseLong(idValue), Long.parseLong(other.idValue)); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java index 5ee63e87d..a229d9d89 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java @@ -61,7 +61,7 @@ void createIndex(Fields fields, String indexType) { indexDescriptor = indexManager.createIndexDescriptor(fields, indexType); } else { // if index already there throw - throw new IndexingException("index already exists on fields: " + fields); + throw new IndexingException("Index already exists on fields: " + fields); } buildIndex(indexDescriptor, false); @@ -113,7 +113,7 @@ void dropAllIndices() { try { future.get(); } catch (InterruptedException | ExecutionException e) { - throw new IndexingException("failed to drop all indices", e); + throw new IndexingException("Failed to drop all indices", e); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java index 364616c52..e9ac800d3 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/ReadOperations.java @@ -135,7 +135,8 @@ private RecordStream> findSuitableStream(FindPlan find if (findPlan.getByIdFilter() != null) { FieldBasedFilter byIdFilter = findPlan.getByIdFilter(); NitriteId nitriteId = NitriteId.createId((String) byIdFilter.getValue()); - rawStream = RecordStream.single(pair(nitriteId, getById(nitriteId))); + Document document = nitriteMap.get(nitriteId); + rawStream = RecordStream.single(pair(nitriteId, document)); } else { IndexDescriptor indexDescriptor = findPlan.getIndexDescriptor(); if (indexDescriptor != null) { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/Constants.java b/nitrite/src/main/java/org/dizitart/no2/common/Constants.java index 8037a687c..4f21e18ea 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/Constants.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/Constants.java @@ -45,7 +45,7 @@ private Constants() {} } } } catch (IOException e) { - throw new NitriteIOException("failed to load version information", e); + throw new NitriteIOException("Failed to load version information", e); } NITRITE_VERSION = v; } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/crypto/AESEncryptor.java b/nitrite/src/main/java/org/dizitart/no2/common/crypto/AESEncryptor.java index 82691b7e8..f3ec0efdd 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/crypto/AESEncryptor.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/crypto/AESEncryptor.java @@ -117,7 +117,7 @@ public String encrypt(byte[] plainText) { // string representation, base64, send this string to other for decryption. return Base64.encodeToString(cipherTextWithIvSalt, Base64.URL_SAFE); } catch (Exception e) { - throw new NitriteSecurityException("failed to encrypt data", e); + throw new NitriteSecurityException("Failed to encrypt data", e); } } @@ -153,7 +153,7 @@ public String decrypt(String encryptedText) { byte[] plainText = cipher.doFinal(cipherText); return new String(plainText, UTF_8); } catch (Exception e) { - throw new NitriteSecurityException("failed to decrypt data", e); + throw new NitriteSecurityException("Failed to decrypt data", e); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java index f333bc2f7..1b764da20 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java @@ -20,6 +20,7 @@ /** * Represents a factory for creating instances of a type. * + * @since 4.0 * @author Anindya Chatterjee */ public interface MappableFactory { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java b/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java index f2253a789..04fd3a403 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java @@ -78,7 +78,7 @@ public void findAndLoadPlugins() { loadInternalPlugins(); } catch (Exception e) { log.error("Error while loading internal plugins", e); - throw new PluginException("error while loading internal plugins", e); + throw new PluginException("Error while loading internal plugins", e); } } @@ -90,7 +90,7 @@ public void initializePlugins() { initializePlugin(nitriteStore); } else { log.error("No storage engine found. Please ensure that a storage module has been loaded properly"); - throw new NitriteIOException("no storage engine found"); + throw new NitriteIOException("No nitrite storage engine found"); } if (nitriteMapper != null) { @@ -137,7 +137,7 @@ private void populatePlugins(NitritePlugin plugin) { loadNitriteStore((NitriteStore) plugin); } else { plugin.close(); - throw new PluginException("invalid plugin loaded " + plugin); + throw new PluginException("Unknown plugin type: " + plugin.getClass().getName()); } } } @@ -145,7 +145,7 @@ private void populatePlugins(NitritePlugin plugin) { private void loadNitriteStore(NitriteStore nitriteStore) { if (this.nitriteStore != null) { nitriteStore.close(); - throw new PluginException("multiple NitriteStore found"); + throw new PluginException("Multiple nitrite store plugins found"); } this.nitriteStore = nitriteStore; } @@ -153,7 +153,7 @@ private void loadNitriteStore(NitriteStore nitriteStore) { private void loadNitriteMapper(NitriteMapper nitriteMapper) { if (this.nitriteMapper != null) { nitriteMapper.close(); - throw new PluginException("multiple NitriteMapper found"); + throw new PluginException("Multiple nitrite mapper plugins found"); } this.nitriteMapper = nitriteMapper; } @@ -161,7 +161,7 @@ private void loadNitriteMapper(NitriteMapper nitriteMapper) { private synchronized void loadIndexer(NitriteIndexer nitriteIndexer) { if (indexerMap.containsKey(nitriteIndexer.getIndexType())) { nitriteIndexer.close(); - throw new PluginException("multiple Indexer found for type " + throw new PluginException("Multiple indexer plugins found for type: " + nitriteIndexer.getIndexType()); } this.indexerMap.put(nitriteIndexer.getIndexType(), nitriteIndexer); diff --git a/nitrite/src/main/java/org/dizitart/no2/common/processors/StringFieldEncryptionProcessor.java b/nitrite/src/main/java/org/dizitart/no2/common/processors/StringFieldEncryptionProcessor.java index 2d702b60b..ccf6a8a46 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/processors/StringFieldEncryptionProcessor.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/processors/StringFieldEncryptionProcessor.java @@ -88,7 +88,7 @@ public Document processBeforeWrite(Document document) { return copy; } catch (Exception e) { log.error("Error while processing document before write", e); - throw new NitriteIOException("failed to process document before write", e); + throw new NitriteIOException("Failed to process document before write", e); } } @@ -111,7 +111,7 @@ public Document processAfterRead(Document document) { return copy; } catch (Exception e) { log.error("Error while processing document after read", e); - throw new NitriteIOException("failed to process document after read", e); + throw new NitriteIOException("Failed to process document after read", e); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedStream.java index 193039553..cd876d937 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/BoundedStream.java @@ -79,13 +79,13 @@ private static class BoundedIterator implements Iterator { */ public BoundedIterator(final Iterator iterator, final long skip, final long limit) { if (iterator == null) { - throw new ValidationException("iterator must not be null"); + throw new ValidationException("Iterator must not be null"); } if (skip < 0) { - throw new ValidationException("skip parameter must not be negative."); + throw new ValidationException("skip parameter must not be negative"); } if (limit < 0) { - throw new ValidationException("limit parameter must not be negative."); + throw new ValidationException("limit parameter must not be negative"); } this.iterator = iterator; diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentStream.java index 66d61da9e..14aff3ddf 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentStream.java @@ -85,7 +85,7 @@ private void validateProjection(Document projection) { private void validateKeyValuePair(Pair kvp) { if (kvp.getSecond() != null) { if (!(kvp.getSecond() instanceof Document)) { - throw new ValidationException("projection contains non-null values"); + throw new ValidationException("Projection contains non-null values"); } else { validateProjection((Document) kvp.getSecond()); } @@ -127,7 +127,7 @@ public Document next() { @Override public void remove() { - throw new InvalidOperationException("remove on cursor is not supported"); + throw new InvalidOperationException("Remove on cursor is not supported"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/FilteredStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/FilteredStream.java index 9c20ca7d3..058f31431 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/FilteredStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/FilteredStream.java @@ -98,7 +98,7 @@ public Pair next() { @Override public void remove() { if (nextPairSet) { - throw new InvalidOperationException("remove operation cannot be called here"); + throw new InvalidOperationException("Remove operation cannot be called here"); } iterator.remove(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/JoinedDocumentStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/JoinedDocumentStream.java index 633c37245..aeb65ea11 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/JoinedDocumentStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/JoinedDocumentStream.java @@ -118,7 +118,7 @@ public Document next() { @Override public void remove() { - throw new InvalidOperationException("remove on a cursor is not supported"); + throw new InvalidOperationException("Remove on a cursor is not supported"); } private Document join(Document localDocument, DocumentCursor foreignCursor, Lookup lookup) { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/ProjectedDocumentStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/ProjectedDocumentStream.java index adb5203de..fac78cbd4 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/ProjectedDocumentStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/ProjectedDocumentStream.java @@ -115,7 +115,7 @@ private void nextMatch() { @Override public void remove() { - throw new InvalidOperationException("remove on a cursor is not supported"); + throw new InvalidOperationException("Remove on a cursor is not supported"); } private Document project(Document original) { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/Base64.java b/nitrite/src/main/java/org/dizitart/no2/common/util/Base64.java index 4e4363e48..19989791f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/Base64.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/Base64.java @@ -92,7 +92,7 @@ public static byte[] decode(byte[] input, int offset, int len, int flags) { // (It could contain less if it contains whitespace, etc.) Decoder decoder = new Decoder(flags, new byte[len*3/4]); if (!decoder.process(input, offset, len)) { - throw new IllegalArgumentException("bad base-64"); + throw new IllegalArgumentException("Bad base-64 input"); } // Maybe we got lucky and allocated exactly enough output space. if (decoder.op == decoder.output.length) { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/Numbers.java b/nitrite/src/main/java/org/dizitart/no2/common/util/Numbers.java index afcfcc875..1d242a212 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/Numbers.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/Numbers.java @@ -65,7 +65,7 @@ public static Object castNumber(Object value, Class type) { return number.longValue(); } } - throw new ValidationException("cannot cast number of type " + value.getClass().getName() + throw new ValidationException("Cannot cast number of type " + value.getClass().getName() + " to " + type.getName()); } @@ -91,7 +91,7 @@ private static BigDecimal toBigDecimal(Number number) { try { return new BigDecimal(number.toString()); } catch (NumberFormatException e) { - throw new ValidationException("the given number (\"" + number + "\" of class " + throw new ValidationException("The given number (\"" + number + "\" of class " + number.getClass().getName() + ") does not have a parsable string representation", e); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java index e61c139a4..40c7526df 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java @@ -179,8 +179,6 @@ public static boolean deepEquals(Object o1, Object o2) { // generic check return o1.equals(o2); } - - // none of the type check passes so they are not of compatible type } @SuppressWarnings({"unchecked", "rawtypes"}) @@ -222,7 +220,7 @@ public static T newInstance(Class type, boolean createSkeleton) { return item; } catch (Throwable e) { - throw new ObjectMappingException("failed to instantiate type " + type.getName(), e); + throw new ObjectMappingException("Failed to instantiate type " + type.getName(), e); } } @@ -285,7 +283,7 @@ public static T deepCopy(T oldObj) { return (T) ois.readObject(); } } catch (IOException | ClassNotFoundException e) { - throw new NitriteIOException("error while deep copying object", e); + throw new NitriteIOException("Error while deep copying object", e); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java index df3cc8546..d2b0cbb4e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java @@ -167,27 +167,27 @@ private static void validateArrayIndexItem(Object value, String field) { } if (!(value instanceof Comparable)) { - throw new IndexingException("Each value for the iterable field " + field + " must implement Comparable"); + throw new IndexingException("Each value in the iterable field " + field + " must implement Comparable"); } } private static void validateStringArrayItem(Object value, String field) { if (!(value instanceof String) && (value instanceof Iterable || value.getClass().isArray())) { - throw new InvalidOperationException("nested array index on iterable field " + field + " is not supported"); + throw new InvalidOperationException("Nested array index on iterable field " + field + " is not supported"); } if (!(value instanceof String)) { - throw new IndexingException("cannot index on an array field containing non string values " + field); + throw new IndexingException("Cannot index on an array field containing non string values " + field); } } private static void validateArrayFilterItem(Object value, String field) { if (value instanceof Iterable || value.getClass().isArray()) { - throw new InvalidOperationException("nested array is not supported"); + throw new InvalidOperationException("Nested array is not supported"); } if (!(value instanceof Comparable)) { - throw new IndexingException("cannot filter using non comparable values " + field); + throw new IndexingException("Cannot filter using non comparable values " + field); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/ElementMatchFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/ElementMatchFilter.java index 2a80e1526..64709e8b9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/ElementMatchFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/ElementMatchFilter.java @@ -128,7 +128,7 @@ private boolean matchElement(Object item, Filter filter) { } else if (filter instanceof RegexFilter) { return matchRegex(item, filter); } else { - throw new FilterException("filter " + filter.getClass().getSimpleName() + + throw new FilterException("Filter " + filter.getClass().getSimpleName() + " is not a supported in elemMatch"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java b/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java index 6f08f9045..b816c6209 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/Filter.java @@ -66,7 +66,7 @@ static Filter byId(NitriteId nitriteId) { static Filter and(Filter... filters) { notEmpty(filters, "At least two filters must be specified"); if (filters.length < 2) { - throw new FilterException("at least two filters must be specified"); + throw new FilterException("At least two filters must be specified"); } return new AndFilter(filters); @@ -79,9 +79,9 @@ static Filter and(Filter... filters) { * @return the filter */ static Filter or(Filter... filters) { - notEmpty(filters, "at least two filters must be specified"); + notEmpty(filters, "At least two filters must be specified"); if (filters.length < 2) { - throw new FilterException("at least two filters must be specified"); + throw new FilterException("At least two filters must be specified"); } return new OrFilter(filters); diff --git a/nitrite/src/main/java/org/dizitart/no2/index/ComparableIndexer.java b/nitrite/src/main/java/org/dizitart/no2/index/ComparableIndexer.java index 0064753be..4d2d24320 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/ComparableIndexer.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/ComparableIndexer.java @@ -21,6 +21,7 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.FieldValues; import org.dizitart.no2.common.Fields; +import org.dizitart.no2.exceptions.IndexingException; import java.util.LinkedHashSet; import java.util.Map; @@ -85,6 +86,10 @@ public void dropIndex(IndexDescriptor indexDescriptor, NitriteConfig nitriteConf } private NitriteIndex findNitriteIndex(IndexDescriptor indexDescriptor, NitriteConfig nitriteConfig) { + if (indexDescriptor == null) { + throw new IndexingException("Index descriptor cannot be null"); + } + if (indexRegistry.containsKey(indexDescriptor)) { return indexRegistry.get(indexDescriptor); } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/CompoundIndex.java b/nitrite/src/main/java/org/dizitart/no2/index/CompoundIndex.java index 0a8f9744d..74d1b979a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/CompoundIndex.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/CompoundIndex.java @@ -170,26 +170,26 @@ private void removeIndexElement(NitriteMap> in } @SuppressWarnings({"rawtypes", "unchecked"}) - private void populateSubMap(NavigableMap subMap, FieldValues fieldValues, int startIndex) { - if (startIndex >= fieldValues.getValues().size()) return; + private void populateSubMap(NavigableMap subMap, FieldValues fieldValues, int depth) { + if (depth >= fieldValues.getValues().size()) return; - Pair pair = fieldValues.getValues().get(startIndex); + Pair pair = fieldValues.getValues().get(depth); Object value = pair.getSecond(); DBValue dbValue; if (value == null) { dbValue = DBNull.getInstance(); } else { if (Iterable.class.isAssignableFrom(value.getClass()) || value.getClass().isArray()) { - throw new IndexingException("compound multikey index is supported on the first field of the index only"); + throw new IndexingException("Compound multikey index is supported on the first field of the index only"); } if (!(value instanceof Comparable)) { - throw new IndexingException(value + " is not comparable"); + throw new IndexingException(value + " is not a comparable type"); } dbValue = new DBValue((Comparable) value); } - if (startIndex == fieldValues.getValues().size() - 1) { + if (depth == fieldValues.getValues().size() - 1) { // terminal field List nitriteIds = (List) subMap.get(dbValue); nitriteIds = addNitriteIds(nitriteIds, fieldValues); @@ -203,13 +203,13 @@ private void populateSubMap(NavigableMap subMap, FieldValues fieldValues, int st } subMap.put(dbValue, subMap2); - populateSubMap(subMap2, fieldValues, startIndex + 1); + populateSubMap(subMap2, fieldValues, depth + 1); } } @SuppressWarnings({"rawtypes", "unchecked"}) - private void deleteFromSubMap(NavigableMap subMap, FieldValues fieldValues, int startIndex) { - Pair pair = fieldValues.getValues().get(startIndex); + private void deleteFromSubMap(NavigableMap subMap, FieldValues fieldValues, int depth) { + Pair pair = fieldValues.getValues().get(depth); Object value = pair.getSecond(); DBValue dbValue; if (value == null) { @@ -221,7 +221,7 @@ private void deleteFromSubMap(NavigableMap subMap, FieldValues fieldValues, int dbValue = new DBValue((Comparable) value); } - if (startIndex == fieldValues.getValues().size() - 1) { + if (depth == fieldValues.getValues().size() - 1) { // terminal field List nitriteIds = (List) subMap.get(dbValue); nitriteIds = removeNitriteIds(nitriteIds, fieldValues); @@ -237,7 +237,7 @@ private void deleteFromSubMap(NavigableMap subMap, FieldValues fieldValues, int return; } - deleteFromSubMap(subMap2, fieldValues, startIndex + 1); + deleteFromSubMap(subMap2, fieldValues, depth + 1); subMap.put(dbValue, subMap2); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexScanner.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexScanner.java index 5684f8891..5f0c81763 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexScanner.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexScanner.java @@ -98,10 +98,10 @@ public LinkedHashSet doScan(List filters, Map terminalResult = indexMap.getTerminalNitriteIds(); nitriteIds.addAll(terminalResult); diff --git a/nitrite/src/main/java/org/dizitart/no2/index/NitriteIndex.java b/nitrite/src/main/java/org/dizitart/no2/index/NitriteIndex.java index 8881058e2..870cde373 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/NitriteIndex.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/NitriteIndex.java @@ -23,7 +23,8 @@ import org.dizitart.no2.exceptions.UniqueConstraintException; import org.dizitart.no2.exceptions.ValidationException; -import java.util.*; +import java.util.LinkedHashSet; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import static org.dizitart.no2.common.util.ValidationUtils.validateArrayIndexField; @@ -91,10 +92,8 @@ default void validateIndexField(Object value, String field) { validateIterableIndexField((Iterable) value, field); } else if (value.getClass().isArray()) { validateArrayIndexField(value, field); - } else { - if (!(value instanceof Comparable)) { - throw new ValidationException(value + " is not comparable"); - } + } else if (!(value instanceof Comparable)) { + throw new ValidationException("Index field " + field + " must be a comparable type"); } } @@ -113,7 +112,7 @@ default List addNitriteIds(List nitriteIds, FieldValues fi if (isUnique() && nitriteIds.size() == 1 && !nitriteIds.contains(fieldValues.getNitriteId())) { // if key is already exists for unique type, throw error - throw new UniqueConstraintException("unique key constraint violation for " + fieldValues.getFields()); + throw new UniqueConstraintException("Unique key constraint violation for " + fieldValues.getFields()); } // index always are in ascending format diff --git a/nitrite/src/main/java/org/dizitart/no2/index/NitriteTextIndexer.java b/nitrite/src/main/java/org/dizitart/no2/index/NitriteTextIndexer.java index 683cc0f73..e01b20707 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/NitriteTextIndexer.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/NitriteTextIndexer.java @@ -69,7 +69,7 @@ public String getIndexType() { @Override public void validateIndex(Fields fields) { if (fields.getFieldNames().size() > 1) { - throw new IndexingException("text index can only be created on a single field"); + throw new IndexingException("Text index can only be created on a single field"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java b/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java index 7a35435bf..cf51680db 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java @@ -97,7 +97,7 @@ public void write(FieldValues fieldValues) { addIndexElement(indexMap, fieldValues, (String) item); } } else { - throw new IndexingException("string data is expected"); + throw new IndexingException("String data is expected"); } } @@ -129,7 +129,7 @@ public void remove(FieldValues fieldValues) { removeIndexElement(indexMap, fieldValues, (String) item); } } else { - throw new IndexingException("string data is expected"); + throw new IndexingException("String data is expected"); } } @@ -152,7 +152,7 @@ public LinkedHashSet findNitriteIds(FindPlan findPlan) { textFilter.setTextTokenizer(textTokenizer); return textFilter.applyOnTextIndex(indexMap); } - throw new FilterException("invalid filter found for full-text index"); + throw new FilterException("Invalid filter found for full-text index"); } private NitriteMap> findIndexMap() { diff --git a/nitrite/src/main/java/org/dizitart/no2/index/fulltext/BaseTextTokenizer.java b/nitrite/src/main/java/org/dizitart/no2/index/fulltext/BaseTextTokenizer.java index ffedc3096..9d224176e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/fulltext/BaseTextTokenizer.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/fulltext/BaseTextTokenizer.java @@ -50,18 +50,18 @@ public Set tokenize(String text) { } /** - * Converts a `word` into all lower case and checks if it - * is a known stop word. If it is, then the `word` will be + * Converts a word into all lower case and checks if it + * is a known stop word. If it is, then the word will be * discarded and will not be considered as a valid token. * * @param word the word * @return the tokenized word in all upper case. */ protected String convertWord(String word) { - word = word.toLowerCase(); - if (stopWords().contains(word)) { + String convertedWord = word.toLowerCase(); + if (stopWords().contains(convertedWord)) { return null; } - return word; + return convertedWord; } } diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java b/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java index 61aef06d2..b205b749c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java @@ -59,10 +59,10 @@ public void doMigrate() { try { database.close(); } catch (Exception e) { - throw new NitriteIOException("failed to close the database", e); + throw new NitriteIOException("Failed to close the database", e); } - throw new MigrationException("schema version mismatch, as no migration path found from version " + throw new MigrationException("Schema version mismatch, as no migration path found from version " + storeMetadata.getSchemaVersion() + " to " + nitriteConfig.getSchemaVersion()); } @@ -82,11 +82,11 @@ private boolean isMigrationNeeded() { Integer incomingVersion = nitriteConfig.getSchemaVersion(); if (existingVersion == null) { - throw new MigrationException("corrupted database, no version information found"); + throw new MigrationException("Corrupted database, no version information found"); } if (incomingVersion == null) { - throw new MigrationException("invalid version provided"); + throw new MigrationException("Invalid version provided"); } return !existingVersion.equals(incomingVersion); } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java b/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java index 2fb209025..eb74e2c1e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java @@ -139,7 +139,7 @@ private void scanIdAnnotation() { String fieldName = StringUtils.isNullOrEmpty(id.fieldName()) ? field.getName() : id.fieldName(); indexValidator.validate(field.getType(), fieldName, nitriteMapper); if (alreadyIdFound) { - throw new NotIdentifiableException("multiple id fields found for the type"); + throw new NotIdentifiableException("Multiple id fields found for the type"); } else { alreadyIdFound = true; objectIdField = new ObjectIdField(); diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java b/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java index f18b42e03..64b2eba63 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java @@ -73,17 +73,17 @@ public void validate(Class fieldType, String field, NitriteMapper nitriteMapp } if (!embeddedFieldFound) { - throw new IndexingException("no embedded field found for object id"); + throw new IndexingException("No embedded field found for object id"); } } else { if (!Comparable.class.isAssignableFrom(fieldType)) { - throw new IndexingException("cannot index on non comparable field " + field); + throw new IndexingException("Cannot index on non comparable field " + field); } } } catch (IndexingException ie) { throw ie; } catch (Throwable e) { - throw new IndexingException("invalid type specified " + fieldType.getName() + " for indexing", e); + throw new IndexingException("Invalid type specified " + fieldType.getName() + " for indexing", e); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/MutatedObjectStream.java b/nitrite/src/main/java/org/dizitart/no2/repository/MutatedObjectStream.java index abf3845dc..ae3213ed6 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/MutatedObjectStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/MutatedObjectStream.java @@ -73,7 +73,7 @@ public T next() { @Override public void remove() { - throw new InvalidOperationException("remove on a cursor is not supported"); + throw new InvalidOperationException("Remove on a cursor is not supported"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java index 0087a6b6c..3e735083f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java @@ -75,20 +75,20 @@ public Iterator iterator() { private Document emptyDocument(NitriteMapper nitriteMapper, Class type) { if (type.isPrimitive()) { - throw new ValidationException("cannot project to primitive type"); + throw new ValidationException("Cannot project to primitive type"); } else if (type.isInterface()) { - throw new ValidationException("cannot project to interface"); + throw new ValidationException("Cannot project to interface"); } else if (type.isArray()) { - throw new ValidationException("cannot project to array"); + throw new ValidationException("Cannot project to array"); } else if (Modifier.isAbstract(type.getModifiers())) { - throw new ValidationException("cannot project to abstract type"); + throw new ValidationException("Cannot project to abstract type"); } else if (nitriteMapper.isValueType(type)) { - throw new ValidationException("cannot to project to nitrite mapper's value type"); + throw new ValidationException("Cannot to project to nitrite mapper's value type"); } Document dummyDoc = skeletonDocument(nitriteMapper, type); if (dummyDoc == null) { - throw new ValidationException("cannot project to empty type"); + throw new ValidationException("Cannot project to empty type"); } else { return dummyDoc; } @@ -114,7 +114,7 @@ public T next() { @Override public void remove() { - throw new InvalidOperationException("remove on a cursor is not supported"); + throw new InvalidOperationException("Remove on a cursor is not supported"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectIdField.java b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectIdField.java index 1061273cd..6a885eff9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectIdField.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectIdField.java @@ -90,7 +90,7 @@ public String[] getFieldNames(NitriteMapper nitriteMapper) { } if (!embeddedFieldFound) { - throw new IndexingException("no embedded field found for " + field.getName()); + throw new IndexingException("No embedded field found for " + field.getName()); } embeddedFieldNames = orderedFieldName.values().toArray(new String[0]); diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/Reflector.java b/nitrite/src/main/java/org/dizitart/no2/repository/Reflector.java index b229cfeca..2c7dba132 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/Reflector.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/Reflector.java @@ -67,14 +67,14 @@ public Field getEmbeddedField(Class startingClass, String embeddedField) String remaining = split.length == 2 ? split[1] : ""; if (isNullOrEmpty(key)) { - throw new ValidationException("invalid embedded field provided"); + throw new ValidationException("Invalid embedded field provided"); } Field field; try { field = startingClass.getDeclaredField(key); } catch (NoSuchFieldException e) { - throw new ValidationException("no such field '" + key + "' for type " + startingClass.getName(), e); + throw new ValidationException("No such field '" + key + "' for type " + startingClass.getName(), e); } if (!isNullOrEmpty(remaining) || remaining.contains(NitriteConfig.getFieldSeparator())) { @@ -123,7 +123,7 @@ public Field getField(Class type, String name) { } } if (field == null) { - throw new ValidationException("no such field '" + name + "' for type " + type.getName()); + throw new ValidationException("No such field '" + name + "' for type " + type.getName()); } return field; } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java index 6a3ef8a93..8aa9dee26 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java @@ -116,7 +116,7 @@ public void clear() { } repositoryMap.clear(); } catch (Exception e) { - throw new NitriteIOException("failed to close an object repository", e); + throw new NitriteIOException("Failed to close an object repository", e); } finally { lock.unlock(); } @@ -127,11 +127,11 @@ private ObjectRepository createRepository(NitriteConfig nitriteConfig, Cl NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); NitriteStore store = nitriteConfig.getNitriteStore(); if (nitriteMapper.isValueType(type)) { - throw new ValidationException("a value type cannot be used to create repository"); + throw new ValidationException("A value type cannot be used to create repository"); } if (store.getCollectionNames().contains(collectionName)) { - throw new ValidationException("a collection with same entity name already exists"); + throw new ValidationException("A collection with same entity name already exists"); } NitriteCollection nitriteCollection = collectionFactory.getCollection(collectionName, diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java index eb4e2703b..753af5125 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java @@ -129,19 +129,19 @@ public Document toDocument(T object, boolean update) { idField.set(object, id); document.put(objectIdField.getIdFieldName(), nitriteMapper.convert(id, Comparable.class)); } else if (!update) { - throw new InvalidIdException("auto generated id should not be set manually"); + throw new InvalidIdException("Auto generated id should not be set manually"); } } catch (IllegalAccessException iae) { - throw new InvalidIdException("auto generated id value cannot be accessed"); + throw new InvalidIdException("Auto generated id value cannot be accessed"); } } Object idValue = document.get(objectIdField.getIdFieldName()); if (idValue == null) { - throw new InvalidIdException("id cannot be null"); + throw new InvalidIdException("Id cannot be null"); } if (idValue instanceof String && isNullOrEmpty((String) idValue)) { - throw new InvalidIdException("id value cannot be empty string"); + throw new InvalidIdException("Id value cannot be empty string"); } } return document; @@ -155,7 +155,7 @@ public Document toDocument(T object, boolean update) { */ public Filter createUniqueFilter(Object object) { if (objectIdField == null) { - throw new NotIdentifiableException("update operation failed as no id value found for the object"); + throw new NotIdentifiableException("Update operation failed as no id value found for the object"); } Field idField = objectIdField.getField(); @@ -163,11 +163,11 @@ public Filter createUniqueFilter(Object object) { try { Object value = idField.get(object); if (value == null) { - throw new InvalidIdException("id value cannot be null"); + throw new InvalidIdException("Id value cannot be null"); } return objectIdField.createUniqueFilter(value, nitriteMapper); } catch (IllegalAccessException iae) { - throw new InvalidIdException("id field is not accessible"); + throw new InvalidIdException("Id field is not accessible"); } } @@ -197,10 +197,10 @@ public void removeNitriteId(Document document) { public Filter createIdFilter(I id) { if (objectIdField != null) { if (id == null) { - throw new InvalidIdException("a null id is not a valid id"); + throw new InvalidIdException("A null id is not a valid id"); } if (!isCompatibleTypes(id.getClass(), objectIdField.getField().getType())) { - throw new InvalidIdException("a value of invalid type is provided as id"); + throw new InvalidIdException("A value of invalid type is provided as id"); } return objectIdField.createUniqueFilter(id, nitriteMapper); @@ -240,7 +240,7 @@ public Cursor find(Filter filter, FindOptions findOptions, Class type) private void validateCollection() { if (collection == null) { - throw new ValidationException("repository has not been initialized properly"); + throw new ValidationException("Repository has not been initialized properly"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/UserAuthenticationService.java b/nitrite/src/main/java/org/dizitart/no2/store/UserAuthenticationService.java index 9286d86c8..a0dee76c8 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/UserAuthenticationService.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/UserAuthenticationService.java @@ -79,15 +79,15 @@ public void authenticate(String username, String password, boolean existing) { byte[] expectedHash = userCredential.getPasswordHash(); if (notExpectedPassword(password.toCharArray(), salt, expectedHash)) { - throw new NitriteSecurityException("username or password is invalid"); + throw new NitriteSecurityException("Username or password is invalid"); } } else { - throw new NitriteSecurityException("username or password is invalid"); + throw new NitriteSecurityException("Username or password is invalid"); } } } else if (existing) { if (store.hasMap(USER_MAP)) { - throw new NitriteSecurityException("username or password is invalid"); + throw new NitriteSecurityException("Username or password is invalid"); } } } @@ -113,12 +113,12 @@ public void addOrUpdatePassword(boolean update, String username, byte[] expectedHash = credential.getPasswordHash(); if (notExpectedPassword(oldPassword.asString().toCharArray(), salt, expectedHash)) { - throw new NitriteSecurityException("username or password is invalid"); + throw new NitriteSecurityException("Username or password is invalid"); } } } else { if (store.hasMap(USER_MAP)) { - throw new NitriteSecurityException("cannot add new credentials"); + throw new NitriteSecurityException("Cannot add new credentials"); } } @@ -150,7 +150,7 @@ private byte[] hash(char[] password, byte[] salt) { return skf.generateSecret(spec).getEncoded(); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { log.error("Error while hashing password", e); - throw new NitriteSecurityException("error while hashing a password: " + throw new NitriteSecurityException("Error while hashing a password: " + e.getMessage()); } finally { spec.clearPassword(); diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index 0c24ee4c5..eb76d332b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -158,14 +158,14 @@ public WriteResult update(Document document, boolean insertIfAbsent) { if (document.hasId()) { return update(createUniqueFilter(document), document, updateOptions(false)); } else { - throw new NotIdentifiableException("update operation failed as no id value found for the document"); + throw new NotIdentifiableException("Update operation failed as no id value found for the document"); } } } @Override public WriteResult remove(Document document) { - notNull(document, "a null document cannot be removed"); + notNull(document, "A null document cannot be removed"); WriteResult result; if (document.hasId()) { @@ -177,7 +177,7 @@ public WriteResult remove(Document document) { writeLock.unlock(); } } else { - throw new NotIdentifiableException("remove operation failed as no id value found for the document"); + throw new NotIdentifiableException("Remove operation failed as no id value found for the document"); } AtomicReference toRemove = new AtomicReference<>(); @@ -201,7 +201,7 @@ public WriteResult remove(Document document) { @Override public WriteResult remove(Filter filter, boolean justOne) { if ((filter == null || filter == Filter.ALL) && justOne) { - throw new InvalidOperationException("remove all cannot be combined with just once"); + throw new InvalidOperationException("Remove all cannot be combined with just once"); } WriteResult result; @@ -518,7 +518,7 @@ public boolean isOpen() { try { close(); } catch (Exception e) { - throw new NitriteIOException("failed to close the database", e); + throw new NitriteIOException("Failed to close the database", e); } return false; } else return true; @@ -634,19 +634,19 @@ public void post(CollectionEventInfo collectionEventInfo) { private void checkOpened() { if (isClosed) { - throw new TransactionException("collection is closed"); + throw new TransactionException("Collection is closed"); } if (!primary.isOpen()) { - throw new NitriteIOException("store is closed"); + throw new NitriteIOException("Store is closed"); } if (isDropped()) { - throw new NitriteIOException("collection has been dropped"); + throw new NitriteIOException("Collection has been dropped"); } if (!transactionContext.getActive().get()) { - throw new TransactionException("transaction is closed"); + throw new TransactionException("Transaction is closed"); } } @@ -658,11 +658,11 @@ private void closeEventBus() { } private void validateRebuildIndex(IndexDescriptor indexDescriptor) { - notNull(indexDescriptor, "indexEntry cannot be null"); + notNull(indexDescriptor, "indexDescriptor cannot be null"); String[] fieldNames = indexDescriptor.getIndexFields().getFieldNames().toArray(new String[0]); if (isIndexing(fieldNames)) { - throw new IndexingException("indexing on value " + indexDescriptor.getIndexFields() + " is currently running"); + throw new IndexingException("Indexing on value " + indexDescriptor.getIndexFields() + " is currently running"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java index 26af4fbc3..1b71e0a19 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java @@ -59,7 +59,7 @@ public synchronized NitriteCollection getCollection(String name) { if (nitrite.hasCollection(name)) { primary = nitrite.getCollection(name); } else { - throw new TransactionException("collection " + name + " does not exists"); + throw new TransactionException("Collection " + name + " does not exists"); } NitriteMap txMap = transactionStore.openMap(name, @@ -91,7 +91,7 @@ public synchronized ObjectRepository getRepository(Class type) { if (nitrite.hasRepository(type)) { primary = nitrite.getRepository(type); } else { - throw new TransactionException("repository of type " + type.getName() + " does not exists"); + throw new TransactionException("Repository of type " + type.getName() + " does not exists"); } NitriteMap txMap = transactionStore.openMap(name, @@ -127,7 +127,7 @@ public synchronized ObjectRepository getRepository(Class type, String if (nitrite.hasRepository(type, key)) { primary = nitrite.getRepository(type, key); } else { - throw new TransactionException("repository of type " + type.getName() + throw new TransactionException("Repository of type " + type.getName() + " and key " + key + " does not exists"); } @@ -190,7 +190,7 @@ public synchronized void commit() { } catch (Exception e) { state = State.Failed; log.error("Error while committing transaction", e); - throw new TransactionException("failed to commit transaction", e); + throw new TransactionException("Failed to commit transaction", e); } finally { undoRegistry.put(collectionName, undoLog); transactionContext.getActive().set(false); @@ -245,7 +245,7 @@ public synchronized void close() { this.transactionStore.close(); this.transactionConfig.close(); } catch (Exception e) { - throw new TransactionException("transaction failed to close", e); + throw new TransactionException("Transaction failed to close", e); } } @@ -275,7 +275,7 @@ private void prepare() { private void checkState() { if (state != State.Active) { - throw new TransactionException("transaction is not active"); + throw new TransactionException("Transaction is not active"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/Session.java b/nitrite/src/main/java/org/dizitart/no2/transaction/Session.java index 7a8c0a93c..0fd68e9d9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/Session.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/Session.java @@ -72,7 +72,7 @@ public void close() { */ public void checkState() { if (!active.get()) { - throw new TransactionException("this session is not active"); + throw new TransactionException("This session is not active"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java index 476033470..69038dca0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java @@ -28,7 +28,7 @@ public NitriteIndexer findIndexer(String indexType) { nitriteIndexer.initialize(this); return nitriteIndexer; } else { - throw new IndexingException("no indexer found for index type " + indexType); + throw new IndexingException("No indexer found for index type " + indexType); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java index 95ba2c7aa..56b77852c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java @@ -45,7 +45,7 @@ public boolean isReadOnly() { @Override public void commit() { - throw new InvalidOperationException("call commit on transaction"); + throw new InvalidOperationException("Call commit on transaction"); } @Override diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/FindPlanTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/FindPlanTest.java index b18f506d2..2d79cb372 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/FindPlanTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/FindPlanTest.java @@ -31,7 +31,8 @@ public void testConstructor() { assertTrue(actualFindPlan.getBlockingSortOrder().isEmpty()); assertEquals( "FindPlan(byIdFilter=null, indexScanFilter=null, collectionScanFilter=null, indexDescriptor=null," - + " indexScanOrder=null, blockingSortOrder=[], skip=null, limit=null, collator=null, subPlans=[])", + + " indexScanOrder=null, blockingSortOrder=[], skip=null, limit=null, distinct=false, " + + "collator=null, subPlans=[])", actualFindPlan.toString()); assertTrue(actualFindPlan.getSubPlans().isEmpty()); assertNull(actualFindPlan.getSkip()); diff --git a/nitrite/src/test/java/org/dizitart/no2/common/streams/SortedDocumentStreamTest.java b/nitrite/src/test/java/org/dizitart/no2/common/streams/SortedDocumentStreamTest.java index 7156cd267..0dbc085ae 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/streams/SortedDocumentStreamTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/streams/SortedDocumentStreamTest.java @@ -40,7 +40,8 @@ public void testConstructor() { List> blockingSortOrder = findPlan.getBlockingSortOrder(); assertTrue(blockingSortOrder instanceof java.util.ArrayList); assertEquals("FindPlan(byIdFilter=null, indexScanFilter=null, collectionScanFilter=null, indexDescriptor=null," - + " indexScanOrder=null, blockingSortOrder=[], skip=null, limit=null, collator=null, subPlans=[])", findPlan.toString()); + + " indexScanOrder=null, blockingSortOrder=[], skip=null, limit=null, distinct=false, collator=null, " + + "subPlans=[])", findPlan.toString()); assertTrue(blockingSortOrder.isEmpty()); List subPlans = findPlan.getSubPlans(); assertTrue(subPlans instanceof java.util.ArrayList); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/TestUtil.java b/nitrite/src/test/java/org/dizitart/no2/integration/TestUtil.java index c1d9e4b89..39e5a39d1 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/TestUtil.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/TestUtil.java @@ -85,7 +85,7 @@ public static Document parse(String json) { return loadDocument(node); } catch (IOException e) { log.error("Error while parsing json", e); - throw new ObjectMappingException("failed to parse json " + json); + throw new ObjectMappingException("Failed to parse json " + json); } } From 8519a753953e1767c328268103734ae7140b83d2 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 27 Jun 2022 23:58:02 +0530 Subject: [PATCH 48/78] refactoring --- .../no2/common/util/ValidationUtils.java | 4 +- .../org/dizitart/no2/index/TextIndex.java | 35 +++------- .../no2/store/AbstractNitriteStore.java | 2 +- .../org/dizitart/no2/store/NitriteStore.java | 4 +- .../org/dizitart/no2/store/StoreMetaData.java | 2 +- .../no2/store/memory/InMemoryMap.java | 42 +++++++++--- .../no2/store/memory/InMemoryRTree.java | 68 +++++++++++++------ .../no2/store/memory/InMemoryStore.java | 2 +- 8 files changed, 94 insertions(+), 65 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java index d2b0cbb4e..d7fdcaef9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java @@ -173,11 +173,11 @@ private static void validateArrayIndexItem(Object value, String field) { private static void validateStringArrayItem(Object value, String field) { if (!(value instanceof String) && (value instanceof Iterable || value.getClass().isArray())) { - throw new InvalidOperationException("Nested array index on iterable field " + field + " is not supported"); + throw new InvalidOperationException("Nested iterables are not supported"); } if (!(value instanceof String)) { - throw new IndexingException("Cannot index on an array field containing non string values " + field); + throw new IndexingException("Each value in the iterable field " + field + " must be a string"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java b/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java index cf51680db..dcb344364 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/TextIndex.java @@ -97,7 +97,8 @@ public void write(FieldValues fieldValues) { addIndexElement(indexMap, fieldValues, (String) item); } } else { - throw new IndexingException("String data is expected"); + throw new IndexingException("Index field " + firstField + + " must be a String, String[] or Iterable"); } } @@ -129,7 +130,8 @@ public void remove(FieldValues fieldValues) { removeIndexElement(indexMap, fieldValues, (String) item); } } else { - throw new IndexingException("String data is expected"); + throw new IndexingException("Index field " + firstField + + " must be a String, String[] or Iterable"); } } @@ -152,7 +154,7 @@ public LinkedHashSet findNitriteIds(FindPlan findPlan) { textFilter.setTextTokenizer(textTokenizer); return textFilter.applyOnTextIndex(indexMap); } - throw new FilterException("Invalid filter found for full-text index"); + throw new FilterException("Text index only supports a single TextFilter"); } private NitriteMap> findIndexMap() { @@ -192,29 +194,8 @@ private void removeIndexElement(NitriteMap> indexMap, FieldValue } } - private Set decompose(Object fieldValue) { - Set result = new HashSet<>(); - if (fieldValue == null) { - result.add(null); - } else if (fieldValue instanceof String) { - result.add((String) fieldValue); - } else if (fieldValue instanceof Iterable) { - Iterable iterable = (Iterable) fieldValue; - for (Object item : iterable) { - result.addAll(decompose(item)); - } - } else if (fieldValue.getClass().isArray()) { - Object[] array = convertToObjectArray(fieldValue); - for (Object item : array) { - result.addAll(decompose(item)); - } - } - - Set words = new HashSet<>(); - for (String item : result) { - words.addAll(textTokenizer.tokenize(item)); - } - - return words; + private Set decompose(String fieldValue) { + if (fieldValue == null) return new HashSet<>(); + return textTokenizer.tokenize(fieldValue); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/AbstractNitriteStore.java b/nitrite/src/main/java/org/dizitart/no2/store/AbstractNitriteStore.java index 87a4f084b..8f0c92fb5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/AbstractNitriteStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/AbstractNitriteStore.java @@ -47,7 +47,7 @@ protected AbstractNitriteStore() { } /** - * Alerts about an {@link StoreEvents} to all subscribed {@link StoreEventListener}s. + * Alerts about a {@link StoreEvents} to all subscribed {@link StoreEventListener}s. * * @param eventType the event type */ diff --git a/nitrite/src/main/java/org/dizitart/no2/store/NitriteStore.java b/nitrite/src/main/java/org/dizitart/no2/store/NitriteStore.java index 1505c400e..a3cad2326 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/NitriteStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/NitriteStore.java @@ -26,7 +26,7 @@ import java.util.Set; /** - * Represents a storage for Nitrite database. + * Represents a storage interface for Nitrite database. * * @param the type parameter * @author Anindya Chatterjee @@ -40,7 +40,7 @@ public interface NitriteStore extends NitritePlugin void openOrCreate(); /** - * Checks whether this store is closed for further modification. + * Checks whether this store is closed. * * @return true if closed; false otherwise. */ diff --git a/nitrite/src/main/java/org/dizitart/no2/store/StoreMetaData.java b/nitrite/src/main/java/org/dizitart/no2/store/StoreMetaData.java index 13181bc73..9bebc4622 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/StoreMetaData.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/StoreMetaData.java @@ -44,7 +44,7 @@ public StoreMetaData(Document document) { } /** - * Gets the database info ina document. + * Gets the database info in a document. * * @return the info */ diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java index 89590c6e9..a82ef759b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java @@ -3,6 +3,7 @@ import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.common.util.Comparables; +import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; @@ -47,11 +48,13 @@ public InMemoryMap(String mapName, NitriteStore nitriteStore) { @Override public boolean containsKey(Key key) { + checkOpened(); return backingMap.containsKey(key); } @Override public Value get(Key key) { + checkOpened(); return backingMap.get(key); } @@ -62,6 +65,7 @@ public NitriteStore getStore() { @Override public void clear() { + checkOpened(); backingMap.clear(); updateLastModifiedTime(); } @@ -73,11 +77,13 @@ public String getName() { @Override public RecordStream values() { + checkOpened(); return RecordStream.fromIterable(backingMap.values()); } @Override public Value remove(Key key) { + checkOpened(); Value value = backingMap.remove(key); updateLastModifiedTime(); return value; @@ -85,11 +91,13 @@ public Value remove(Key key) { @Override public RecordStream keys() { + checkOpened(); return RecordStream.fromIterable(backingMap.keySet()); } @Override public void put(Key key, Value value) { + checkOpened(); notNull(value, "value cannot be null"); backingMap.put(key, value); updateLastModifiedTime(); @@ -97,33 +105,38 @@ public void put(Key key, Value value) { @Override public long size() { + checkOpened(); return backingMap.size(); } @Override public Value putIfAbsent(Key key, Value value) { + checkOpened(); notNull(value, "value cannot be null"); Value v = get(key); if (v == null) { put(key, value); + updateLastModifiedTime(); } - updateLastModifiedTime(); return v; } @Override public RecordStream> entries() { + checkOpened(); return getStream(backingMap); } @Override public RecordStream> reversedEntries() { + checkOpened(); return getStream(backingMap.descendingMap()); } @Override public Key higherKey(Key key) { + checkOpened(); if (key == null) { return null; } @@ -132,6 +145,7 @@ public Key higherKey(Key key) { @Override public Key ceilingKey(Key key) { + checkOpened(); if (key == null) { return null; } @@ -140,6 +154,7 @@ public Key ceilingKey(Key key) { @Override public Key lowerKey(Key key) { + checkOpened(); if (key == null) { return null; } @@ -148,6 +163,7 @@ public Key lowerKey(Key key) { @Override public Key floorKey(Key key) { + checkOpened(); if (key == null) { return null; } @@ -156,23 +172,21 @@ public Key floorKey(Key key) { @Override public boolean isEmpty() { + checkOpened(); return backingMap.isEmpty(); } @Override public void drop() { - if (!droppedFlag.get()) { - droppedFlag.compareAndSet(false, true); - clear(); - getStore().removeMap(mapName); - } + checkOpened(); + droppedFlag.compareAndSet(false, true); + clear(); + getStore().removeMap(mapName); } @Override public void close() { - if (!closedFlag.get() && !droppedFlag.get()) { - closedFlag.compareAndSet(false, true); - } + closedFlag.compareAndSet(false, true); } private RecordStream> getStream(NavigableMap primaryMap) { @@ -191,4 +205,14 @@ public Pair next() { } }); } + + private void checkOpened() { + if (closedFlag.get()) { + throw new InvalidOperationException("Map " + mapName + " is closed"); + } + + if (droppedFlag.get()) { + throw new InvalidOperationException("Map " + mapName + " is dropped"); + } + } } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryRTree.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryRTree.java index 293951224..379e820c1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryRTree.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryRTree.java @@ -2,6 +2,7 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.RecordStream; +import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.index.BoundingBox; import org.dizitart.no2.store.NitriteRTree; @@ -10,6 +11,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; /** * The in-memory {@link NitriteRTree}. @@ -21,16 +23,21 @@ */ public class InMemoryRTree implements NitriteRTree { private final Map backingMap; + private final AtomicBoolean droppedFlag; + private final AtomicBoolean closedFlag; /** * Instantiates a new {@link InMemoryRTree}. */ public InMemoryRTree() { this.backingMap = new ConcurrentHashMap<>(); + this.closedFlag = new AtomicBoolean(false); + this.droppedFlag = new AtomicBoolean(false); } @Override public void add(Key key, NitriteId nitriteId) { + checkOpened(); if (nitriteId != null && nitriteId.getIdValue() != null) { SpatialKey spatialKey = getKey(key, Long.parseLong(nitriteId.getIdValue())); backingMap.put(spatialKey, key); @@ -39,6 +46,7 @@ public void add(Key key, NitriteId nitriteId) { @Override public void remove(Key key, NitriteId nitriteId) { + checkOpened(); if (nitriteId != null && nitriteId.getIdValue() != null) { SpatialKey spatialKey = getKey(key, Long.parseLong(nitriteId.getIdValue())); backingMap.remove(spatialKey); @@ -47,6 +55,7 @@ public void remove(Key key, NitriteId nitriteId) { @Override public RecordStream findIntersectingKeys(Key key) { + checkOpened(); SpatialKey spatialKey = getKey(key, 0L); Set set = new HashSet<>(); @@ -61,6 +70,7 @@ public RecordStream findIntersectingKeys(Key key) { @Override public RecordStream findContainedKeys(Key key) { + checkOpened(); SpatialKey spatialKey = getKey(key, 0L); Set set = new HashSet<>(); @@ -73,6 +83,35 @@ public RecordStream findContainedKeys(Key key) { return RecordStream.fromIterable(set); } + @Override + public long size() { + checkOpened(); + return backingMap.size(); + } + + @Override + public void close() { + closedFlag.compareAndSet(false, true); + } + + @Override + public void clear() { + checkOpened(); + backingMap.clear(); + } + + @Override + public void drop() { + checkOpened(); + droppedFlag.compareAndSet(false, true); + backingMap.clear(); + } + + private SpatialKey getKey(Key key, long id) { + return new SpatialKey(id, key.getMinX(), + key.getMaxX(), key.getMinY(), key.getMaxY()); + } + private boolean isOverlap(SpatialKey a, SpatialKey b) { if (a.isNull() || b.isNull()) { return false; @@ -97,29 +136,14 @@ private boolean isInside(SpatialKey a, SpatialKey b) { return true; } - @Override - public long size() { - return backingMap.size(); - } - - private SpatialKey getKey(Key key, long id) { - return new SpatialKey(id, key.getMinX(), - key.getMaxX(), key.getMinY(), key.getMaxY()); - } - - @Override - public void close() { - - } - - @Override - public void clear() { - backingMap.clear(); - } + private void checkOpened() { + if (closedFlag.get()) { + throw new InvalidOperationException("RTreeMap is closed"); + } - @Override - public void drop() { - backingMap.clear(); + if (droppedFlag.get()) { + throw new InvalidOperationException("RTreeMap is dropped"); + } } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java index 1568dee3e..63ee00eb5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java @@ -138,7 +138,7 @@ public String getStoreVersion() { private void initEventBus() { if (getStoreConfig().eventListeners() != null) { for (StoreEventListener eventListener : getStoreConfig().eventListeners()) { - eventBus.register(eventListener); + subscribe(eventListener); } } } From 83ca80dafb00911899d5e6d67c12400979895425 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 30 Jun 2022 22:01:28 +0530 Subject: [PATCH 49/78] refactoring --- .../dizitart/no2/mvstore/NitriteMVMap.java | 10 +++ .../NitriteSecurityNegativeTest.java | 2 +- .../org/dizitart/no2/rocksdb/RocksDBMap.java | 10 +++ .../NitriteSecurityNegativeTest.java | 2 +- .../org/dizitart/no2/NitriteDatabase.java | 70 +++++++------------ .../no2/collection/CollectionFactory.java | 11 ++- .../collection/DefaultNitriteCollection.java | 20 ++++-- .../collection/operation/IndexManager.java | 21 +++--- .../collection/operation/IndexOperations.java | 1 + .../org/dizitart/no2/store/NitriteMap.java | 52 +++++++++----- .../no2/store/UserAuthenticationService.java | 14 ++-- .../no2/store/memory/InMemoryMap.java | 34 ++++++--- .../no2/store/memory/InMemoryStore.java | 17 +++-- .../no2/transaction/TransactionStore.java | 7 +- .../no2/transaction/TransactionalMap.java | 25 ++++--- .../store/UserAuthenticationServiceTest.java | 46 ++---------- .../no2/store/memory/InMemoryMapTest.java | 13 ++-- .../no2/transaction/TransactionalMapTest.java | 2 +- 18 files changed, 188 insertions(+), 169 deletions(-) diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVMap.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVMap.java index 08703bccc..23901cd2c 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVMap.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVMap.java @@ -195,6 +195,11 @@ public void drop() { } } + @Override + public boolean isDropped() { + return droppedFlag.get(); + } + @Override public void close() { if (!closedFlag.get() && !droppedFlag.get()) { @@ -202,4 +207,9 @@ public void close() { nitriteStore.closeMap(getName()); } } + + @Override + public boolean isClosed() { + return closedFlag.get(); + } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteSecurityNegativeTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteSecurityNegativeTest.java index 222038b62..d4bef6baf 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteSecurityNegativeTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteSecurityNegativeTest.java @@ -55,7 +55,7 @@ public void testOpenSecuredWithoutCredential() { assertEquals(dbCollection.find().size(), 1); } - @Test(expected = NitriteException.class) + @Test public void testOpenUnsecuredWithCredential() { db = createDb(fileName); NitriteCollection dbCollection = db.getCollection("test"); diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java index a1b5381d4..fc403be9c 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBMap.java @@ -344,6 +344,11 @@ public void drop() { } } + @Override + public boolean isDropped() { + return droppedFlag.get(); + } + @Override public void close() { if (!closedFlag.get() && !droppedFlag.get()) { @@ -352,6 +357,11 @@ public void close() { } } + @Override + public boolean isClosed() { + return closedFlag.get(); + } + private void initialize() { this.size = new AtomicLong(0); // just initialized this.closedFlag = new AtomicBoolean(false); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteSecurityNegativeTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteSecurityNegativeTest.java index 222038b62..d4bef6baf 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteSecurityNegativeTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteSecurityNegativeTest.java @@ -55,7 +55,7 @@ public void testOpenSecuredWithoutCredential() { assertEquals(dbCollection.find().size(), 1); } - @Test(expected = NitriteException.class) + @Test public void testOpenUnsecuredWithCredential() { db = createDb(fileName); NitriteCollection dbCollection = db.getCollection("test"); diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java index da7c0da9d..1a78b88cf 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java @@ -27,13 +27,12 @@ import org.dizitart.no2.migration.MigrationManager; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.RepositoryFactory; -import org.dizitart.no2.store.StoreMetaData; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; +import org.dizitart.no2.store.StoreMetaData; import org.dizitart.no2.store.UserAuthenticationService; import org.dizitart.no2.transaction.Session; -import java.io.File; import java.util.Map; import java.util.Set; @@ -155,7 +154,7 @@ public synchronized void close() { storeInfo.close(); if (nitriteConfig != null) { - // close all plugins + // close all plugins and store nitriteConfig.close(); } @@ -163,7 +162,7 @@ public synchronized void close() { } catch (NitriteIOException e) { throw e; } catch (Throwable error) { - throw new NitriteIOException("Error while shutting down nitrite", error); + throw new NitriteIOException("Error occurred while closing the database", error); } } @@ -174,9 +173,9 @@ public void commit() { try { store.commit(); } catch (Exception e) { - throw new NitriteIOException("Failed to commit changes", e); + throw new NitriteIOException("Error occurred while committing the database", e); } - log.debug("Unsaved changes committed successfully."); + log.debug("Unsaved changes has been committed successfully."); } } @@ -195,25 +194,11 @@ public Session createSession() { return new Session(this, lockService); } - private void validateUserCredentials(String username, String password) { - if (isNullOrEmpty(username) && isNullOrEmpty(password)) { - return; - } - - if (isNullOrEmpty(username)) { - throw new NitriteSecurityException("username cannot be empty"); - } - if (isNullOrEmpty(password)) { - throw new NitriteSecurityException("password cannot be empty"); - } - } - public void initialize(String username, String password) { validateUserCredentials(username, password); try { nitriteConfig.initialize(); store = nitriteConfig.getNitriteStore(); - boolean isExisting = isExisting(); store.openOrCreate(); prepareDatabaseMetaData(); @@ -222,29 +207,35 @@ public void initialize(String username, String password) { migrationManager.doMigrate(); UserAuthenticationService userAuthenticationService = new UserAuthenticationService(store); - userAuthenticationService.authenticate(username, password, isExisting); - } catch (NitriteException e) { - log.error("Error while initializing the database", e); - if (store != null && !store.isClosed()) { - try { - store.close(); - } catch (Exception ex) { - log.error("Error while closing the database", ex); - throw new NitriteIOException("Failed to close database", ex); - } - } - throw e; + userAuthenticationService.authenticate(username, password); } catch (Exception e) { - log.error("Error while initializing the database", e); + log.error("Error occurred while initializing the database", e); if (store != null && !store.isClosed()) { try { store.close(); } catch (Exception ex) { - log.error("Error while closing the database"); + log.error("Error occurred while closing the database"); throw new NitriteIOException("Failed to close database", ex); } } - throw new NitriteIOException("Failed to initialize database", e); + if (e instanceof NitriteException) { + throw e; + } else { + throw new NitriteIOException("Failed to initialize database", e); + } + } + } + + private void validateUserCredentials(String username, String password) { + if (isNullOrEmpty(username) && isNullOrEmpty(password)) { + return; + } + + if (isNullOrEmpty(username)) { + throw new NitriteSecurityException("Username is required"); + } + if (isNullOrEmpty(password)) { + throw new NitriteSecurityException("Password is required"); } } @@ -261,13 +252,4 @@ private void prepareDatabaseMetaData() { storeInfo.put(STORE_INFO, storeMetadata.getInfo()); } } - - private boolean isExisting() { - String filePath = store.getStoreConfig().filePath(); - if (!isNullOrEmpty(filePath)) { - File dbFile = new File(filePath); - return dbFile.exists(); - } - return false; - } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java index d493524ca..d9fa5bf47 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java @@ -83,25 +83,24 @@ public NitriteCollection getCollection(String name, NitriteConfig nitriteConfig, private NitriteCollection createCollection(String name, NitriteConfig nitriteConfig, boolean writeCatalog) { NitriteStore store = nitriteConfig.getNitriteStore(); - NitriteMap nitriteMap = store.openMap(name, NitriteId.class, Document.class); - NitriteCollection collection = new DefaultNitriteCollection(name, nitriteMap, nitriteConfig, lockService); if (writeCatalog) { // ignore repository request if (store.getRepositoryRegistry().contains(name)) { - nitriteMap.close(); - collection.close(); throw new ValidationException("A repository with same name already exists"); } for (Set set : store.getKeyedRepositoryRegistry().values()) { if (set.contains(name)) { - nitriteMap.close(); - collection.close(); throw new ValidationException("A keyed repository with same name already exists"); } } + } + NitriteMap nitriteMap = store.openMap(name, NitriteId.class, Document.class); + NitriteCollection collection = new DefaultNitriteCollection(name, nitriteMap, nitriteConfig, lockService); + + if (writeCatalog) { collectionMap.put(name, collection); StoreCatalog storeCatalog = store.getCatalog(); storeCatalog.writeCollectionEntry(name); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 8953ec0fa..2437f7d80 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -303,11 +303,11 @@ public void drop() { checkOpened(); if (collectionOperations != null) { - // close collection and indexes - collectionOperations.close(); - // drop collection and indexes collectionOperations.dropCollection(); + + // close collection and indexes +// collectionOperations.close(); } // set all reference to null @@ -324,12 +324,22 @@ public void drop() { isDropped = true; } + public boolean isDropped() { + try { + readLock.lock(); + return isDropped || nitriteMap == null || nitriteMap.isDropped(); + } finally { + readLock.unlock(); + } + } + public boolean isOpen() { try { readLock.lock(); - return nitriteStore != null && !nitriteStore.isClosed() && !isDropped; + return nitriteStore != null && !nitriteStore.isClosed() + && !isDropped && !nitriteMap.isClosed() && !nitriteMap.isDropped(); } catch (Exception e) { - throw new NitriteIOException("Failed to close the database", e); + throw new NitriteIOException("Failed to check the collection state", e); } finally { readLock.unlock(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java index 2464048e2..170665b54 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java @@ -103,17 +103,19 @@ public IndexDescriptor findExactIndexDescriptor(Fields fields) { @Override public void close() { // close all index maps - Iterable indexMetas = indexMetaMap.values(); - for (IndexMeta indexMeta : indexMetas) { - if (indexMeta != null && indexMeta.getIndexDescriptor() != null) { - String indexMapName = indexMeta.getIndexMap(); - NitriteMap indexMap = nitriteStore.openMap(indexMapName, Object.class, Object.class); - indexMap.close(); + if (!indexMetaMap.isClosed() && !indexMetaMap.isDropped()) { + Iterable indexMetas = indexMetaMap.values(); + for (IndexMeta indexMeta : indexMetas) { + if (indexMeta != null && indexMeta.getIndexDescriptor() != null) { + String indexMapName = indexMeta.getIndexMap(); + NitriteMap indexMap = nitriteStore.openMap(indexMapName, Object.class, Object.class); + indexMap.close(); + } } - } - // close index meta - indexMetaMap.close(); + // close index meta + indexMetaMap.close(); + } } /** @@ -181,7 +183,6 @@ void dropIndexDescriptor(Fields fields) { } void dropIndexMeta() { - indexMetaMap.clear(); indexMetaMap.drop(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java index a229d9d89..0c2dc7e20 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java @@ -119,6 +119,7 @@ void dropAllIndices() { indexManager.dropIndexMeta(); indexBuildTracker.clear(); + indexManager.close(); // recreate index manager to discard old native resources // special measure for RocksDB adapter diff --git a/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java b/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java index d77274bed..f55a3127b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/NitriteMap.java @@ -56,6 +56,13 @@ public interface NitriteMap extends AttributesAware, AutoCloseable { */ void clear(); + /** + * Indicates if the map already is closed. + * + * @return the boolean + */ + boolean isClosed(); + /** * Closes this {@link NitriteMap}. * */ @@ -185,13 +192,22 @@ public interface NitriteMap extends AttributesAware, AutoCloseable { */ void drop(); + /** + * Indicates if this map is dropped already. + * + * @return the boolean result + */ + boolean isDropped(); + /** * Gets the attributes of this map. * */ default Attributes getAttributes() { - NitriteMap metaMap = getStore().openMap(META_MAP_NAME, String.class, Attributes.class); - if (metaMap != null && !getName().contentEquals(META_MAP_NAME)) { - return metaMap.get(getName()); + if (!isDropped()) { + NitriteMap metaMap = getStore().openMap(META_MAP_NAME, String.class, Attributes.class); + if (metaMap != null && !getName().contentEquals(META_MAP_NAME)) { + return metaMap.get(getName()); + } } return null; } @@ -200,9 +216,11 @@ default Attributes getAttributes() { * Sets the attributes for this map. * */ default void setAttributes(Attributes attributes) { - NitriteMap metaMap = getStore().openMap(META_MAP_NAME, String.class, Attributes.class); - if (metaMap != null && !getName().contentEquals(META_MAP_NAME)) { - metaMap.put(getName(), attributes); + if (!isDropped()) { + NitriteMap metaMap = getStore().openMap(META_MAP_NAME, String.class, Attributes.class); + if (metaMap != null && !getName().contentEquals(META_MAP_NAME)) { + metaMap.put(getName(), attributes); + } } } @@ -210,17 +228,19 @@ default void setAttributes(Attributes attributes) { * Update last modified time of the map. */ default void updateLastModifiedTime() { - if (isNullOrEmpty(getName()) - || META_MAP_NAME.equals(getName())) return; - - NitriteMap metaMap = getStore().openMap(META_MAP_NAME, String.class, Attributes.class); - if (metaMap != null) { - Attributes attributes = metaMap.get(getName()); - if (attributes == null) { - attributes = new Attributes(getName()); - metaMap.put(getName(), attributes); + if (!isDropped()) { + if (isNullOrEmpty(getName()) + || META_MAP_NAME.equals(getName())) return; + + NitriteMap metaMap = getStore().openMap(META_MAP_NAME, String.class, Attributes.class); + if (metaMap != null) { + Attributes attributes = metaMap.get(getName()); + if (attributes == null) { + attributes = new Attributes(getName()); + metaMap.put(getName(), attributes); + } + attributes.set(Attributes.LAST_MODIFIED_TIME, Long.toString(System.currentTimeMillis())); } - attributes.set(Attributes.LAST_MODIFIED_TIME, Long.toString(System.currentTimeMillis())); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/UserAuthenticationService.java b/nitrite/src/main/java/org/dizitart/no2/store/UserAuthenticationService.java index a0dee76c8..5285c3485 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/UserAuthenticationService.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/UserAuthenticationService.java @@ -57,9 +57,9 @@ public UserAuthenticationService(NitriteStore store) { * * @param username the username * @param password the password - * @param existing indicates if authentication data is already existing */ - public void authenticate(String username, String password, boolean existing) { + public void authenticate(String username, String password) { + boolean existing = store.hasMap(USER_MAP); if (!isNullOrEmpty(password) && !isNullOrEmpty(username)) { if (!existing) { byte[] salt = getNextSalt(); @@ -86,9 +86,7 @@ public void authenticate(String username, String password, boolean existing) { } } } else if (existing) { - if (store.hasMap(USER_MAP)) { - throw new NitriteSecurityException("Username or password is invalid"); - } + throw new NitriteSecurityException("Username or password is invalid"); } } @@ -100,8 +98,7 @@ public void authenticate(String username, String password, boolean existing) { * @param oldPassword the old password * @param newPassword the new password */ - public void addOrUpdatePassword(boolean update, String username, - SecureString oldPassword, SecureString newPassword) { + public void addOrUpdatePassword(boolean update, String username, SecureString oldPassword, SecureString newPassword) { NitriteMap userMap = null; if (update) { @@ -150,8 +147,7 @@ private byte[] hash(char[] password, byte[] salt) { return skf.generateSecret(spec).getEncoded(); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { log.error("Error while hashing password", e); - throw new NitriteSecurityException("Error while hashing a password: " - + e.getMessage()); + throw new NitriteSecurityException("Error while hashing a password: " + e.getMessage()); } finally { spec.clearPassword(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java index a82ef759b..f3323885d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryMap.java @@ -63,13 +63,6 @@ public NitriteStore getStore() { return nitriteStore; } - @Override - public void clear() { - checkOpened(); - backingMap.clear(); - updateLastModifiedTime(); - } - @Override public String getName() { return mapName; @@ -178,10 +171,17 @@ public boolean isEmpty() { @Override public void drop() { - checkOpened(); - droppedFlag.compareAndSet(false, true); - clear(); - getStore().removeMap(mapName); + if (!droppedFlag.get()) { + backingMap.clear(); + getStore().removeMap(mapName); + droppedFlag.compareAndSet(false, true); + closedFlag.compareAndSet(false, true); + } + } + + @Override + public boolean isDropped() { + return droppedFlag.get(); } @Override @@ -189,6 +189,18 @@ public void close() { closedFlag.compareAndSet(false, true); } + @Override + public boolean isClosed() { + return closedFlag.get(); + } + + @Override + public void clear() { + checkOpened(); + backingMap.clear(); + updateLastModifiedTime(); + } + private RecordStream> getStream(NavigableMap primaryMap) { return RecordStream.fromIterable(() -> new Iterator>() { private final Iterator> entryIterator = diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java index 63ee00eb5..992fd0127 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryStore.java @@ -85,7 +85,12 @@ public boolean hasMap(String mapName) { @SuppressWarnings("unchecked") public NitriteMap openMap(String mapName, Class keyType, Class valueType) { if (nitriteMapRegistry.containsKey(mapName)) { - return (InMemoryMap) nitriteMapRegistry.get(mapName); + NitriteMap nitriteMap = (NitriteMap) nitriteMapRegistry.get(mapName); + if (nitriteMap.isClosed()) { + nitriteMapRegistry.remove(mapName); + } else { + return nitriteMap; + } } NitriteMap nitriteMap = new InMemoryMap<>(mapName, this); @@ -97,19 +102,23 @@ public NitriteMap openMap(String mapName, Class keyT @Override public void closeMap(String mapName) { // nothing to close as it is volatile map, moreover, - // removing it form registry means loosing the map + // removing it from registry means losing the map } @Override public void closeRTree(String rTreeName) { // nothing to close as it is volatile map, moreover, - // removing it form registry means loosing the map + // removing it from registry means losing the map } @Override public void removeMap(String mapName) { if (nitriteMapRegistry.containsKey(mapName)) { - nitriteMapRegistry.get(mapName).clear(); + NitriteMap nitriteMap = nitriteMapRegistry.get(mapName); + if (!nitriteMap.isClosed() && !nitriteMap.isDropped()) { + nitriteMap.clear(); + nitriteMap.close(); + } nitriteMapRegistry.remove(mapName); getCatalog().remove(mapName); } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java index 56b77852c..efc94dd8a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java @@ -79,7 +79,12 @@ public boolean hasMap(String mapName) { @SuppressWarnings("unchecked") public NitriteMap openMap(String mapName, Class keyType, Class valueType) { if (mapRegistry.containsKey(mapName)) { - return (NitriteMap) mapRegistry.get(mapName); + NitriteMap nitriteMap = (NitriteMap) mapRegistry.get(mapName); + if (nitriteMap.isClosed()) { + mapRegistry.remove(mapName); + } else { + return (NitriteMap) mapRegistry.get(mapName); + } } NitriteMap primaryMap = null; diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java index b3053c000..6ac60382d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java @@ -284,22 +284,29 @@ public boolean isEmpty() { @Override public void drop() { if (!droppedFlag.get()) { - droppedFlag.compareAndSet(false, true); - closedFlag.compareAndSet(false, true); - - cleared = true; backingMap.clear(); tombstones.clear(); + cleared = true; + droppedFlag.compareAndSet(false, true); } } + @Override + public boolean isDropped() { + return droppedFlag.get(); + } + @Override public void close() { - if (!closedFlag.get() && !droppedFlag.get()) { - closedFlag.compareAndSet(false, true); - backingMap.clear(); - tombstones.clear(); - } + backingMap.clear(); + tombstones.clear(); + cleared = true; + closedFlag.compareAndSet(false, true); + } + + @Override + public boolean isClosed() { + return closedFlag.get(); } private RecordStream> getStream(RecordStream> primaryStream, diff --git a/nitrite/src/test/java/org/dizitart/no2/store/UserAuthenticationServiceTest.java b/nitrite/src/test/java/org/dizitart/no2/store/UserAuthenticationServiceTest.java index aa32c4958..e746d73ca 100644 --- a/nitrite/src/test/java/org/dizitart/no2/store/UserAuthenticationServiceTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/store/UserAuthenticationServiceTest.java @@ -17,71 +17,33 @@ package org.dizitart.no2.store; -import org.dizitart.no2.exceptions.NitriteSecurityException; import org.dizitart.no2.store.memory.InMemoryStore; import org.junit.Test; -import static org.junit.Assert.assertThrows; - public class UserAuthenticationServiceTest { @Test public void testConstructor() { - // TODO: This test is incomplete. - // Reason: Nothing to assert: the constructed class does not have observers (e.g. getters or public fields). - // Add observers (e.g. getters or public fields) to the class. - // See https://diff.blue/R002 - new UserAuthenticationService(null); } - @Test - public void testAuthenticate() { - assertThrows(NitriteSecurityException.class, - () -> (new UserAuthenticationService(new InMemoryStore())).authenticate("janedoe", "iloveyou", true)); - } - @Test public void testAuthenticate2() { - // TODO: This test is incomplete. - // Reason: No meaningful assertions found. - // To help Diffblue Cover to find assertions, please add getters to the - // class under test that return fields written by the method under test. - // See https://diff.blue/R004 - - (new UserAuthenticationService(new InMemoryStore())).authenticate("", "iloveyou", true); + (new UserAuthenticationService(new InMemoryStore())).authenticate("", "iloveyou"); } @Test public void testAuthenticate3() { - // TODO: This test is incomplete. - // Reason: No meaningful assertions found. - // To help Diffblue Cover to find assertions, please add getters to the - // class under test that return fields written by the method under test. - // See https://diff.blue/R004 - - (new UserAuthenticationService(new InMemoryStore())).authenticate("janedoe", "", true); + (new UserAuthenticationService(new InMemoryStore())).authenticate("janedoe", ""); } @Test public void testAuthenticate4() { - // TODO: This test is incomplete. - // Reason: No meaningful assertions found. - // To help Diffblue Cover to find assertions, please add getters to the - // class under test that return fields written by the method under test. - // See https://diff.blue/R004 - - (new UserAuthenticationService(new InMemoryStore())).authenticate("janedoe", "iloveyou", false); + (new UserAuthenticationService(new InMemoryStore())).authenticate("janedoe", "iloveyou"); } @Test public void testAuthenticate5() { - // TODO: This test is incomplete. - // Reason: No meaningful assertions found. - // To help Diffblue Cover to find assertions, please add getters to the - // class under test that return fields written by the method under test. - // See https://diff.blue/R004 - - (new UserAuthenticationService(new InMemoryStore())).authenticate("", "iloveyou", false); + (new UserAuthenticationService(new InMemoryStore())).authenticate("", "iloveyou"); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/store/memory/InMemoryMapTest.java b/nitrite/src/test/java/org/dizitart/no2/store/memory/InMemoryMapTest.java index 4bf390ec8..1cf618589 100644 --- a/nitrite/src/test/java/org/dizitart/no2/store/memory/InMemoryMapTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/store/memory/InMemoryMapTest.java @@ -1,5 +1,6 @@ package org.dizitart.no2.store.memory; +import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; import org.junit.Test; @@ -333,7 +334,7 @@ public void testIsEmpty2() { assertFalse(inMemoryMap.isEmpty()); } - @Test + @Test(expected = InvalidOperationException.class) public void testDrop() { InMemoryMap inMemoryMap = new InMemoryMap<>("Map Name", new InMemoryStore()); inMemoryMap.put("Key", "Value"); @@ -342,7 +343,7 @@ public void testDrop() { assertEquals(4, inMemoryMap.getAttributes().getAttributes().size()); } - @Test + @Test(expected = InvalidOperationException.class) public void testDrop2() { InMemoryMap inMemoryMap = new InMemoryMap<>("42", new InMemoryStore()); inMemoryMap.put("Key", "Value"); @@ -351,7 +352,7 @@ public void testDrop2() { assertEquals(4, inMemoryMap.getAttributes().getAttributes().size()); } - @Test + @Test(expected = InvalidOperationException.class) public void testDrop3() { InMemoryMap inMemoryMap = new InMemoryMap<>("Map Name", new InMemoryStore()); inMemoryMap.drop(); @@ -361,12 +362,6 @@ public void testDrop3() { @Test public void testClose() { - // TODO: This test is incomplete. - // Reason: No meaningful assertions found. - // To help Diffblue Cover to find assertions, please add getters to the - // class under test that return fields written by the method under test. - // See https://diff.blue/R004 - (new InMemoryMap<>("Map Name", null)).close(); } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalMapTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalMapTest.java index 4687b5e6a..30ca37e64 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalMapTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionalMapTest.java @@ -435,7 +435,7 @@ public void testDrop() { transactionalMap.drop(); assertTrue(transactionalMap.entries().toList().isEmpty()); assertEquals(0L, transactionalMap.size()); - assertEquals(4, transactionalMap.getAttributes().getAttributes().size()); + assertNull(transactionalMap.getAttributes()); } @Test From a920266e8b33bec8f575dc0c17bd644a8cdff1e0 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 5 Jul 2022 18:57:59 +0530 Subject: [PATCH 50/78] refactoring --- README.md | 4 +-- .../integration/migrate/MigrationTest.java | 36 +++++++++---------- .../RepositoryModificationTest.java | 13 +++---- .../no2/integration/event/EventTest.java | 3 +- .../integration/migration/MigrationTest.java | 36 +++++++++---------- .../RepositoryModificationTest.java | 13 +++---- .../no2/integration/event/EventTest.java | 9 +++-- .../integration/migrate/MigrationTest.java | 36 +++++++++---------- .../RepositoryModificationTest.java | 13 +++---- .../no2/collection/DocumentCursor.java | 4 +-- .../no2/common/PersistentCollection.java | 32 ----------------- .../streams}/MutatedObjectStream.java | 11 +++--- .../no2/migration/CollectionInstruction.java | 1 + .../no2/migration/DatabaseInstruction.java | 6 ++-- ...{Instructions.java => InstructionSet.java} | 2 +- .../org/dizitart/no2/migration/Migration.java | 6 ++-- .../no2/migration/MigrationManager.java | 3 +- ...ctions.java => NitriteInstructionSet.java} | 6 ++-- .../no2/migration/RepositoryInstruction.java | 2 ++ .../org/dizitart/no2/repository/Cursor.java | 10 +++--- .../repository/DefaultObjectRepository.java | 11 +++--- .../dizitart/no2/repository/ObjectCursor.java | 1 + .../no2/repository/ObjectRepository.java | 20 +++++------ .../dizitart/no2/repository/Reflector.java | 2 +- .../no2/repository/RepositoryFactory.java | 4 +-- .../DefaultTransactionalRepository.java | 12 ++++--- .../dizitart/no2/common/event/EventTest.java | 3 +- .../RepositoryModificationTest.java | 13 +++---- ...st.java => NitriteInstructionSetTest.java} | 4 +-- .../DefaultObjectRepositoryTest.java | 11 +++--- .../no2/repository/ObjectCursorTest.java | 1 + .../DefaultTransactionalRepositoryTest.java | 7 ++-- 32 files changed, 157 insertions(+), 178 deletions(-) rename nitrite/src/main/java/org/dizitart/no2/{repository => common/streams}/MutatedObjectStream.java (89%) rename nitrite/src/main/java/org/dizitart/no2/migration/{Instructions.java => InstructionSet.java} (98%) rename nitrite/src/main/java/org/dizitart/no2/migration/{NitriteInstructions.java => NitriteInstructionSet.java} (88%) rename nitrite/src/test/java/org/dizitart/no2/migration/{NitriteInstructionsTest.java => NitriteInstructionSetTest.java} (85%) diff --git a/README.md b/README.md index bde2283a3..952c53ebc 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,7 @@ try (Session session = db.createSession()) { Migration migration1 = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instruction instructions) { + public void migrate(InstructionSet instructions) { instructions.forDatabase() // make a non-secure db to secure db .addPassword("test-user", "test-password"); @@ -288,7 +288,7 @@ Migration migration1 = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { Migration migration2 = new Migration(2, 3) { @Override - public void migrate(Instruction instructions) { + public void migrate(InstructionSet instructions) { instructions.forCollection("test") .addField("fullName", "Dummy Name"); } diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java index f44092187..8ce2d0b2b 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java @@ -26,7 +26,7 @@ import org.dizitart.no2.common.mapper.JacksonMapperModule; import org.dizitart.no2.exceptions.MigrationException; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.migration.Instructions; +import org.dizitart.no2.migration.InstructionSet; import org.dizitart.no2.migration.Migration; import org.dizitart.no2.migration.TypeConverter; import org.dizitart.no2.mvstore.MVStoreModule; @@ -96,7 +96,7 @@ public void testRepositoryMigrate() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forDatabase() .addPassword("test-user", "test-password"); @@ -153,7 +153,7 @@ public void testCollectionMigrate() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forDatabase() .addPassword("test-user", "test-password"); @@ -184,11 +184,11 @@ public void migrate(Instructions instruction) { migration = new Migration(2, 3) { @Override - public void migrate(Instructions instructions) { - instructions.forDatabase() + public void migrate(InstructionSet instructionSet) { + instructionSet.forDatabase() .changePassword("test-user", "test-password", "password"); - instructions.forCollection("testCollectionMigrate") + instructionSet.forCollection("testCollectionMigrate") .dropIndex("firstName") .deleteField("bloodGroup") .addField("name", document -> faker.name().fullName()) @@ -235,7 +235,7 @@ public void testOpenWithoutSchemaVersion() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testOpenWithoutSchemaVersion") @@ -287,7 +287,7 @@ public void testDescendingSchema() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testDescendingSchema") @@ -313,9 +313,9 @@ public void migrate(Instructions instruction) { migration = new Migration(2, Constants.INITIAL_SCHEMA_VERSION) { @Override - public void migrate(Instructions instructions) { + public void migrate(InstructionSet instructionSet) { - instructions.forCollection("testDescendingSchema") + instructionSet.forCollection("testDescendingSchema") .rename("test"); } }; @@ -351,7 +351,7 @@ public void testMigrationWithoutVersion() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testMigrationWithoutVersion") @@ -392,7 +392,7 @@ public void testWrongSchemaVersionNoMigration() { Migration migration = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testWrongSchemaVersionNoMigration") .rename("test") @@ -421,8 +421,8 @@ public void migrate(Instructions instruction) { migration = new Migration(2, 3) { @Override - public void migrate(Instructions instructions) { - instructions.forCollection("test") + public void migrate(InstructionSet instructionSet) { + instructionSet.forCollection("test") .rename("testWrongSchemaVersionNoMigration"); } }; @@ -461,7 +461,7 @@ public void testReOpenAfterMigration() { Migration migration = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testReOpenAfterMigration") .rename("test") @@ -529,7 +529,7 @@ public void testMultipleMigrations() { Migration migration1 = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testMultipleMigrations") .rename("test"); @@ -538,7 +538,7 @@ public void migrate(Instructions instruction) { Migration migration2 = new Migration(2, 3) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .addField("fullName", "Dummy Name"); } @@ -563,7 +563,7 @@ public void migrate(Instructions instruction) { Migration migration3 = new Migration(3, 4) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .addField("age", 10); } diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java index 26c89c680..8fa0d1c98 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java @@ -39,6 +39,7 @@ import static org.awaitility.Awaitility.await; import static org.dizitart.no2.collection.Document.createDocument; +import static org.dizitart.no2.collection.UpdateOptions.updateOptions; import static org.dizitart.no2.filters.FluentFilter.where; import static org.junit.Assert.*; @@ -208,7 +209,7 @@ public void testUpsertTrue() { employee.setEmployeeNote(empNote1); WriteResult writeResult - = employeeRepository.update(where("empId").eq(12), employee, true); + = employeeRepository.update(where("empId").eq(12), employee, updateOptions(true)); assertEquals(writeResult.getAffectedCount(), 1); result = employeeRepository.find(where("joinDate").eq(joiningDate)); @@ -233,7 +234,7 @@ public void testUpsertFalse() { employee.setEmployeeNote(empNote1); WriteResult writeResult - = employeeRepository.update(where("empId").eq(12), employee, false); + = employeeRepository.update(where("empId").eq(12), employee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 0); result = employeeRepository.find(where("joinDate").eq(joiningDate)); @@ -325,7 +326,7 @@ public void testMultiUpdateWithObject() { update.setAddress("new address"); WriteResult writeResult - = employeeRepository.update(where("joinDate").eq(now), update, false); + = employeeRepository.update(where("joinDate").eq(now), update, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 0); } @@ -359,7 +360,7 @@ public void testUpdateWithChangedId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); assertEquals(count, employeeRepository.size()); @@ -378,7 +379,7 @@ public void testUpdateWithNullId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); } @@ -394,7 +395,7 @@ public void testUpdateWithDuplicateId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); assertEquals(count, employeeRepository.size()); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java index 813a36503..2bfd51c5a 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java @@ -17,6 +17,7 @@ package org.dizitart.no2.integration.event; +import org.dizitart.no2.collection.UpdateOptions; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.Employee; import org.dizitart.no2.Nitrite; @@ -159,7 +160,7 @@ public void testUpsert() { e.setEmpId(1L); e.setAddress("abcd"); - employeeRepository.update(where("empId").eq(1), e, true); + employeeRepository.update(where("empId").eq(1), e, UpdateOptions.updateOptions(true)); await().atMost(1, TimeUnit.SECONDS).until(listenerPrepared(EventType.Insert)); assertEquals(listener.getAction(), EventType.Insert); assertNotNull(listener.getItem()); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java index 1190ee868..a0787f3a9 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java @@ -27,7 +27,7 @@ import org.dizitart.no2.exceptions.MigrationException; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.migration.Instructions; +import org.dizitart.no2.migration.InstructionSet; import org.dizitart.no2.migration.Migration; import org.dizitart.no2.migration.TypeConverter; import org.dizitart.no2.mvstore.MVStoreModule; @@ -91,7 +91,7 @@ public void testRepositoryMigrate() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forDatabase() .addPassword("test-user", "test-password"); @@ -148,7 +148,7 @@ public void testCollectionMigrate() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forDatabase() .addPassword("test-user", "test-password"); @@ -179,11 +179,11 @@ public void migrate(Instructions instruction) { migration = new Migration(2, 3) { @Override - public void migrate(Instructions instructions) { - instructions.forDatabase() + public void migrate(InstructionSet instructionSet) { + instructionSet.forDatabase() .changePassword("test-user", "test-password", "password"); - instructions.forCollection("testCollectionMigrate") + instructionSet.forCollection("testCollectionMigrate") .dropIndex("firstName") .deleteField("bloodGroup") .addField("name", document -> faker.name().fullName()) @@ -230,7 +230,7 @@ public void testOpenWithoutSchemaVersion() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testOpenWithoutSchemaVersion") @@ -282,7 +282,7 @@ public void testDescendingSchema() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testDescendingSchema") @@ -308,9 +308,9 @@ public void migrate(Instructions instruction) { migration = new Migration(2, Constants.INITIAL_SCHEMA_VERSION) { @Override - public void migrate(Instructions instructions) { + public void migrate(InstructionSet instructionSet) { - instructions.forCollection("testDescendingSchema") + instructionSet.forCollection("testDescendingSchema") .rename("test"); } }; @@ -346,7 +346,7 @@ public void testMigrationWithoutVersion() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testMigrationWithoutVersion") @@ -387,7 +387,7 @@ public void testWrongSchemaVersionNoMigration() { Migration migration = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testWrongSchemaVersionNoMigration") .rename("test") @@ -416,8 +416,8 @@ public void migrate(Instructions instruction) { migration = new Migration(2, 3) { @Override - public void migrate(Instructions instructions) { - instructions.forCollection("test") + public void migrate(InstructionSet instructionSet) { + instructionSet.forCollection("test") .rename("testWrongSchemaVersionNoMigration"); } }; @@ -456,7 +456,7 @@ public void testReOpenAfterMigration() { Migration migration = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testReOpenAfterMigration") .rename("test") @@ -524,7 +524,7 @@ public void testMultipleMigrations() { Migration migration1 = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testMultipleMigrations") .rename("test"); @@ -533,7 +533,7 @@ public void migrate(Instructions instruction) { Migration migration2 = new Migration(2, 3) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .addField("fullName", "Dummy Name"); } @@ -558,7 +558,7 @@ public void migrate(Instructions instruction) { Migration migration3 = new Migration(3, 4) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .addField("age", 10); } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java index d5bd0f5cb..487d27ff6 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java @@ -39,6 +39,7 @@ import static org.awaitility.Awaitility.await; import static org.dizitart.no2.collection.Document.createDocument; +import static org.dizitart.no2.collection.UpdateOptions.updateOptions; import static org.dizitart.no2.filters.FluentFilter.where; import static org.junit.Assert.*; @@ -210,7 +211,7 @@ public void testUpsertTrue() { employee.setEmployeeNote(empNote1); WriteResult writeResult - = employeeRepository.update(where("empId").eq(12), employee, true); + = employeeRepository.update(where("empId").eq(12), employee, updateOptions(true)); assertEquals(writeResult.getAffectedCount(), 1); result = employeeRepository.find(where("joinDate").eq(joiningDate)); @@ -235,7 +236,7 @@ public void testUpsertFalse() { employee.setEmployeeNote(empNote1); WriteResult writeResult - = employeeRepository.update(where("empId").eq(12), employee, false); + = employeeRepository.update(where("empId").eq(12), employee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 0); result = employeeRepository.find(where("joinDate").eq(joiningDate)); @@ -327,7 +328,7 @@ public void testMultiUpdateWithObject() { update.setAddress("new address"); WriteResult writeResult - = employeeRepository.update(where("joinDate").eq(now), update, false); + = employeeRepository.update(where("joinDate").eq(now), update, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 0); } @@ -361,7 +362,7 @@ public void testUpdateWithChangedId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); assertEquals(count, employeeRepository.size()); @@ -380,7 +381,7 @@ public void testUpdateWithNullId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); } @@ -396,7 +397,7 @@ public void testUpdateWithDuplicateId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); assertEquals(count, employeeRepository.size()); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java index 95baec828..3ec7a6823 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java @@ -17,14 +17,12 @@ package org.dizitart.no2.integration.event; -import org.dizitart.no2.integration.Retry; -import org.dizitart.no2.integration.repository.data.Employee; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.events.EventType; -import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.integration.Retry; -import org.dizitart.no2.rocksdb.RocksDBModule; import org.dizitart.no2.integration.repository.data.Employee; +import org.dizitart.no2.repository.ObjectRepository; +import org.dizitart.no2.rocksdb.RocksDBModule; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -40,6 +38,7 @@ import java.util.concurrent.atomic.AtomicInteger; import static org.awaitility.Awaitility.await; +import static org.dizitart.no2.collection.UpdateOptions.updateOptions; import static org.dizitart.no2.filters.Filter.ALL; import static org.dizitart.no2.filters.FluentFilter.where; import static org.dizitart.no2.integration.TestUtil.deleteDb; @@ -129,7 +128,7 @@ public void testUpsert() { e.setEmpId(1L); e.setAddress("abcd"); - employeeRepository.update(where("empId").eq(1), e, true); + employeeRepository.update(where("empId").eq(1), e, updateOptions(true)); await().atMost(1, TimeUnit.SECONDS).until(listenerPrepared(EventType.Insert)); assertEquals(listener.getAction(), EventType.Insert); assertNotNull(listener.getItem()); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java index 6e95491ad..ffd30a92b 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java @@ -27,7 +27,7 @@ import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.integration.Retry; -import org.dizitart.no2.migration.Instructions; +import org.dizitart.no2.migration.InstructionSet; import org.dizitart.no2.migration.Migration; import org.dizitart.no2.migration.TypeConverter; import org.dizitart.no2.repository.ObjectRepository; @@ -91,7 +91,7 @@ public void testRepositoryMigrate() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forDatabase() .addPassword("test-user", "test-password"); @@ -147,7 +147,7 @@ public void testCollectionMigrate() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forDatabase() .addPassword("test-user", "test-password"); @@ -177,11 +177,11 @@ public void migrate(Instructions instruction) { migration = new Migration(2, 3) { @Override - public void migrate(Instructions instructions) { - instructions.forDatabase() + public void migrate(InstructionSet instructionSet) { + instructionSet.forDatabase() .changePassword("test-user", "test-password", "password"); - instructions.forCollection("testCollectionMigrate") + instructionSet.forCollection("testCollectionMigrate") .dropIndex("firstName") .deleteField("bloodGroup") .addField("name", document -> faker.name().fullName()) @@ -227,7 +227,7 @@ public void testOpenWithoutSchemaVersion() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testOpenWithoutSchemaVersion") @@ -277,7 +277,7 @@ public void testDescendingSchema() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testDescendingSchema") @@ -302,9 +302,9 @@ public void migrate(Instructions instruction) { migration = new Migration(2, Constants.INITIAL_SCHEMA_VERSION) { @Override - public void migrate(Instructions instructions) { + public void migrate(InstructionSet instructionSet) { - instructions.forCollection("testDescendingSchema") + instructionSet.forCollection("testDescendingSchema") .rename("test"); } }; @@ -339,7 +339,7 @@ public void testMigrationWithoutVersion() { Migration migration = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .rename("testMigrationWithoutVersion") @@ -379,7 +379,7 @@ public void testWrongSchemaVersionNoMigration() { Migration migration = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testWrongSchemaVersionNoMigration") .rename("test") @@ -407,8 +407,8 @@ public void migrate(Instructions instruction) { migration = new Migration(2, 3) { @Override - public void migrate(Instructions instructions) { - instructions.forCollection("test") + public void migrate(InstructionSet instructionSet) { + instructionSet.forCollection("test") .rename("testWrongSchemaVersionNoMigration"); } }; @@ -446,7 +446,7 @@ public void testReOpenAfterMigration() { Migration migration = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testReOpenAfterMigration") .rename("test") @@ -513,7 +513,7 @@ public void testMultipleMigrations() { Migration migration1 = new Migration(1, 2) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("testMultipleMigrations") .rename("test"); @@ -522,7 +522,7 @@ public void migrate(Instructions instruction) { Migration migration2 = new Migration(2, 3) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .addField("fullName", "Dummy Name"); } @@ -546,7 +546,7 @@ public void migrate(Instructions instruction) { Migration migration3 = new Migration(3, 4) { @Override - public void migrate(Instructions instruction) { + public void migrate(InstructionSet instruction) { instruction.forCollection("test") .addField("age", 10); } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java index d5bd0f5cb..487d27ff6 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java @@ -39,6 +39,7 @@ import static org.awaitility.Awaitility.await; import static org.dizitart.no2.collection.Document.createDocument; +import static org.dizitart.no2.collection.UpdateOptions.updateOptions; import static org.dizitart.no2.filters.FluentFilter.where; import static org.junit.Assert.*; @@ -210,7 +211,7 @@ public void testUpsertTrue() { employee.setEmployeeNote(empNote1); WriteResult writeResult - = employeeRepository.update(where("empId").eq(12), employee, true); + = employeeRepository.update(where("empId").eq(12), employee, updateOptions(true)); assertEquals(writeResult.getAffectedCount(), 1); result = employeeRepository.find(where("joinDate").eq(joiningDate)); @@ -235,7 +236,7 @@ public void testUpsertFalse() { employee.setEmployeeNote(empNote1); WriteResult writeResult - = employeeRepository.update(where("empId").eq(12), employee, false); + = employeeRepository.update(where("empId").eq(12), employee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 0); result = employeeRepository.find(where("joinDate").eq(joiningDate)); @@ -327,7 +328,7 @@ public void testMultiUpdateWithObject() { update.setAddress("new address"); WriteResult writeResult - = employeeRepository.update(where("joinDate").eq(now), update, false); + = employeeRepository.update(where("joinDate").eq(now), update, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 0); } @@ -361,7 +362,7 @@ public void testUpdateWithChangedId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); assertEquals(count, employeeRepository.size()); @@ -380,7 +381,7 @@ public void testUpdateWithNullId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); } @@ -396,7 +397,7 @@ public void testUpdateWithDuplicateId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); assertEquals(count, employeeRepository.size()); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DocumentCursor.java b/nitrite/src/main/java/org/dizitart/no2/collection/DocumentCursor.java index 919b7ae9f..ab87832ea 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DocumentCursor.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DocumentCursor.java @@ -64,8 +64,8 @@ public interface DocumentCursor extends RecordStream { /** * Performs a left outer join with a foreign cursor with the specified lookup parameters. *

- * It performs an equality match on the localString to the foreignString from the documents of the foreign cursor. - * If an input document does not contain the localString, the join treats the field as having a value of null + * It performs an equality match on the localField to the foreignField from the documents of the foreign cursor. + * If an input document does not contain the localField, the join treats the field as having a value of null * for matching purposes. * * @param foreignCursor the foreign cursor for the join. diff --git a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java index 76eb362e3..ce79f6f02 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java @@ -23,19 +23,13 @@ import org.dizitart.no2.collection.events.EventType; import org.dizitart.no2.common.meta.AttributesAware; import org.dizitart.no2.common.processors.Processor; -import org.dizitart.no2.common.util.Iterables; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.store.NitriteStore; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; - -import static org.dizitart.no2.common.util.ValidationUtils.containsNull; -import static org.dizitart.no2.common.util.ValidationUtils.notNull; /** * The interface Persistent collection. @@ -201,32 +195,6 @@ default WriteResult update(T element) { */ WriteResult update(T element, boolean insertIfAbsent); - - /** - * Updates {@code elements} in the collection. Specified {@code elements} must have an id. - * If the {@code elements} are not found in the collection, it will be inserted only if {@code insertIfAbsent} - * is set to {@code true}. - * - * @param elements the elements to update. - * @param insertIfAbsent if set to {@code true}, {@code elements} will be inserted if not found. - * @return the result of the update operation. - * @throws org.dizitart.no2.exceptions.ValidationException if the {@code elements} is {@code null}. - * @throws org.dizitart.no2.exceptions.NotIdentifiableException if the {@code elements} does not have any id field. - */ - default WriteResult update(T[] elements, boolean insertIfAbsent) { - notNull(elements, "a null element cannot be updated"); - containsNull(elements, "a null element cannot be updated"); - - List affectedIds = new ArrayList<>(); - - for (T element : elements) { - WriteResult writeResult = update(element, insertIfAbsent); - affectedIds.addAll(Iterables.toList(writeResult)); - } - - return affectedIds::iterator; - } - /** * Deletes the {@code element} from the collection. The {@code element} must have an id. * diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/MutatedObjectStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/MutatedObjectStream.java similarity index 89% rename from nitrite/src/main/java/org/dizitart/no2/repository/MutatedObjectStream.java rename to nitrite/src/main/java/org/dizitart/no2/common/streams/MutatedObjectStream.java index ae3213ed6..856322d5c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/MutatedObjectStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/MutatedObjectStream.java @@ -1,20 +1,21 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2022 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.repository; +package org.dizitart.no2.common.streams; import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.RecordStream; @@ -28,12 +29,12 @@ /** * @author Anindya Chatterjee. */ -class MutatedObjectStream implements RecordStream { +public class MutatedObjectStream implements RecordStream { private final RecordStream recordIterable; private final Class mutationType; private final NitriteMapper nitriteMapper; - MutatedObjectStream(NitriteMapper nitriteMapper, + public MutatedObjectStream(NitriteMapper nitriteMapper, RecordStream recordIterable, Class mutationType) { this.recordIterable = recordIterable; diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/CollectionInstruction.java b/nitrite/src/main/java/org/dizitart/no2/migration/CollectionInstruction.java index edc12009c..ebce6123f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/CollectionInstruction.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/CollectionInstruction.java @@ -24,6 +24,7 @@ default CollectionInstruction rename(String name) { addStep(migrationStep); final CollectionInstruction parent = this; + // new instruction set for new collection return new CollectionInstruction() { @Override public String collectionName() { diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/DatabaseInstruction.java b/nitrite/src/main/java/org/dizitart/no2/migration/DatabaseInstruction.java index 3f6f919e4..c1c2acb4c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/DatabaseInstruction.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/DatabaseInstruction.java @@ -4,6 +4,8 @@ import org.dizitart.no2.common.tuples.Triplet; import org.dizitart.no2.common.util.SecureString; +import static org.dizitart.no2.common.util.ObjectUtils.getEntityName; + /** * Represents a migration instruction set for the nitrite database. * @@ -64,7 +66,7 @@ default DatabaseInstruction dropCollection(String collectionName) { * @return the database instruction */ default DatabaseInstruction dropRepository(Class type) { - return dropRepository(type.getName()); + return dropRepository(getEntityName(type)); } /** @@ -85,7 +87,7 @@ default DatabaseInstruction dropRepository(String typeName) { * @return the database instruction */ default DatabaseInstruction dropRepository(Class type, String key) { - return dropRepository(type.getName(), key); + return dropRepository(getEntityName(type), key); } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/Instructions.java b/nitrite/src/main/java/org/dizitart/no2/migration/InstructionSet.java similarity index 98% rename from nitrite/src/main/java/org/dizitart/no2/migration/Instructions.java rename to nitrite/src/main/java/org/dizitart/no2/migration/InstructionSet.java index 5f98c267e..46c338b8c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/Instructions.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/InstructionSet.java @@ -8,7 +8,7 @@ * @author Anindya Chatterjee * @since 4.0 */ -public interface Instructions { +public interface InstructionSet { /** * Creates a {@link DatabaseInstruction}. * diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/Migration.java b/nitrite/src/main/java/org/dizitart/no2/migration/Migration.java index 05177f507..a75354881 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/Migration.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/Migration.java @@ -37,9 +37,9 @@ public Migration(Integer fromVersion, Integer toVersion) { /** * Migrates the database using the instructions. * - * @param instructions the instructions + * @param instructionSet the instructions */ - public abstract void migrate(Instructions instructions); + public abstract void migrate(InstructionSet instructionSet); /** * Returns the {@link MigrationStep}s as a queue for execution. @@ -54,7 +54,7 @@ public Queue steps() { } private void execute() { - NitriteInstructions instruction = new NitriteInstructions(migrationSteps); + NitriteInstructionSet instruction = new NitriteInstructionSet(migrationSteps); migrate(instruction); this.executed = true; } diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java b/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java index b205b749c..5190ef3d7 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/MigrationManager.java @@ -62,7 +62,7 @@ public void doMigrate() { throw new NitriteIOException("Failed to close the database", e); } - throw new MigrationException("Schema version mismatch, as no migration path found from version " + throw new MigrationException("Schema version mismatch, no migration path found from version " + storeMetadata.getSchemaVersion() + " to " + nitriteConfig.getSchemaVersion()); } @@ -88,6 +88,7 @@ private boolean isMigrationNeeded() { if (incomingVersion == null) { throw new MigrationException("Invalid version provided"); } + return !existingVersion.equals(incomingVersion); } diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructions.java b/nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructionSet.java similarity index 88% rename from nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructions.java rename to nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructionSet.java index 3dade87ec..2d1e3bc53 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructions.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructionSet.java @@ -6,16 +6,16 @@ import java.util.Queue; /** - * Default implementation of {@link Instructions}. + * Default implementation of {@link InstructionSet}. * * @author Anindya Chatterjee * @since 4.0 */ -class NitriteInstructions implements Instructions { +class NitriteInstructionSet implements InstructionSet { @Getter(AccessLevel.PACKAGE) private final Queue migrationSteps; - NitriteInstructions(Queue migrationSteps) { + NitriteInstructionSet(Queue migrationSteps) { this.migrationSteps = migrationSteps; } diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/RepositoryInstruction.java b/nitrite/src/main/java/org/dizitart/no2/migration/RepositoryInstruction.java index 7dd160cac..c24f304c0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/RepositoryInstruction.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/RepositoryInstruction.java @@ -27,8 +27,10 @@ default RepositoryInstruction renameRepository(String entityName, String key) { migrationStep.setInstructionType(InstructionType.RenameRepository); migrationStep.setArguments(new Quartet<>(entityName(), key(), entityName, key)); addStep(migrationStep); + final RepositoryInstruction parent = this; + // new instruction set for new repository return new RepositoryInstruction() { @Override public String entityName() { diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/Cursor.java b/nitrite/src/main/java/org/dizitart/no2/repository/Cursor.java index 5d6049923..50aaa3402 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/Cursor.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/Cursor.java @@ -17,13 +17,11 @@ package org.dizitart.no2.repository; import org.dizitart.no2.collection.FindPlan; -import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.Lookup; import org.dizitart.no2.common.RecordStream; /** - * A collection of {@link NitriteId}s of the database records, - * as a result of a find operation. + * An interface to iterate over {@link ObjectRepository#find()} results. * * @author Anindya Chatterjee * @since 1.0 @@ -37,7 +35,7 @@ public interface Cursor extends RecordStream { FindPlan getFindPlan(); /** - * Projects the result of one type into an {@link Iterable} of other type. + * Projects the result of one type into an {@link Iterable} of another type. * * @param

the type of the target objects. * @param projectionType the projection type. @@ -48,8 +46,8 @@ public interface Cursor extends RecordStream { /** * Performs a left outer join with a foreign cursor with the specified lookup parameters. *

- * It performs an equality match on the localString to the foreignString from the objects of the foreign cursor. - * If an input object does not contain the localString, the join treats the field as having a value of null + * It performs an equality match on the localField to the foreignField from the objects of the foreign cursor. + * If an input object does not contain the localField, the join treats the field as having a value of null * for matching purposes. * * @param the type of the foreign object. diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java b/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java index 6a9ea6483..d60923af3 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java @@ -20,6 +20,7 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.collection.UpdateOptions; import org.dizitart.no2.collection.events.CollectionEventListener; import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; @@ -105,17 +106,19 @@ public WriteResult insert(T[] elements) { @Override public WriteResult update(T element, boolean insertIfAbsent) { notNull(element, "a null object cannot be used for update"); - return update(operations.createUniqueFilter(element), element, insertIfAbsent); + return update(operations.createUniqueFilter(element), element, updateOptions(insertIfAbsent, true)); } @Override - public WriteResult update(Filter filter, T update, boolean insertIfAbsent) { + public WriteResult update(Filter filter, T update, UpdateOptions updateOptions) { notNull(update, "a null object cannot be used for update"); Document updateDocument = operations.toDocument(update, true); - if (!insertIfAbsent) { + if (updateOptions == null || !updateOptions.isInsertIfAbsent()) { operations.removeNitriteId(updateDocument); } - return collection.update(operations.asObjectFilter(filter), updateDocument, updateOptions(insertIfAbsent, true)); + + return collection.update(operations.asObjectFilter(filter), updateDocument, + updateOptions); } @Override diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java index 3e735083f..174aa3e28 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java @@ -21,6 +21,7 @@ import org.dizitart.no2.collection.FindPlan; import org.dizitart.no2.common.Lookup; import org.dizitart.no2.common.RecordStream; +import org.dizitart.no2.common.streams.MutatedObjectStream; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.common.mapper.NitriteMapper; diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectRepository.java b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectRepository.java index 48c01be4f..b22ed32e7 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectRepository.java @@ -16,10 +16,7 @@ package org.dizitart.no2.repository; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.FindOptions; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventListener; import org.dizitart.no2.collection.events.EventAware; import org.dizitart.no2.collection.events.EventType; @@ -37,6 +34,7 @@ import java.util.Collections; import java.util.List; +import static org.dizitart.no2.collection.UpdateOptions.updateOptions; import static org.dizitart.no2.common.util.ValidationUtils.containsNull; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -78,9 +76,7 @@ public interface ObjectRepository extends PersistentCollection { /** * Inserts object into this repository. If the object contains a value marked with * {@link Id}, then the value will be used as a unique key to identify the object - * in the repository. If the object does not contain any value marked with {@link Id}, - * then nitrite will generate a new {@link NitriteId} and will add it to the document - * generated from the object. + * in the repository. *

* If any of the value is already indexed in the repository, then after insertion the * index will also be updated. @@ -112,7 +108,7 @@ default WriteResult insert(T object, T... others) { List itemList = new ArrayList<>(); itemList.add(object); - if (others != null && itemList.size() > 0) { + if (others != null) { Collections.addAll(itemList, others); } @@ -141,7 +137,7 @@ default WriteResult insert(T object, T... others) { * @throws ValidationException if the {@code update} object is {@code null}. */ default WriteResult update(Filter filter, T update) { - return update(filter, update, false); + return update(filter, update, updateOptions(false)); } /** @@ -163,12 +159,12 @@ default WriteResult update(Filter filter, T update) { * * @param filter the filter to apply to select objects from the collection. * @param update the modifications to apply. - * @param insertIfAbsent if set to {@code true}, {@code update} object will be inserted if not found. + * @param updateOptions various options for update operation. * @return the result of the update operation. * @throws ValidationException if the {@code update} object is {@code null}. * @throws ValidationException if {@code updateOptions} is {@code null}. */ - WriteResult update(Filter filter, T update, boolean insertIfAbsent); + WriteResult update(Filter filter, T update, UpdateOptions updateOptions); /** * Updates object in the repository by setting the field specified in {@code document}. @@ -328,7 +324,7 @@ default Cursor find(FindOptions findOptions) { Class getType(); /** - * Returns the underlying document collection. + * Returns the underlying {@link NitriteCollection} instance. * * @return the underlying document collection. */ diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/Reflector.java b/nitrite/src/main/java/org/dizitart/no2/repository/Reflector.java index 2c7dba132..fa334668c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/Reflector.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/Reflector.java @@ -139,7 +139,7 @@ public List getAllFields(Class type) { return fields; } - public void filterSynthetics(List fields) { + private void filterSynthetics(List fields) { if (fields == null || fields.isEmpty()) return; Iterator iterator = fields.iterator(); if (iterator.hasNext()) { diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java index 8aa9dee26..86693a4d5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java @@ -116,7 +116,7 @@ public void clear() { } repositoryMap.clear(); } catch (Exception e) { - throw new NitriteIOException("Failed to close an object repository", e); + throw new NitriteIOException("Failed to clear an object repository", e); } finally { lock.unlock(); } @@ -127,7 +127,7 @@ private ObjectRepository createRepository(NitriteConfig nitriteConfig, Cl NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); NitriteStore store = nitriteConfig.getNitriteStore(); if (nitriteMapper.isValueType(type)) { - throw new ValidationException("A value type cannot be used to create repository"); + throw new ValidationException("Cannot create a repository for a value type"); } if (store.getCollectionNames().contains(collectionName)) { diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java index 466e5a420..fe81102a0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java @@ -4,6 +4,7 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.FindOptions; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.collection.UpdateOptions; import org.dizitart.no2.collection.events.CollectionEventListener; import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; @@ -97,19 +98,19 @@ public WriteResult insert(T[] elements) { public WriteResult update(T element, boolean insertIfAbsent) { notNull(element, "a null object cannot be used for update"); - return update(operations.createUniqueFilter(element), element, insertIfAbsent); + return update(operations.createUniqueFilter(element), element, updateOptions(insertIfAbsent, true)); } @Override - public WriteResult update(Filter filter, T update, boolean insertIfAbsent) { + public WriteResult update(Filter filter, T update, UpdateOptions updateOptions) { notNull(update, "a null object cannot be used for update"); Document updateDocument = operations.toDocument(update, true); - if (!insertIfAbsent) { + if (updateOptions == null || !updateOptions.isInsertIfAbsent()) { operations.removeNitriteId(updateDocument); } return backingCollection.update(operations.asObjectFilter(filter), updateDocument, - updateOptions(insertIfAbsent, true)); + updateOptions); } @Override @@ -118,7 +119,8 @@ public WriteResult update(Filter filter, Document update, boolean justOnce) { operations.removeNitriteId(update); operations.serializeFields(update); - return backingCollection.update(operations.asObjectFilter(filter), update, updateOptions(false, justOnce)); + return backingCollection.update(operations.asObjectFilter(filter), update, + updateOptions(false, justOnce)); } @Override diff --git a/nitrite/src/test/java/org/dizitart/no2/common/event/EventTest.java b/nitrite/src/test/java/org/dizitart/no2/common/event/EventTest.java index bf73a45bb..1213cf3dc 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/event/EventTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/event/EventTest.java @@ -18,6 +18,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; +import org.dizitart.no2.collection.UpdateOptions; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.collection.events.EventType; import org.dizitart.no2.repository.ObjectRepository; @@ -115,7 +116,7 @@ public void testUpsert() { e.setEmpId(1L); e.setAddress("abcd"); - employeeRepository.update(where("empId").eq(1), e, true); + employeeRepository.update(where("empId").eq(1), e, UpdateOptions.updateOptions(true)); await().atMost(1, TimeUnit.SECONDS).until(listenerPrepared(EventType.Insert)); assertEquals(listener.getAction(), EventType.Insert); assertNotNull(listener.getItem()); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java index 90559fea1..095e08ae6 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java @@ -39,6 +39,7 @@ import static org.awaitility.Awaitility.await; import static org.dizitart.no2.collection.Document.createDocument; +import static org.dizitart.no2.collection.UpdateOptions.updateOptions; import static org.dizitart.no2.filters.FluentFilter.where; import static org.junit.Assert.*; @@ -210,7 +211,7 @@ public void testUpsertTrue() { employee.setEmployeeNote(empNote1); WriteResult writeResult - = employeeRepository.update(where("empId").eq(12), employee, true); + = employeeRepository.update(where("empId").eq(12), employee, updateOptions(true)); assertEquals(writeResult.getAffectedCount(), 1); result = employeeRepository.find(where("joinDate").eq(joiningDate)); @@ -235,7 +236,7 @@ public void testUpsertFalse() { employee.setEmployeeNote(empNote1); WriteResult writeResult - = employeeRepository.update(where("empId").eq(12), employee, false); + = employeeRepository.update(where("empId").eq(12), employee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 0); result = employeeRepository.find(where("joinDate").eq(joiningDate)); @@ -327,7 +328,7 @@ public void testMultiUpdateWithObject() { update.setAddress("new address"); WriteResult writeResult - = employeeRepository.update(where("joinDate").eq(now), update, false); + = employeeRepository.update(where("joinDate").eq(now), update, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 0); } @@ -361,7 +362,7 @@ public void testUpdateWithChangedId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); assertEquals(count, employeeRepository.size()); @@ -380,7 +381,7 @@ public void testUpdateWithNullId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); } @@ -396,7 +397,7 @@ public void testUpdateWithDuplicateId() { Employee result = employeeRepository.find(where("empId").eq(oldId)).firstOrNull(); assertNotNull(result.getJoinDate()); - WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, false); + WriteResult writeResult = employeeRepository.update(where("empId").eq(oldId), newEmployee, updateOptions(false)); assertEquals(writeResult.getAffectedCount(), 1); assertEquals(count, employeeRepository.size()); diff --git a/nitrite/src/test/java/org/dizitart/no2/migration/NitriteInstructionsTest.java b/nitrite/src/test/java/org/dizitart/no2/migration/NitriteInstructionSetTest.java similarity index 85% rename from nitrite/src/test/java/org/dizitart/no2/migration/NitriteInstructionsTest.java rename to nitrite/src/test/java/org/dizitart/no2/migration/NitriteInstructionSetTest.java index 0b1e7c621..32858c257 100644 --- a/nitrite/src/test/java/org/dizitart/no2/migration/NitriteInstructionsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/migration/NitriteInstructionSetTest.java @@ -23,11 +23,11 @@ import static org.junit.Assert.assertSame; -public class NitriteInstructionsTest { +public class NitriteInstructionSetTest { @Test public void testConstructor() { LinkedList migrationStepList = new LinkedList<>(); - assertSame(migrationStepList, (new NitriteInstructions(migrationStepList)).getMigrationSteps()); + assertSame(migrationStepList, (new NitriteInstructionSet(migrationStepList)).getMigrationSteps()); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java index 7064c0b33..071f4a747 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/DefaultObjectRepositoryTest.java @@ -18,10 +18,7 @@ package org.dizitart.no2.repository; import org.dizitart.no2.NitriteConfig; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.collection.FindOptions; -import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventListener; import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.RecordStream; @@ -158,9 +155,9 @@ public void testInsert() { public void testUpdate2() { DefaultObjectRepository defaultObjectRepository = (DefaultObjectRepository) mock( DefaultObjectRepository.class); - when(defaultObjectRepository.update(any(), (Object) any(), anyBoolean())).thenReturn(null); - defaultObjectRepository.update(mock(Filter.class), "Update", true); - verify(defaultObjectRepository).update(any(), (Object) any(), anyBoolean()); + when(defaultObjectRepository.update(any(), (Object) any(), any())).thenReturn(null); + defaultObjectRepository.update(mock(Filter.class), "Update", UpdateOptions.updateOptions(true)); + verify(defaultObjectRepository).update(any(), (Object) any(), any()); } @Test diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java index b2edd5e3e..5fcb74699 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java @@ -26,6 +26,7 @@ import org.dizitart.no2.common.mapper.MappableMapper; import org.dizitart.no2.common.processors.ProcessorChain; import org.dizitart.no2.common.streams.DocumentStream; +import org.dizitart.no2.common.streams.MutatedObjectStream; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.exceptions.ValidationException; import org.junit.Test; diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java index bfbca34ee..993407d1f 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalRepositoryTest.java @@ -27,6 +27,7 @@ import java.util.ArrayList; +import static org.dizitart.no2.collection.UpdateOptions.updateOptions; import static org.mockito.Mockito.*; public class DefaultTransactionalRepositoryTest { @@ -124,9 +125,9 @@ public void testUpdate() { public void testUpdate2() { DefaultTransactionalRepository defaultTransactionalRepository = (DefaultTransactionalRepository) mock( DefaultTransactionalRepository.class); - when(defaultTransactionalRepository.update(any(), (Object) any(), anyBoolean())).thenReturn(null); - defaultTransactionalRepository.update(mock(Filter.class), "Update", true); - verify(defaultTransactionalRepository).update(any(), (Object) any(), anyBoolean()); + when(defaultTransactionalRepository.update(any(), (Object) any(), any())).thenReturn(null); + defaultTransactionalRepository.update(mock(Filter.class), "Update", updateOptions(true)); + verify(defaultTransactionalRepository).update(any(), (Object) any(), any()); } @Test From 9c6c545873f049645580528564c1e2c259692a43 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 6 Jul 2022 19:33:54 +0530 Subject: [PATCH 51/78] refactor --- .../org/dizitart/no2/common/mapper/MappableMapper.java | 6 +++++- .../org/dizitart/no2/repository/RepositoryOperations.java | 7 ++++++- .../dizitart/no2/repository/RepositoryOperationsTest.java | 5 +++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java index 056639d53..f7ae7f377 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java @@ -125,7 +125,11 @@ public Target convert(Source source, Class type) { return (Target) source; } else { if (Document.class.isAssignableFrom(type)) { - return (Target) convertToDocument(source); + if (source instanceof Document) { + return (Target) source; + } else { + return (Target) convertToDocument(source); + } } else if (source instanceof Document) { return convertFromDocument((Document) source, type); } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java index 753af5125..546363b6f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java @@ -21,6 +21,7 @@ import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.exceptions.InvalidIdException; import org.dizitart.no2.exceptions.NotIdentifiableException; +import org.dizitart.no2.exceptions.ObjectMappingException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.filters.NitriteFilter; @@ -102,7 +103,7 @@ public Document[] toDocuments(T[] others) { if (others == null || others.length == 0) return null; Document[] documents = new Document[others.length]; for (int i = 0; i < others.length; i++) { - documents[i] = toDocument(others[i], false); + documents[i] = toDocument(others[i], false); // this method is for insert only } return documents; } @@ -117,6 +118,9 @@ public Document[] toDocuments(T[] others) { */ public Document toDocument(T object, boolean update) { Document document = nitriteMapper.convert(object, Document.class); + if (document == null) { + throw new ObjectMappingException("Failed to map object to document"); + } if (objectIdField != null) { Field idField = objectIdField.getField(); @@ -129,6 +133,7 @@ public Document toDocument(T object, boolean update) { idField.set(object, id); document.put(objectIdField.getIdFieldName(), nitriteMapper.convert(id, Comparable.class)); } else if (!update) { + // if it is an insert, then we should not allow to insert the document with user provided id throw new InvalidIdException("Auto generated id should not be set manually"); } } catch (IllegalAccessException iae) { diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryOperationsTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryOperationsTest.java index df3580ea8..2aff316cd 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryOperationsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryOperationsTest.java @@ -27,6 +27,7 @@ import org.dizitart.no2.common.streams.DocumentStream; import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.exceptions.NotIdentifiableException; +import org.dizitart.no2.exceptions.ObjectMappingException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.filters.Filter; import org.junit.Test; @@ -42,7 +43,7 @@ public void testConstructor() { () -> new RepositoryOperations(type, new NitriteBuilderTest.CustomNitriteMapper(), null)); } - @Test + @Test(expected = ObjectMappingException.class) public void testToDocuments() { Class type = Object.class; assertEquals(3, @@ -61,7 +62,7 @@ public void testToDocuments3() { .toDocuments(new Object[]{})); } - @Test + @Test(expected = ObjectMappingException.class) public void testToDocument() { Class type = Object.class; assertNull( From 28a4210de8132056c2a4674eff762ffc76de0abf Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 6 Jul 2022 19:56:10 +0530 Subject: [PATCH 52/78] refactoring --- .../org/dizitart/no2/repository/RepositoryOperations.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java index 546363b6f..2a9599649 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java @@ -160,7 +160,7 @@ public Document toDocument(T object, boolean update) { */ public Filter createUniqueFilter(Object object) { if (objectIdField == null) { - throw new NotIdentifiableException("Update operation failed as no id value found for the object"); + throw new NotIdentifiableException("No id value found for the object"); } Field idField = objectIdField.getField(); @@ -202,7 +202,7 @@ public void removeNitriteId(Document document) { public Filter createIdFilter(I id) { if (objectIdField != null) { if (id == null) { - throw new InvalidIdException("A null id is not a valid id"); + throw new InvalidIdException("Id cannot be null"); } if (!isCompatibleTypes(id.getClass(), objectIdField.getField().getType())) { throw new InvalidIdException("A value of invalid type is provided as id"); From 54cb0b3963acdcefd4107d5176c3ae2d7adf92f6 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 11 Jul 2022 15:50:44 +0530 Subject: [PATCH 53/78] refactoring --- .../no2/common/PersistentCollection.java | 2 +- .../dizitart/no2/common/util/SpatialKey.java | 82 +++++++++++++ .../no2/store/memory/InMemoryRTree.java | 109 +----------------- .../dizitart/no2/transaction/ChangeType.java | 14 +-- .../DefaultTransactionalCollection.java | 9 +- .../no2/transaction/NitriteTransaction.java | 25 ++-- .../org/dizitart/no2/transaction/Session.java | 4 +- .../dizitart/no2/transaction/Transaction.java | 2 +- .../no2/transaction/TransactionConfig.java | 31 ----- .../{State.java => TransactionState.java} | 12 +- .../no2/transaction/TransactionStore.java | 6 +- .../no2/transaction/TransactionalMap.java | 2 +- .../no2/transaction/TransactionalRTree.java | 69 +---------- .../no2/store/memory/InMemoryRTreeTest.java | 3 +- .../no2/transaction/ChangeTypeTest.java | 7 +- .../transaction/NitriteTransactionTest.java | 2 +- ...ateTest.java => TransactionStateTest.java} | 18 +-- 17 files changed, 131 insertions(+), 266 deletions(-) create mode 100644 nitrite/src/main/java/org/dizitart/no2/common/util/SpatialKey.java rename nitrite/src/main/java/org/dizitart/no2/transaction/{State.java => TransactionState.java} (63%) rename nitrite/src/test/java/org/dizitart/no2/transaction/{StateTest.java => TransactionStateTest.java} (58%) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java index ce79f6f02..17d7fe026 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/PersistentCollection.java @@ -50,7 +50,7 @@ public interface PersistentCollection extends EventAware, AttributesAware, Au void addProcessor(Processor processor); /** - * Creates an unique index on the {@code fields}, if not already exists. + * Creates a unique index on the {@code fields}, if not already exists. * * @param fields the fields to be indexed. * @throws org.dizitart.no2.exceptions.IndexingException if an index already exists on the field. diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/SpatialKey.java b/nitrite/src/main/java/org/dizitart/no2/common/util/SpatialKey.java new file mode 100644 index 000000000..da9d8e1b4 --- /dev/null +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/SpatialKey.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.common.util; + +import java.util.Arrays; + +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +public class SpatialKey { + private final long id; + private final float[] minMax; + + public SpatialKey(long id, float... minMax) { + this.id = id; + this.minMax = minMax; + } + + public float min(int dim) { + return minMax[dim + dim]; + } + + public void setMin(int dim, float x) { + minMax[dim + dim] = x; + } + + public float max(int dim) { + return minMax[dim + dim + 1]; + } + + public void setMax(int dim, float x) { + minMax[dim + dim + 1] = x; + } + + public long getId() { + return id; + } + + public boolean isNull() { + return minMax.length == 0; + } + + @Override + public int hashCode() { + return (int) ((id >>> 32) ^ id); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (!(other instanceof SpatialKey)) { + return false; + } + SpatialKey o = (SpatialKey) other; + if (id != o.id) { + return false; + } + return equalsIgnoringId(o); + } + + public boolean equalsIgnoringId(SpatialKey o) { + return Arrays.equals(minMax, o.minMax); + } +} diff --git a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryRTree.java b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryRTree.java index 379e820c1..ebedd2db1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryRTree.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/memory/InMemoryRTree.java @@ -2,11 +2,11 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.RecordStream; +import org.dizitart.no2.common.util.SpatialKey; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.index.BoundingBox; import org.dizitart.no2.store.NitriteRTree; -import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -145,111 +145,4 @@ private void checkOpened() { throw new InvalidOperationException("RTreeMap is dropped"); } } - - /** - * The type Spatial key. - */ - static class SpatialKey { - - private final long id; - private final float[] minMax; - - /** - * Instantiates a new Spatial key. - * - * @param id the id - * @param minMax the min max - */ - public SpatialKey(long id, float... minMax) { - this.id = id; - this.minMax = minMax; - } - - /** - * Min float. - * - * @param dim the dim - * @return the float - */ - public float min(int dim) { - return minMax[dim + dim]; - } - - /** - * Sets min. - * - * @param dim the dim - * @param x the x - */ - public void setMin(int dim, float x) { - minMax[dim + dim] = x; - } - - /** - * Max float. - * - * @param dim the dim - * @return the float - */ - public float max(int dim) { - return minMax[dim + dim + 1]; - } - - /** - * Sets max. - * - * @param dim the dim - * @param x the x - */ - public void setMax(int dim, float x) { - minMax[dim + dim + 1] = x; - } - - /** - * Gets id. - * - * @return the id - */ - public long getId() { - return id; - } - - /** - * Is null boolean. - * - * @return the boolean - */ - public boolean isNull() { - return minMax.length == 0; - } - - @Override - public int hashCode() { - return (int) ((id >>> 32) ^ id); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } else if (!(other instanceof SpatialKey)) { - return false; - } - SpatialKey o = (SpatialKey) other; - if (id != o.id) { - return false; - } - return equalsIgnoringId(o); - } - - /** - * Equals ignoring id boolean. - * - * @param o the o - * @return the boolean - */ - public boolean equalsIgnoringId(SpatialKey o) { - return Arrays.equals(minMax, o.minMax); - } - } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/ChangeType.java b/nitrite/src/main/java/org/dizitart/no2/transaction/ChangeType.java index 6c4ab7323..a29675bf1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/ChangeType.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/ChangeType.java @@ -45,7 +45,7 @@ enum ChangeType { /** * Drop all indices. */ - DropAllIndices, + DropAllIndexes, /** * Drop collection. @@ -55,15 +55,5 @@ enum ChangeType { /** * Set attribute. */ - SetAttribute, - - /** - * Add processor - * */ - AddProcessor, - - /** - * Remove processor - * */ - RemoveProcessor, + SetAttributes, } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index eb76d332b..057f029c1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -278,11 +278,6 @@ public void addProcessor(Processor processor) { } finally { writeLock.unlock(); } - - JournalEntry journalEntry = new JournalEntry(); - journalEntry.setChangeType(ChangeType.AddProcessor); - journalEntry.setCommit(() -> primary.addProcessor(processor)); - transactionContext.getJournal().add(journalEntry); } @Override @@ -431,7 +426,7 @@ public void dropAllIndices() { List indexEntries = new ArrayList<>(); JournalEntry journalEntry = new JournalEntry(); - journalEntry.setChangeType(ChangeType.DropAllIndices); + journalEntry.setChangeType(ChangeType.DropAllIndexes); journalEntry.setCommit(() -> { indexEntries.addAll(primary.listIndices()); primary.dropAllIndices(); @@ -595,7 +590,7 @@ public void setAttributes(Attributes attributes) { AtomicReference original = new AtomicReference<>(); JournalEntry journalEntry = new JournalEntry(); - journalEntry.setChangeType(ChangeType.SetAttribute); + journalEntry.setChangeType(ChangeType.SetAttributes); journalEntry.setCommit(() -> { original.set(primary.getAttributes()); primary.setAttributes(attributes); diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java index 1b71e0a19..a6807a381 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java @@ -39,7 +39,7 @@ class NitriteTransaction implements Transaction { @Getter private String id; - private State state; + private TransactionState state; public NitriteTransaction(Nitrite nitrite, LockService lockService) { this.nitrite = nitrite; @@ -152,7 +152,7 @@ public synchronized ObjectRepository getRepository(Class type, String @Override public synchronized void commit() { checkState(); - this.state = State.PartiallyCommitted; + this.state = TransactionState.PartiallyCommitted; for (Map.Entry contextEntry : contextMap.entrySet()) { String collectionName = contextEntry.getKey(); @@ -184,13 +184,13 @@ public synchronized void commit() { } } } catch (TransactionException te) { - state = State.Failed; + state = TransactionState.Failed; log.error("Error while committing transaction", te); throw te; } catch (Exception e) { - state = State.Failed; + state = TransactionState.Failed; log.error("Error while committing transaction", e); - throw new TransactionException("Failed to commit transaction", e); + throw new TransactionException("Error committing transaction", e); } finally { undoRegistry.put(collectionName, undoLog); transactionContext.getActive().set(false); @@ -198,13 +198,13 @@ public synchronized void commit() { } } - state = State.Committed; + state = TransactionState.Committed; close(); } @Override public synchronized void rollback() { - this.state = State.Aborted; + this.state = TransactionState.Aborted; for (Map.Entry> entry : undoRegistry.entrySet()) { String collectionName = entry.getKey(); @@ -233,7 +233,7 @@ public synchronized void rollback() { @Override public synchronized void close() { try { - state = State.Closed; + state = TransactionState.Closed; for (TransactionContext context : contextMap.values()) { context.getActive().set(false); } @@ -245,12 +245,11 @@ public synchronized void close() { this.transactionStore.close(); this.transactionConfig.close(); } catch (Exception e) { - throw new TransactionException("Transaction failed to close", e); + throw new TransactionException("Error closing transaction", e); } } - @Override - public synchronized State getState() { + public synchronized TransactionState getState() { return state; } @@ -270,11 +269,11 @@ private void prepare() { this.transactionConfig.autoConfigure(); this.transactionConfig.initialize(); this.transactionStore = (TransactionStore) this.transactionConfig.getNitriteStore(); - this.state = State.Active; + this.state = TransactionState.Active; } private void checkState() { - if (state != State.Active) { + if (state != TransactionState.Active) { throw new TransactionException("Transaction is not active"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/Session.java b/nitrite/src/main/java/org/dizitart/no2/transaction/Session.java index 0fd68e9d9..22bf76ae5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/Session.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/Session.java @@ -57,7 +57,7 @@ public Transaction beginTransaction() { public void close() { this.active.compareAndSet(true, false); for (Transaction transaction : transactionMap.values()) { - if (transaction.getState() != State.Closed) { + if (transaction.getState() != TransactionState.Closed) { transaction.rollback(); } } @@ -72,7 +72,7 @@ public void close() { */ public void checkState() { if (!active.get()) { - throw new TransactionException("This session is not active"); + throw new TransactionException("Session is closed"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/Transaction.java b/nitrite/src/main/java/org/dizitart/no2/transaction/Transaction.java index 5986c6cc7..d09ff2bd1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/Transaction.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/Transaction.java @@ -22,7 +22,7 @@ public interface Transaction extends AutoCloseable { * * @return the state */ - State getState(); + TransactionState getState(); /** * Gets a {@link NitriteCollection} to perform ACID operations on it. diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java index 69038dca0..3e8e6a979 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionConfig.java @@ -2,11 +2,7 @@ import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.NitriteConfig; -import org.dizitart.no2.exceptions.IndexingException; -import org.dizitart.no2.index.NitriteIndexer; import org.dizitart.no2.common.mapper.NitriteMapper; -import org.dizitart.no2.common.module.NitriteModule; -import org.dizitart.no2.store.NitriteStore; /** * @author Anindya Chatterjee @@ -21,43 +17,16 @@ public TransactionConfig(NitriteConfig config) { this.config = config; } - @Override - public NitriteIndexer findIndexer(String indexType) { - NitriteIndexer nitriteIndexer = pluginManager.getIndexerMap().get(indexType); - if (nitriteIndexer != null) { - nitriteIndexer.initialize(this); - return nitriteIndexer; - } else { - throw new IndexingException("No indexer found for index type " + indexType); - } - } - @Override public void fieldSeparator(String separator) { config.fieldSeparator(separator); } - @Override - public NitriteConfig loadModule(NitriteModule module) { - pluginManager.loadModule(module); - return this; - } - - @Override - public void autoConfigure() { - pluginManager.findAndLoadPlugins(); - } - @Override public NitriteMapper nitriteMapper() { return config.nitriteMapper(); } - @Override - public NitriteStore getNitriteStore() { - return pluginManager.getNitriteStore(); - } - @Override public void initialize() { super.initialize(); diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/State.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionState.java similarity index 63% rename from nitrite/src/main/java/org/dizitart/no2/transaction/State.java rename to nitrite/src/main/java/org/dizitart/no2/transaction/TransactionState.java index 39bad9902..88129c048 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/State.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionState.java @@ -6,34 +6,34 @@ * @author Anindya Chatterjee * @since 4.0 */ -public enum State { +public enum TransactionState { /** * Transaction is active. */ Active, /** - * Transaction is partially committed. + * Transaction partially committed. */ PartiallyCommitted, /** - * Transaction is fully committed. + * Transaction fully committed. */ Committed, /** - * Transaction is closed. + * Transaction closed. */ Closed, /** - * Transaction is failed. + * Transaction failed. */ Failed, /** - * Transaction is aborted. + * Transaction aborted. */ Aborted, } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java index efc94dd8a..b1e19a377 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionStore.java @@ -83,7 +83,7 @@ public NitriteMap openMap(String mapName, Class keyT if (nitriteMap.isClosed()) { mapRegistry.remove(mapName); } else { - return (NitriteMap) mapRegistry.get(mapName); + return nitriteMap; } } @@ -100,13 +100,13 @@ public NitriteMap openMap(String mapName, Class keyT @Override public void closeMap(String mapName) { // nothing to close as it is volatile map, moreover, - // removing it form registry means loosing the map + // removing it from registry means losing the map } @Override public void closeRTree(String rTreeName) { // nothing to close as it is volatile map, moreover, - // removing it form registry means loosing the map + // removing it from registry means losing the map } @Override diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java index 6ac60382d..8aa7b9657 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalMap.java @@ -64,7 +64,7 @@ public V get(K k) { if (result == null) { result = primary.get(k); if (result instanceof CopyOnWriteArrayList) { - // create a deep copy of the list so that it does not effect the original one + // create a deep copy of the list so that it does not affect the original one List list = deepCopy((CopyOnWriteArrayList) result); backingMap.put(k, (V) list); result = (V) list; diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalRTree.java b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalRTree.java index 1b96895e2..6c077b42b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalRTree.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/TransactionalRTree.java @@ -2,10 +2,14 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.RecordStream; +import org.dizitart.no2.common.util.SpatialKey; import org.dizitart.no2.index.BoundingBox; import org.dizitart.no2.store.NitriteRTree; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * @author Anindya Chatterjee @@ -114,67 +118,4 @@ public void clear() { public void drop() { map.clear(); } - - /* - * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, - * and the EPL 1.0 (https://h2database.com/html/license.html). - * Initial Developer: H2 Group - */ - private static class SpatialKey { - - private final long id; - private final float[] minMax; - - public SpatialKey(long id, float... minMax) { - this.id = id; - this.minMax = minMax; - } - - public float min(int dim) { - return minMax[dim + dim]; - } - - public void setMin(int dim, float x) { - minMax[dim + dim] = x; - } - - public float max(int dim) { - return minMax[dim + dim + 1]; - } - - public void setMax(int dim, float x) { - minMax[dim + dim + 1] = x; - } - - public long getId() { - return id; - } - - public boolean isNull() { - return minMax.length == 0; - } - - @Override - public int hashCode() { - return (int) ((id >>> 32) ^ id); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } else if (!(other instanceof SpatialKey)) { - return false; - } - SpatialKey o = (SpatialKey) other; - if (id != o.id) { - return false; - } - return equalsIgnoringId(o); - } - - public boolean equalsIgnoringId(SpatialKey o) { - return Arrays.equals(minMax, o.minMax); - } - } } diff --git a/nitrite/src/test/java/org/dizitart/no2/store/memory/InMemoryRTreeTest.java b/nitrite/src/test/java/org/dizitart/no2/store/memory/InMemoryRTreeTest.java index 445657d80..5dd54ecab 100644 --- a/nitrite/src/test/java/org/dizitart/no2/store/memory/InMemoryRTreeTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/store/memory/InMemoryRTreeTest.java @@ -1,5 +1,6 @@ package org.dizitart.no2.store.memory; +import org.dizitart.no2.common.util.SpatialKey; import org.junit.Test; import static org.junit.Assert.assertArrayEquals; @@ -19,7 +20,7 @@ public void testSize() { @Test public void testSpatialKeyConstructor() { float[] floatArray = new float[]{10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f}; - new InMemoryRTree.SpatialKey(123L, floatArray); + new SpatialKey(123L, floatArray); assertEquals(8, floatArray.length); assertArrayEquals(new float[]{10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f}, floatArray, 0.0f); } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/ChangeTypeTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/ChangeTypeTest.java index 599c37904..a5ba03e6f 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/ChangeTypeTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/ChangeTypeTest.java @@ -23,14 +23,9 @@ public class ChangeTypeTest { - @Test - public void testValueOf3() { - assertEquals(ChangeType.AddProcessor, ChangeType.valueOf("AddProcessor")); - } - @Test public void testValues() { - assertEquals(12, ChangeType.values().length); + assertEquals(10, ChangeType.values().length); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/NitriteTransactionTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/NitriteTransactionTest.java index 5e4c4c4be..6f5c48d0a 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/NitriteTransactionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/NitriteTransactionTest.java @@ -32,7 +32,7 @@ public void testConstructor() { Nitrite nitrite = mock(Nitrite.class); when(nitrite.getConfig()).thenReturn(new NitriteConfig()); doReturn(new TransactionStore<>(new InMemoryStore())).when(nitrite).getStore(); - assertEquals(State.Active, (new NitriteTransaction(nitrite, new LockService())).getState()); + assertEquals(TransactionState.Active, (new NitriteTransaction(nitrite, new LockService())).getState()); verify(nitrite).getConfig(); verify(nitrite).getStore(); } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/StateTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionStateTest.java similarity index 58% rename from nitrite/src/test/java/org/dizitart/no2/transaction/StateTest.java rename to nitrite/src/test/java/org/dizitart/no2/transaction/TransactionStateTest.java index ba93373cc..a5940c749 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/StateTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/TransactionStateTest.java @@ -21,22 +21,22 @@ import static org.junit.Assert.assertEquals; -public class StateTest { +public class TransactionStateTest { @Test public void testValueOf2() { - assertEquals(State.Aborted, State.valueOf("Aborted")); + assertEquals(TransactionState.Aborted, TransactionState.valueOf("Aborted")); } @Test public void testValues() { - State[] actualValuesResult = State.values(); + TransactionState[] actualValuesResult = TransactionState.values(); assertEquals(6, actualValuesResult.length); - assertEquals(State.Active, actualValuesResult[0]); - assertEquals(State.PartiallyCommitted, actualValuesResult[1]); - assertEquals(State.Committed, actualValuesResult[2]); - assertEquals(State.Closed, actualValuesResult[3]); - assertEquals(State.Failed, actualValuesResult[4]); - assertEquals(State.Aborted, actualValuesResult[5]); + assertEquals(TransactionState.Active, actualValuesResult[0]); + assertEquals(TransactionState.PartiallyCommitted, actualValuesResult[1]); + assertEquals(TransactionState.Committed, actualValuesResult[2]); + assertEquals(TransactionState.Closed, actualValuesResult[3]); + assertEquals(TransactionState.Failed, actualValuesResult[4]); + assertEquals(TransactionState.Aborted, actualValuesResult[5]); } } From fed27ba27b389b470ec148a1a0aa3207f68f232e Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 12 Jul 2022 10:24:07 +0530 Subject: [PATCH 54/78] refactoring --- .../transaction/DefaultTransactionalCollection.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index 057f029c1..e038bfe51 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -513,7 +513,7 @@ public boolean isOpen() { try { close(); } catch (Exception e) { - throw new NitriteIOException("Failed to close the database", e); + throw new NitriteIOException("Failed to close the collection", e); } return false; } else return true; @@ -633,15 +633,15 @@ private void checkOpened() { } if (!primary.isOpen()) { - throw new NitriteIOException("Store is closed"); + throw new TransactionException("Store is closed"); } if (isDropped()) { - throw new NitriteIOException("Collection has been dropped"); + throw new TransactionException("Collection is dropped"); } if (!transactionContext.getActive().get()) { - throw new TransactionException("Transaction is closed"); + throw new TransactionException("Transaction is not active"); } } @@ -657,7 +657,7 @@ private void validateRebuildIndex(IndexDescriptor indexDescriptor) { String[] fieldNames = indexDescriptor.getIndexFields().getFieldNames().toArray(new String[0]); if (isIndexing(fieldNames)) { - throw new IndexingException("Indexing on value " + indexDescriptor.getIndexFields() + " is currently running"); + throw new IndexingException("Indexing on fields " + indexDescriptor.getIndexFields() + " is currently running"); } } } From c419d05b9a97ddb1bbfeaa118c013f66ed6eef5e Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 19 Jul 2022 11:30:28 +0530 Subject: [PATCH 55/78] refactoring --- .../dizitart/no2/rocksdb/RocksDBRTree.java | 155 ++++++++++++++++++ .../dizitart/no2/rocksdb/RocksDBStore.java | 25 ++- .../TransactionCollectionTest.java | 2 +- .../TransactionRepositoryTest.java | 4 +- .../no2/rocksdb/RocksDBStoreTest.java | 16 +- .../no2/exceptions/MigrationException.java | 10 ++ .../dizitart/no2/transaction/ChangeType.java | 21 ++- .../DefaultTransactionalCollection.java | 35 +--- .../TransactionCollectionTest.java | 2 +- .../TransactionRepositoryTest.java | 5 +- 10 files changed, 226 insertions(+), 49 deletions(-) create mode 100644 nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBRTree.java diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBRTree.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBRTree.java new file mode 100644 index 000000000..0c2534059 --- /dev/null +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBRTree.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.rocksdb; + +import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.RecordStream; +import org.dizitart.no2.common.util.SpatialKey; +import org.dizitart.no2.exceptions.InvalidOperationException; +import org.dizitart.no2.index.BoundingBox; +import org.dizitart.no2.store.NitriteRTree; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * @author Anindya Chatterjee + */ +public class RocksDBRTree implements NitriteRTree { + private final RocksDBMap backingMap; + private final AtomicBoolean droppedFlag; + private final AtomicBoolean closedFlag; + + public RocksDBRTree(RocksDBMap backingMap) { + this.backingMap = backingMap; + this.closedFlag = new AtomicBoolean(false); + this.droppedFlag = new AtomicBoolean(false); + } + + @Override + public void add(Key key, NitriteId nitriteId) { + checkOpened(); + if (nitriteId != null && nitriteId.getIdValue() != null) { + SpatialKey spatialKey = getKey(key, Long.parseLong(nitriteId.getIdValue())); + backingMap.put(spatialKey, key); + } + } + + @Override + public void remove(Key key, NitriteId nitriteId) { + checkOpened(); + if (nitriteId != null && nitriteId.getIdValue() != null) { + SpatialKey spatialKey = getKey(key, Long.parseLong(nitriteId.getIdValue())); + backingMap.remove(spatialKey); + } + } + + @Override + public RecordStream findIntersectingKeys(Key key) { + checkOpened(); + SpatialKey spatialKey = getKey(key, 0L); + Set set = new HashSet<>(); + + for (SpatialKey sk : backingMap.keys()) { + if (isOverlap(sk, spatialKey)) { + set.add(NitriteId.createId(Long.toString(sk.getId()))); + } + } + + return RecordStream.fromIterable(set); + } + + @Override + public RecordStream findContainedKeys(Key key) { + checkOpened(); + SpatialKey spatialKey = getKey(key, 0L); + Set set = new HashSet<>(); + + for (SpatialKey sk : backingMap.keys()) { + if (isInside(sk, spatialKey)) { + set.add(NitriteId.createId(Long.toString(sk.getId()))); + } + } + + return RecordStream.fromIterable(set); + } + + @Override + public long size() { + checkOpened(); + return backingMap.size(); + } + + @Override + public void close() { + closedFlag.compareAndSet(false, true); + } + + @Override + public void clear() { + checkOpened(); + backingMap.clear(); + } + + @Override + public void drop() { + checkOpened(); + droppedFlag.compareAndSet(false, true); + backingMap.clear(); + } + + private SpatialKey getKey(Key key, long id) { + return new SpatialKey(id, key.getMinX(), + key.getMaxX(), key.getMinY(), key.getMaxY()); + } + + private boolean isOverlap(SpatialKey a, SpatialKey b) { + if (a.isNull() || b.isNull()) { + return false; + } + for (int i = 0; i < 2; i++) { + if (a.max(i) < b.min(i) || a.min(i) > b.max(i)) { + return false; + } + } + return true; + } + + private boolean isInside(SpatialKey a, SpatialKey b) { + if (a.isNull() || b.isNull()) { + return false; + } + for (int i = 0; i < 2; i++) { + if (a.min(i) <= b.min(i) || a.max(i) >= b.max(i)) { + return false; + } + } + return true; + } + + private void checkOpened() { + if (closedFlag.get()) { + throw new InvalidOperationException("RTreeMap is closed"); + } + + if (droppedFlag.get()) { + throw new InvalidOperationException("RTreeMap is dropped"); + } + } +} diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java index 5a5b35488..7cb929c91 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/RocksDBStore.java @@ -2,8 +2,8 @@ import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.common.UnknownType; +import org.dizitart.no2.common.util.SpatialKey; import org.dizitart.no2.common.util.StringUtils; -import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteException; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.index.BoundingBox; @@ -22,11 +22,14 @@ public class RocksDBStore extends AbstractNitriteStore { private final AtomicBoolean closed; private final Map> nitriteMapRegistry; + + private final Map> nitriteRTreeMapRegistry; private RocksDBReference reference; public RocksDBStore() { super(); nitriteMapRegistry = new ConcurrentHashMap<>(); + nitriteRTreeMapRegistry = new ConcurrentHashMap<>(); closed = new AtomicBoolean(true); } @@ -131,20 +134,34 @@ public void removeMap(String mapName) { } @Override + @SuppressWarnings("unchecked") public NitriteRTree openRTree(String rTreeName, Class keyType, Class valueType) { - throw new InvalidOperationException("Rtree not supported on rocksdb store"); + + if (nitriteRTreeMapRegistry.containsKey(rTreeName)) { + return (RocksDBRTree) nitriteRTreeMapRegistry.get(rTreeName); + } else { + RocksDBMap nitriteMap = new RocksDBMap<>(rTreeName, this, this.reference, + SpatialKey.class, keyType); + RocksDBRTree nitriteRTree = new RocksDBRTree<>(nitriteMap); + nitriteRTreeMapRegistry.put(rTreeName, nitriteRTree); + return nitriteRTree; + } } @Override public void closeRTree(String rTreeName) { - throw new InvalidOperationException("Rtree not supported on rocksdb store"); + if (!StringUtils.isNullOrEmpty(rTreeName)) { + nitriteRTreeMapRegistry.remove(rTreeName); + } } @Override public void removeRTree(String mapName) { - throw new InvalidOperationException("Rtree not supported on rocksdb store"); + reference.dropColumnFamily(mapName); + getCatalog().remove(mapName); + nitriteRTreeMapRegistry.remove(mapName); } @Override diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java index 816ce394e..629b5fac4 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java @@ -431,7 +431,7 @@ public void testCommitDropCollection() { boolean expectedException = false; try { assertEquals(0, txCol.size()); - } catch (NitriteIOException e) { + } catch (TransactionException e) { expectedException = true; } assertTrue(expectedException); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 52e8bc641..c28d890c0 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -328,7 +328,7 @@ public void testRollbackClear() { } catch (TransactionException e) { assert transaction != null; transaction.rollback(); - assertEquals(2, repository.size()); + assertEquals(0, repository.size()); } } } @@ -454,7 +454,7 @@ public void testCommitDropRepository() { boolean expectedException = false; try { assertEquals(0, txRepo.size()); - } catch (NitriteIOException e) { + } catch (TransactionException e) { expectedException = true; } assertTrue(expectedException); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/rocksdb/RocksDBStoreTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/rocksdb/RocksDBStoreTest.java index d34087441..8fa98466b 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/rocksdb/RocksDBStoreTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/rocksdb/RocksDBStoreTest.java @@ -17,11 +17,11 @@ package org.dizitart.no2.rocksdb; -import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteException; import org.junit.Test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.*; public class RocksDBStoreTest { @@ -46,16 +46,20 @@ public void testOpenMap() { RocksDBStore rocksDBStore = new RocksDBStore(); rocksDBStore.setStoreConfig(rocksDBConfig); Class keyType = Object.class; - rocksDBStore.openMap("Map Name", keyType, Object.class); + rocksDBStore.openMap("MapName", keyType, Object.class); verify(rocksDBConfig).objectFormatter(); } - @Test + @Test(expected = NitriteException.class) public void testOpenRTree() { + RocksDBConfig rocksDBConfig = mock(RocksDBConfig.class); + when(rocksDBConfig.objectFormatter()).thenThrow(new NitriteException("An error occurred")); + RocksDBStore rocksDBStore = new RocksDBStore(); + rocksDBStore.setStoreConfig(rocksDBConfig); Class keyType = Object.class; - assertThrows(InvalidOperationException.class, - () -> rocksDBStore.openRTree("R Tree Name", keyType, Object.class)); + rocksDBStore.openRTree("Rtree", keyType, Object.class); + verify(rocksDBConfig).objectFormatter(); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/exceptions/MigrationException.java b/nitrite/src/main/java/org/dizitart/no2/exceptions/MigrationException.java index 2980689dd..d74aa030f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/exceptions/MigrationException.java +++ b/nitrite/src/main/java/org/dizitart/no2/exceptions/MigrationException.java @@ -15,4 +15,14 @@ public class MigrationException extends NitriteException { public MigrationException(String errorMessage) { super(errorMessage); } + + /** + * Instantiates a new Migration exception. + * + * @param errorMessage the error message + * @param cause the cause + */ + public MigrationException(String errorMessage, Throwable cause) { + super(errorMessage, cause); + } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/ChangeType.java b/nitrite/src/main/java/org/dizitart/no2/transaction/ChangeType.java index a29675bf1..d7c296ed1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/ChangeType.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/ChangeType.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.transaction; /** @@ -23,7 +40,7 @@ enum ChangeType { Remove, /** - * Clear. + * Clear. Commit only operation, cannot be rolled back. */ Clear, @@ -48,7 +65,7 @@ enum ChangeType { DropAllIndexes, /** - * Drop collection. + * Drop collection. Commit only operation, cannot be rolled back. */ DropCollection, diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index e038bfe51..2ea3870cf 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -450,19 +450,10 @@ public void clear() { writeLock.unlock(); } - List documentList = new ArrayList<>(); - JournalEntry journalEntry = new JournalEntry(); journalEntry.setChangeType(ChangeType.Clear); - journalEntry.setCommit(() -> { - documentList.addAll(primary.find().toList()); - primary.clear(); - }); - journalEntry.setRollback(() -> { - for (Document document : documentList) { - primary.insert(document); - } - }); + journalEntry.setCommit(primary::clear); + journalEntry.setRollback(() -> {}); // can't be rolled back transactionContext.getJournal().add(journalEntry); } @@ -477,28 +468,10 @@ public void drop() { } isDropped = true; - List documentList = new ArrayList<>(); - List indexEntries = new ArrayList<>(); - JournalEntry journalEntry = new JournalEntry(); journalEntry.setChangeType(ChangeType.DropCollection); - journalEntry.setCommit(() -> { - documentList.addAll(primary.find().toList()); - indexEntries.addAll(primary.listIndices()); - primary.drop(); - }); - journalEntry.setRollback(() -> { - NitriteCollection collection = nitrite.getCollection(collectionName); - - for (IndexDescriptor indexDescriptor : indexEntries) { - String[] fieldNames = indexDescriptor.getIndexFields().getFieldNames().toArray(new String[0]); - collection.createIndex(indexOptions(indexDescriptor.getIndexType()), fieldNames); - } - - for (Document document : documentList) { - collection.insert(document); - } - }); + journalEntry.setCommit(primary::drop); + journalEntry.setRollback(() -> {}); // can't be rolled back transactionContext.getJournal().add(journalEntry); } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java index 816ce394e..629b5fac4 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java @@ -431,7 +431,7 @@ public void testCommitDropCollection() { boolean expectedException = false; try { assertEquals(0, txCol.size()); - } catch (NitriteIOException e) { + } catch (TransactionException e) { expectedException = true; } assertTrue(expectedException); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 2e98a4561..108756957 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -328,7 +328,8 @@ public void testRollbackClear() { } catch (TransactionException e) { assert transaction != null; transaction.rollback(); - assertEquals(2, repository.size()); + // clear/drop can't be rolled back + assertEquals(0, repository.size()); } } } @@ -454,7 +455,7 @@ public void testCommitDropRepository() { boolean expectedException = false; try { assertEquals(0, txRepo.size()); - } catch (NitriteIOException e) { + } catch (TransactionException e) { expectedException = true; } assertTrue(expectedException); From 37a7d7a7ffce6435e8347e3e9a968934cc935ad0 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sun, 24 Jul 2022 23:08:11 +0530 Subject: [PATCH 56/78] refactor --- .../transaction/TransactionRepositoryTest.java | 5 ++--- .../integration/collection/BaseCollectionTest.java | 2 +- .../transaction/TransactionCollectionTest.java | 6 +++--- .../transaction/TransactionRepositoryTest.java | 5 ++--- .../integration/collection/BaseCollectionTest.java | 2 +- .../transaction/TransactionCollectionTest.java | 4 ++-- .../transaction/TransactionRepositoryTest.java | 1 - .../no2/collection/DefaultNitriteCollection.java | 2 +- .../collection/operation/CollectionOperations.java | 5 +++++ .../no2/collection/operation/IndexManager.java | 14 ++++++++++++++ .../no2/collection/operation/IndexOperations.java | 11 +++++++++++ .../java/org/dizitart/no2/index/NitriteIndex.java | 3 +-- .../DefaultTransactionalCollection.java | 10 +++------- .../no2/transaction/NitriteTransaction.java | 6 +++--- .../integration/collection/BaseCollectionTest.java | 2 +- .../integration/collection/CollectionFindTest.java | 2 +- .../transaction/TransactionCollectionTest.java | 4 ++-- .../transaction/TransactionRepositoryTest.java | 1 - .../DefaultTransactionalCollectionTest.java | 5 ++--- 19 files changed, 55 insertions(+), 35 deletions(-) diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 4f4cb996c..d2da4fd6d 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -324,11 +324,10 @@ public void testRollbackClear() { repository.insert(txData2); transaction.commit(); - fail(); } catch (TransactionException e) { assert transaction != null; transaction.rollback(); - assertEquals(2, repository.size()); + assertEquals(0, repository.size()); } } } @@ -454,7 +453,7 @@ public void testCommitDropRepository() { boolean expectedException = false; try { assertEquals(0, txRepo.size()); - } catch (NitriteIOException e) { + } catch (TransactionException e) { expectedException = true; } assertTrue(expectedException); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java index 6898e1169..200d7c873 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java @@ -104,7 +104,7 @@ public void setUp() { .put("lastName", "ln2") .put("birthDay", simpleDateFormat.parse("2010-06-12T16:02:48.440Z")) .put("data", new byte[]{3, 4, 3}) - .put("list", Arrays.asList("three", "four", "three")) + .put("list", Arrays.asList("three", "four", "five")) .put("body", "quick hello world from nitrite"); doc3 = createDocument("firstName", "fn3") .put("lastName", "ln2") diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java index 816ce394e..931f70a14 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java @@ -299,11 +299,11 @@ public void testRollbackClear() { txCol.insert(document2); collection.insert(document2); - throw new TransactionException("failed"); + transaction.commit(); } catch (TransactionException e) { assert transaction != null; transaction.rollback(); - assertEquals(2, collection.size()); + assertEquals(0, collection.size()); } } } @@ -431,7 +431,7 @@ public void testCommitDropCollection() { boolean expectedException = false; try { assertEquals(0, txCol.size()); - } catch (NitriteIOException e) { + } catch (TransactionException e) { expectedException = true; } assertTrue(expectedException); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 52e8bc641..f1107695f 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -324,11 +324,10 @@ public void testRollbackClear() { repository.insert(txData2); transaction.commit(); - fail(); } catch (TransactionException e) { assert transaction != null; transaction.rollback(); - assertEquals(2, repository.size()); + assertEquals(0, repository.size()); } } } @@ -454,7 +453,7 @@ public void testCommitDropRepository() { boolean expectedException = false; try { assertEquals(0, txRepo.size()); - } catch (NitriteIOException e) { + } catch (TransactionException e) { expectedException = true; } assertTrue(expectedException); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java index de25007a2..95243ae84 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java @@ -85,7 +85,7 @@ public void setUp() { .put("lastName", "ln2") .put("birthDay", simpleDateFormat.parse("2010-06-12T16:02:48.440Z")) .put("data", new byte[]{3, 4, 3}) - .put("list", Arrays.asList("three", "four", "three")) + .put("list", Arrays.asList("three", "four", "five")) .put("body", "quick hello world from nitrite"); doc3 = createDocument("firstName", "fn3") .put("lastName", "ln2") diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java index 629b5fac4..931f70a14 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java @@ -299,11 +299,11 @@ public void testRollbackClear() { txCol.insert(document2); collection.insert(document2); - throw new TransactionException("failed"); + transaction.commit(); } catch (TransactionException e) { assert transaction != null; transaction.rollback(); - assertEquals(2, collection.size()); + assertEquals(0, collection.size()); } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index c28d890c0..f1107695f 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -324,7 +324,6 @@ public void testRollbackClear() { repository.insert(txData2); transaction.commit(); - fail(); } catch (TransactionException e) { assert transaction != null; transaction.rollback(); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index 2437f7d80..b9c021f19 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -164,7 +164,7 @@ public void clear() { try { writeLock.lock(); checkOpened(); - nitriteMap.clear(); + collectionOperations.clear(); } finally { writeLock.unlock(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java index 3d91916f2..fb8354ebd 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/CollectionOperations.java @@ -257,6 +257,11 @@ public void close() { nitriteMap.close(); } + public void clear() { + nitriteMap.clear(); + indexOperations.clear(); + } + private void initialize() { this.processorChain = new ProcessorChain(); this.indexOperations = new IndexOperations(collectionName, nitriteConfig, nitriteMap, eventBus); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java index 170665b54..297d60c94 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java @@ -118,6 +118,20 @@ public void close() { } } + public void clearAll() { + // close all index maps + if (!indexMetaMap.isClosed() && !indexMetaMap.isDropped()) { + Iterable indexMetas = indexMetaMap.values(); + for (IndexMeta indexMeta : indexMetas) { + if (indexMeta != null && indexMeta.getIndexDescriptor() != null) { + String indexMapName = indexMeta.getIndexMap(); + NitriteMap indexMap = nitriteStore.openMap(indexMapName, Object.class, Object.class); + indexMap.clear(); + } + } + } + } + /** * Is dirty index boolean. * diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java index 0c2dc7e20..f9de72e22 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java @@ -126,6 +126,17 @@ void dropAllIndices() { this.indexManager = new IndexManager(collectionName, nitriteConfig); } + void clear() { + for (Map.Entry entry : indexBuildTracker.entrySet()) { + if (entry.getValue() != null && entry.getValue().get()) { + throw new IndexingException("Index build already in progress on fields: " + entry.getKey()); + } + } + + indexManager.clearAll(); + indexBuildTracker.clear(); + } + boolean isIndexing(Fields field) { // has an index will only return true, if there is an index on // the value and indexing is not running on it diff --git a/nitrite/src/main/java/org/dizitart/no2/index/NitriteIndex.java b/nitrite/src/main/java/org/dizitart/no2/index/NitriteIndex.java index 870cde373..836a0007a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/NitriteIndex.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/NitriteIndex.java @@ -109,8 +109,7 @@ default List addNitriteIds(List nitriteIds, FieldValues fi nitriteIds = new CopyOnWriteArrayList<>(); } - if (isUnique() && nitriteIds.size() == 1 - && !nitriteIds.contains(fieldValues.getNitriteId())) { + if (isUnique() && nitriteIds.size() == 1) { // if key is already exists for unique type, throw error throw new UniqueConstraintException("Unique key constraint violation for " + fieldValues.getFields()); } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index 2ea3870cf..fd959067d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -3,17 +3,16 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; -import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.*; import org.dizitart.no2.collection.events.CollectionEventInfo; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.collection.operation.CollectionOperations; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.event.EventBus; import org.dizitart.no2.common.event.NitriteEventBus; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.exceptions.*; import org.dizitart.no2.filters.Filter; @@ -45,7 +44,6 @@ class DefaultTransactionalCollection implements NitriteCollection { private final NitriteCollection primary; private final TransactionContext transactionContext; - private final Nitrite nitrite; private String collectionName; private NitriteMap nitriteMap; private NitriteStore nitriteStore; @@ -66,11 +64,9 @@ class DefaultTransactionalCollection implements NitriteCollection { private EventBus, CollectionEventListener> eventBus; public DefaultTransactionalCollection(NitriteCollection primary, - TransactionContext transactionContext, - Nitrite nitrite) { + TransactionContext transactionContext) { this.primary = primary; this.transactionContext = transactionContext; - this.nitrite = nitrite; initialize(); } @@ -445,7 +441,7 @@ public void clear() { try { writeLock.lock(); checkOpened(); - nitriteMap.clear(); + collectionOperations.clear(); } finally { writeLock.unlock(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java index a6807a381..1421ae91a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java @@ -71,7 +71,7 @@ public synchronized NitriteCollection getCollection(String name) { context.setJournal(new LinkedList<>()); context.setConfig(transactionConfig); - NitriteCollection txCollection = new DefaultTransactionalCollection(primary, context, nitrite); + NitriteCollection txCollection = new DefaultTransactionalCollection(primary, context); collectionRegistry.put(name, txCollection); contextMap.put(name, context); return txCollection; @@ -104,7 +104,7 @@ public synchronized ObjectRepository getRepository(Class type) { context.setConfig(transactionConfig); NitriteCollection primaryCollection = primary.getDocumentCollection(); - NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context, nitrite); + NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context); ObjectRepository txRepository = new DefaultTransactionalRepository<>(type, primary, backingCollection, transactionConfig); @@ -141,7 +141,7 @@ public synchronized ObjectRepository getRepository(Class type, String context.setConfig(transactionConfig); NitriteCollection primaryCollection = primary.getDocumentCollection(); - NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context, nitrite); + NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context); ObjectRepository txRepository = new DefaultTransactionalRepository<>(type, primary, backingCollection, transactionConfig); repositoryRegistry.put(name, txRepository); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java index 9dee23797..3278172eb 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/BaseCollectionTest.java @@ -78,7 +78,7 @@ public void setUp() { .put("lastName", "ln2") .put("birthDay", simpleDateFormat.parse("2010-06-12T16:02:48.440Z")) .put("data", new byte[]{3, 4, 3}) - .put("list", Arrays.asList("three", "four", "three")) + .put("list", Arrays.asList("three", "four", "five")) .put("body", "quick hello world from nitrite"); doc3 = createDocument("firstName", "fn3") .put("lastName", "ln2") diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java index 885a05f2c..ac38940b7 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java @@ -457,7 +457,7 @@ public void testFindWithIterableEqual() { new ArrayList() {{ add("three"); add("four"); - add("three"); + add("five"); }})); assertNotNull(ids); assertEquals(ids.size(), 1); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java index 629b5fac4..931f70a14 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionCollectionTest.java @@ -299,11 +299,11 @@ public void testRollbackClear() { txCol.insert(document2); collection.insert(document2); - throw new TransactionException("failed"); + transaction.commit(); } catch (TransactionException e) { assert transaction != null; transaction.rollback(); - assertEquals(2, collection.size()); + assertEquals(0, collection.size()); } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java index 108756957..12251a1b2 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TransactionRepositoryTest.java @@ -324,7 +324,6 @@ public void testRollbackClear() { repository.insert(txData2); transaction.commit(); - fail(); } catch (TransactionException e) { assert transaction != null; transaction.rollback(); diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalCollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalCollectionTest.java index c09fcc21d..332f03377 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalCollectionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/DefaultTransactionalCollectionTest.java @@ -33,7 +33,7 @@ public void testConstructor() { TransactionContext transactionContext = new TransactionContext(); transactionContext.setConfig(transactionConfig); assertThrows(NotIdentifiableException.class, - () -> new DefaultTransactionalCollection(null, transactionContext, null)); + () -> new DefaultTransactionalCollection(null, transactionContext)); verify(transactionConfig).getNitriteStore(); } @@ -47,14 +47,13 @@ public void testConstructor2() { TransactionContext transactionContext = new TransactionContext(); transactionContext.setConfig(transactionConfig); DefaultTransactionalCollection actualDefaultTransactionalCollection = new DefaultTransactionalCollection(null, - transactionContext, null); + transactionContext); assertNull(actualDefaultTransactionalCollection.getCollectionName()); assertFalse(actualDefaultTransactionalCollection.isDropped()); TransactionContext transactionContext1 = actualDefaultTransactionalCollection.getTransactionContext(); assertSame(transactionContext, transactionContext1); assertSame(transactionStore, actualDefaultTransactionalCollection.getStore()); assertNull(actualDefaultTransactionalCollection.getPrimary()); - assertNull(actualDefaultTransactionalCollection.getNitrite()); assertNull(actualDefaultTransactionalCollection.getNitriteMap()); assertNull(actualDefaultTransactionalCollection.getCollectionOperations().getAttributes()); verify(transactionConfig, times(2)).getNitriteStore(); From a3eee5a0ed3b3f6c85c55336965598a4baa65238 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 25 Jul 2022 11:30:39 +0530 Subject: [PATCH 57/78] fix tests --- .../src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java | 2 ++ .../dizitart/no2/integration/collection/CollectionFindTest.java | 2 +- .../dizitart/no2/integration/collection/CollectionFindTest.java | 2 +- .../src/test/kotlin/org/dizitart/kno2/TransactionTest.kt | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java index cc24dc112..9aee397b0 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java @@ -212,6 +212,8 @@ private static void testForMigration(MVStore store) { MVStore.TxCounter txCounter = store.registerVersionUsage(); MVMap metaMap = store.openMap(META_MAP_NAME); try { + // fire one operation to trigger compatibility issue + // if no exception thrown, then the database is compatible metaMap.remove("MigrationTest"); } catch (IllegalStateException e) { store.close(); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java index f5e5a2c04..7dedc072f 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java @@ -420,7 +420,7 @@ public void testFindWithIterableEqual() { new ArrayList() {{ add("three"); add("four"); - add("three"); + add("five"); }})); assertNotNull(ids); assertEquals(ids.size(), 1); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java index f5e5a2c04..7dedc072f 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java @@ -420,7 +420,7 @@ public void testFindWithIterableEqual() { new ArrayList() {{ add("three"); add("four"); - add("three"); + add("five"); }})); assertNotNull(ids); assertEquals(ids.size(), 1); diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/TransactionTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/TransactionTest.kt index b6f174100..82b8f3966 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/TransactionTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/TransactionTest.kt @@ -485,7 +485,7 @@ class TransactionTest: BaseTest() { var expectedException = false try { Assert.assertEquals(0, txCol.size()) - } catch (e: NitriteIOException) { + } catch (e: TransactionException) { expectedException = true } Assert.assertTrue(expectedException) From 9af0586a0bc84833686a20882db7643cf55bffb5 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 25 Jul 2022 13:40:14 +0530 Subject: [PATCH 58/78] dependency upgraded --- build.gradle | 1 - nitrite-android-example/build.gradle | 12 +++---- nitrite-bom/build.gradle | 8 ++--- nitrite-jackson-mapper/build.gradle | 15 ++++---- nitrite-mvstore-adapter/build.gradle | 34 ++++++++++--------- nitrite-replication/build.gradle | 32 ++++++++++------- nitrite-rocksdb-adapter/build.gradle | 34 ++++++++++--------- nitrite-spatial/build.gradle | 4 +-- nitrite-support/build.gradle | 4 +-- nitrite/build.gradle | 32 +++++++++-------- .../org/dizitart/no2/NitriteConfigTest.java | 6 ---- .../no2/common/util/ObjectUtilsTest.java | 22 ++---------- .../dizitart/no2/store/StoreCatalogTest.java | 12 ++----- .../no2/transaction/JournalEntryTest.java | 6 ---- potassium-nitrite/build.gradle | 13 ++++--- 15 files changed, 108 insertions(+), 127 deletions(-) diff --git a/build.gradle b/build.gradle index c85914883..c88137a39 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,6 @@ buildscript { } dependencies { classpath "io.freefair.gradle:lombok-plugin:6.3.0" - classpath "org.asciidoctor:asciidoctor-gradle-plugin:1.6.1" classpath "net.ltgt.gradle:gradle-errorprone-plugin:2.0.2" classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0" classpath "com.adarshr:gradle-test-logger-plugin:3.1.0" diff --git a/nitrite-android-example/build.gradle b/nitrite-android-example/build.gradle index 008fb4204..f70835ce9 100644 --- a/nitrite-android-example/build.gradle +++ b/nitrite-android-example/build.gradle @@ -87,15 +87,15 @@ dependencies { implementation project(path: ':nitrite-mvstore-adapter', configuration: 'default') implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'androidx.appcompat:appcompat:1.4.2' implementation 'androidx.multidex:multidex:2.0.1' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - implementation 'com.google.android.material:material:1.4.0' - annotationProcessor "org.projectlombok:lombok:1.18.22" + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'com.google.android.material:material:1.6.1' + annotationProcessor 'org.projectlombok:lombok:1.18.24' testImplementation 'junit:junit:4.13.2' - testImplementation 'org.mockito:mockito-core:4.1.0' - testAnnotationProcessor "org.projectlombok:lombok:1.18.20" + testImplementation 'org.mockito:mockito-core:4.6.1' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.24' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } diff --git a/nitrite-bom/build.gradle b/nitrite-bom/build.gradle index 15a765ed5..8570bd8c1 100644 --- a/nitrite-bom/build.gradle +++ b/nitrite-bom/build.gradle @@ -24,10 +24,10 @@ dependencies { api "org.slf4j:slf4j-api:1.7.32" api "org.objenesis:objenesis:2.6" api "org.jasypt:jasypt:1.9.3" - api "com.fasterxml.jackson.core:jackson-databind:2.13.0" - api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.1" + api "com.fasterxml.jackson.core:jackson-databind:2.13.3" + api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3" api "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.3" - api "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.0" + api "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3" api "org.mapdb:mapdb:3.0.8" api "com.h2database:h2-mvstore:1.4.200" api "com.squareup.okhttp3:okhttp:4.9.3" @@ -35,7 +35,7 @@ dependencies { api "com.esotericsoftware.kryo:kryo5:5.2.1" api "org.locationtech.jts:jts-core:1.18.2" api "commons-codec:commons-codec:1.15" - api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0" + api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21" api "org.jetbrains.kotlin:kotlin-reflect:1.6.21" } } diff --git a/nitrite-jackson-mapper/build.gradle b/nitrite-jackson-mapper/build.gradle index 29b1f918c..71a45c19d 100644 --- a/nitrite-jackson-mapper/build.gradle +++ b/nitrite-jackson-mapper/build.gradle @@ -47,18 +47,21 @@ dependencies { api "org.slf4j:slf4j-api" api "com.fasterxml.jackson.core:jackson-databind" - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation project(path: ':nitrite-mvstore-adapter', configuration: 'default') - testAnnotationProcessor "org.projectlombok:lombok:1.18.20" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation "junit:junit:4.13.2" testImplementation "org.locationtech.jts:jts-core:1.18.2" - testImplementation "org.awaitility:awaitility:4.1.1" - testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.14.1" - testImplementation "org.apache.logging.log4j:log4j-core:2.15.0" - testImplementation "com.github.javafaker:javafaker:1.0.2" + testImplementation "org.awaitility:awaitility:4.2.0" + testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.17.2" + testImplementation "org.apache.logging.log4j:log4j-core:2.17.2" + testImplementation ("com.github.javafaker:javafaker:1.0.2") { + exclude module: 'snakeyaml' + } testImplementation platform("com.fasterxml.jackson:jackson-bom:2.13.0") testImplementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" + testImplementation 'org.yaml:snakeyaml:1.30' } test { diff --git a/nitrite-mvstore-adapter/build.gradle b/nitrite-mvstore-adapter/build.gradle index 9d14111d0..073084468 100644 --- a/nitrite-mvstore-adapter/build.gradle +++ b/nitrite-mvstore-adapter/build.gradle @@ -46,26 +46,28 @@ dependencies { api project(':nitrite') api "org.slf4j:slf4j-api" api "com.h2database:h2-mvstore" - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" - testAnnotationProcessor "org.projectlombok:lombok:1.18.20" - testImplementation "uk.co.jemos.podam:podam:7.2.7.RELEASE" - testImplementation "com.github.javafaker:javafaker:1.0.2" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" + testImplementation "uk.co.jemos.podam:podam:7.2.9.RELEASE" + testImplementation ("com.github.javafaker:javafaker:1.0.2") { + exclude module: 'snakeyaml' + } testImplementation "junit:junit:4.13.2" - testImplementation "org.mockito:mockito-core:4.1.0" - testImplementation "org.apache.lucene:lucene-core:8.11.0" - testImplementation "org.apache.lucene:lucene-analyzers-common:8.11.0" - testImplementation "org.apache.lucene:lucene-queryparser:8.11.0" - testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.14.1" - testImplementation "org.apache.logging.log4j:log4j-core:2.15.0" - testImplementation "org.awaitility:awaitility:4.1.1" - testImplementation "joda-time:joda-time:2.10.13" + testImplementation "org.mockito:mockito-core:4.6.1" + testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.17.2" + testImplementation "org.apache.logging.log4j:log4j-core:2.17.2" + testImplementation "org.awaitility:awaitility:4.2.0" + testImplementation "joda-time:joda-time:2.10.14" testImplementation "org.meanbean:meanbean:2.0.3" - testImplementation "com.fasterxml.jackson.core:jackson-databind:2.13.0" + testImplementation ("com.fasterxml.jackson.core:jackson-databind:2.13.3") { + exclude module: 'org.yaml' + } testImplementation "commons-io:commons-io:2.11.0" - testImplementation "com.google.guava:guava:31.0.1-jre" - testImplementation "jakarta.xml.bind:jakarta.xml.bind-api:3.0.1" - testImplementation "com.sun.xml.bind:jaxb-impl:3.0.2" + testImplementation 'com.google.guava:guava:31.1-jre' + testImplementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.0' + testImplementation 'com.sun.xml.bind:jaxb-impl:4.0.0' + testImplementation 'org.yaml:snakeyaml:1.30' } test { diff --git a/nitrite-replication/build.gradle b/nitrite-replication/build.gradle index d060dc78e..ab06cf8d8 100644 --- a/nitrite-replication/build.gradle +++ b/nitrite-replication/build.gradle @@ -48,23 +48,29 @@ dependencies { api "org.slf4j:slf4j-api" api "com.fasterxml.jackson.core:jackson-databind" api "com.squareup.okhttp3:okhttp" - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation project(path: ':nitrite-mvstore-adapter', configuration: 'default') - testAnnotationProcessor "org.projectlombok:lombok:1.18.20" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation "jakarta.websocket:jakarta.websocket-api:2.0.0" - testImplementation "org.glassfish.tyrus:tyrus-server:2.0.1" - testImplementation "org.glassfish.tyrus:tyrus-container-grizzly-server:2.0.1" - testImplementation "org.awaitility:awaitility:4.1.1" - testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.14.1" - testImplementation "org.apache.logging.log4j:log4j-core:2.15.0" + testImplementation 'org.glassfish.tyrus:tyrus-server:2.0.2' + testImplementation 'org.glassfish.tyrus:tyrus-container-grizzly-server:2.0.2' + testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.2' + testImplementation 'org.apache.logging.log4j:log4j-core:2.17.2' testImplementation "junit:junit:4.13.2" - testImplementation "org.testcontainers:testcontainers:1.16.0" - testImplementation 'org.testcontainers:mongodb:1.16.0' - testImplementation 'org.mongodb:mongo-java-driver:3.12.10' - testImplementation 'commons-lang:commons-lang:1.0.1' - testImplementation 'com.github.javafaker:javafaker:1.0.2' - testImplementation "com.palantir.docker.compose:docker-compose-rule-junit4:1.7.0" + testImplementation "org.testcontainers:testcontainers:1.17.2" + testImplementation 'org.testcontainers:mongodb:1.17.2' + testImplementation 'org.mongodb:mongo-java-driver:3.12.11' + testImplementation 'commons-lang:commons-lang:2.6' + testImplementation ("com.github.javafaker:javafaker:1.0.2") { + exclude module: 'snakeyaml' + } + testImplementation ("com.palantir.docker.compose:docker-compose-rule-junit4:1.7.0") { + exclude module: 'guava' + } + testImplementation 'org.yaml:snakeyaml:1.30' + testImplementation 'com.google.guava:guava:31.1-jre' } test { diff --git a/nitrite-rocksdb-adapter/build.gradle b/nitrite-rocksdb-adapter/build.gradle index cbe151413..6b3d6318b 100644 --- a/nitrite-rocksdb-adapter/build.gradle +++ b/nitrite-rocksdb-adapter/build.gradle @@ -34,26 +34,28 @@ dependencies { exclude group: "org.objenesis" } - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" - testAnnotationProcessor "org.projectlombok:lombok:1.18.20" - testImplementation "uk.co.jemos.podam:podam:7.2.7.RELEASE" - testImplementation "com.github.javafaker:javafaker:1.0.2" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" + testImplementation "uk.co.jemos.podam:podam:7.2.9.RELEASE" + testImplementation ("com.github.javafaker:javafaker:1.0.2") { + exclude module: 'snakeyaml' + } testImplementation "junit:junit:4.13.2" - testImplementation "org.mockito:mockito-core:4.1.0" - testImplementation "org.apache.lucene:lucene-core:8.11.0" - testImplementation "org.apache.lucene:lucene-analyzers-common:8.11.0" - testImplementation "org.apache.lucene:lucene-queryparser:8.11.0" - testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.14.1" - testImplementation "org.apache.logging.log4j:log4j-core:2.15.0" - testImplementation "org.awaitility:awaitility:4.1.1" - testImplementation "joda-time:joda-time:2.10.13" + testImplementation "org.mockito:mockito-core:4.6.1" + testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.17.2" + testImplementation "org.apache.logging.log4j:log4j-core:2.17.2" + testImplementation "org.awaitility:awaitility:4.2.0" + testImplementation "joda-time:joda-time:2.10.14" testImplementation "org.meanbean:meanbean:2.0.3" - testImplementation "com.fasterxml.jackson.core:jackson-databind:2.13.0" + testImplementation ("com.fasterxml.jackson.core:jackson-databind:2.13.3") { + exclude module: 'org.yaml' + } testImplementation "commons-io:commons-io:2.11.0" - testImplementation "com.google.guava:guava:31.0.1-jre" - testImplementation "jakarta.xml.bind:jakarta.xml.bind-api:3.0.1" - testImplementation "com.sun.xml.bind:jaxb-impl:3.0.2" + testImplementation 'com.google.guava:guava:31.1-jre' + testImplementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.0' + testImplementation 'com.sun.xml.bind:jaxb-impl:4.0.0' + testImplementation 'org.yaml:snakeyaml:1.30' } test { diff --git a/nitrite-spatial/build.gradle b/nitrite-spatial/build.gradle index b40a66772..6a743b422 100644 --- a/nitrite-spatial/build.gradle +++ b/nitrite-spatial/build.gradle @@ -48,9 +48,9 @@ dependencies { api "org.slf4j:slf4j-api" api "org.locationtech.jts:jts-core" - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" - testAnnotationProcessor "org.projectlombok:lombok:1.18.20" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation project(path: ':nitrite-mvstore-adapter', configuration: 'default') testImplementation project(path: ':nitrite-jackson-mapper', configuration: 'default') testImplementation "junit:junit:4.13.2" diff --git a/nitrite-support/build.gradle b/nitrite-support/build.gradle index 5adece14b..b2761db7e 100644 --- a/nitrite-support/build.gradle +++ b/nitrite-support/build.gradle @@ -47,9 +47,9 @@ dependencies { api "com.fasterxml.jackson.core:jackson-databind" api "commons-codec:commons-codec" - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" - testAnnotationProcessor "org.projectlombok:lombok:1.18.20" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation project(path: ':nitrite-mvstore-adapter', configuration: 'default') testImplementation "junit:junit:4.13.2" } diff --git a/nitrite/build.gradle b/nitrite/build.gradle index 4904c276d..8a01b0e5a 100644 --- a/nitrite/build.gradle +++ b/nitrite/build.gradle @@ -46,25 +46,27 @@ dependencies { api "org.slf4j:slf4j-api" api "org.objenesis:objenesis" api "org.jasypt:jasypt" - annotationProcessor "org.projectlombok:lombok:1.18.22" + annotationProcessor "org.projectlombok:lombok:1.18.24" - testAnnotationProcessor "org.projectlombok:lombok:1.18.20" + testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation "junit:junit:4.13.2" - testImplementation "org.mockito:mockito-core:4.1.0" - testImplementation "uk.co.jemos.podam:podam:7.2.7.RELEASE" - testImplementation "com.github.javafaker:javafaker:1.0.2" - testImplementation "org.apache.lucene:lucene-core:8.11.0" - testImplementation "org.apache.lucene:lucene-analyzers-common:8.11.0" - testImplementation "org.apache.lucene:lucene-queryparser:8.11.0" - testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.14.1" - testImplementation "org.apache.logging.log4j:log4j-core:2.15.0" - testImplementation "org.awaitility:awaitility:4.1.1" - testImplementation "joda-time:joda-time:2.10.13" + testImplementation "org.mockito:mockito-core:4.6.1" + testImplementation 'uk.co.jemos.podam:podam:7.2.9.RELEASE' + testImplementation ("com.github.javafaker:javafaker:1.0.2") { + exclude module: 'snakeyaml' + } + testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.17.2" + testImplementation "org.apache.logging.log4j:log4j-core:2.17.2" + testImplementation "org.awaitility:awaitility:4.2.0" + testImplementation 'joda-time:joda-time:2.10.14' testImplementation "org.meanbean:meanbean:2.0.3" - testImplementation "com.fasterxml.jackson.core:jackson-databind:2.13.0" + testImplementation ("com.fasterxml.jackson.core:jackson-databind:2.13.3") { + exclude module: 'org.yaml' + } testImplementation "commons-io:commons-io:2.11.0" - testImplementation "jakarta.xml.bind:jakarta.xml.bind-api:3.0.1" - testImplementation "com.sun.xml.bind:jaxb-impl:3.0.2" + testImplementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.0' + testImplementation 'com.sun.xml.bind:jaxb-impl:4.0.0' + testImplementation 'org.yaml:snakeyaml:1.30' } test { diff --git a/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java b/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java index 0da47a27a..deeea8545 100644 --- a/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java @@ -47,12 +47,6 @@ public void testConstructor() { @Test public void testFieldSeparator() { - // TODO: This test is incomplete. - // Reason: No meaningful assertions found. - // To help Diffblue Cover to find assertions, please add getters to the - // class under test that return fields written by the method under test. - // See https://diff.blue/R004 - (new NitriteConfig()).fieldSeparator("Separator"); } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java index 8a6fea0db..c4f43b629 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java @@ -21,15 +21,13 @@ import lombok.Data; import org.apache.commons.lang3.mutable.MutableByte; import org.apache.commons.lang3.mutable.MutableDouble; -import org.apache.lucene.analysis.CharArraySet; -import org.apache.lucene.analysis.StopFilter; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.exceptions.ValidationException; -import org.dizitart.no2.repository.annotations.Entity; -import org.dizitart.no2.repository.annotations.Index; import org.dizitart.no2.integration.repository.data.ChildClass; import org.dizitart.no2.integration.repository.data.Employee; +import org.dizitart.no2.repository.annotations.Entity; +import org.dizitart.no2.repository.annotations.Index; import org.junit.Test; import java.io.Serializable; @@ -67,13 +65,6 @@ public void testDeepEquals() { assertFalse(ObjectUtils.deepEquals("o1", null)); } - @Test - public void testDeepEquals2() { - CharArraySet makeStopSetResult = StopFilter.makeStopSet(new String[]{"foo", "foo", "foo"}, true); - makeStopSetResult.add((Object) "foo"); - assertFalse(ObjectUtils.deepEquals(makeStopSetResult, new AnnotatedMethodMap())); - } - @Test public void testDeepEquals3() { MutableByte o1 = new MutableByte(); @@ -92,15 +83,6 @@ public void testDeepEquals5() { assertTrue(ObjectUtils.deepEquals(o1, new MutableByte())); } - @Test - public void testDeepEquals6() { - CharArraySet makeStopSetResult = StopFilter.makeStopSet(new String[]{"foo", "foo", "foo"}, true); - makeStopSetResult.add((Object) "foo"); - CharArraySet makeStopSetResult1 = StopFilter.makeStopSet(new String[]{"foo", "foo", "foo"}, true); - makeStopSetResult1.add((Object) "foo"); - assertTrue(ObjectUtils.deepEquals(makeStopSetResult, makeStopSetResult1)); - } - @Test public void testDeepEquals7() { AnnotatedMethodMap o1 = new AnnotatedMethodMap(); diff --git a/nitrite/src/test/java/org/dizitart/no2/store/StoreCatalogTest.java b/nitrite/src/test/java/org/dizitart/no2/store/StoreCatalogTest.java index 025b41aa1..c16a665e4 100644 --- a/nitrite/src/test/java/org/dizitart/no2/store/StoreCatalogTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/store/StoreCatalogTest.java @@ -17,12 +17,12 @@ package org.dizitart.no2.store; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import org.dizitart.no2.store.memory.InMemoryStore; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + public class StoreCatalogTest { @Test public void testConstructor() { @@ -69,12 +69,6 @@ public void testGetKeyedRepositoryNames() { @Test public void testRemove() { - // TODO: This test is incomplete. - // Reason: No meaningful assertions found. - // To help Diffblue Cover to find assertions, please add getters to the - // class under test that return fields written by the method under test. - // See https://diff.blue/R004 - (new StoreCatalog(new InMemoryStore())).remove("Name"); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/transaction/JournalEntryTest.java b/nitrite/src/test/java/org/dizitart/no2/transaction/JournalEntryTest.java index 831a9e6dc..8aa076efc 100644 --- a/nitrite/src/test/java/org/dizitart/no2/transaction/JournalEntryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/transaction/JournalEntryTest.java @@ -110,12 +110,6 @@ public void testHashCode() { @Test public void testHashCode2() { - // TODO: This test is incomplete. - // Reason: No meaningful assertions found. - // To help Diffblue Cover to find assertions, please add getters to the - // class under test that return fields written by the method under test. - // See https://diff.blue/R004 - (new JournalEntry(ChangeType.Insert, mock(Command.class), mock(Command.class))).hashCode(); } } diff --git a/potassium-nitrite/build.gradle b/potassium-nitrite/build.gradle index c210d6b28..7fa336727 100644 --- a/potassium-nitrite/build.gradle +++ b/potassium-nitrite/build.gradle @@ -57,11 +57,14 @@ dependencies { testApi project(':nitrite-mvstore-adapter') testImplementation "junit:junit:4.13.2" - testImplementation "joda-time:joda-time:2.10.13" - testImplementation "org.threeten:threetenbp:1.5.2" - testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.14.1" - testImplementation "org.apache.logging.log4j:log4j-core:2.15.0" - testImplementation "com.github.javafaker:javafaker:1.0.2" + testImplementation "joda-time:joda-time:2.10.14" + testImplementation 'org.threeten:threetenbp:1.6.0' + testImplementation "org.apache.logging.log4j:log4j-slf4j-impl:2.17.2" + testImplementation "org.apache.logging.log4j:log4j-core:2.17.2" + testImplementation ("com.github.javafaker:javafaker:1.0.2") { + exclude module: 'snakeyaml' + } + testImplementation 'org.yaml:snakeyaml:1.30' } compileKotlin { From ec6e952cbe13ff57186d1c8186ac63df7a2629e9 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 26 Jul 2022 18:37:59 +0530 Subject: [PATCH 59/78] h2 upgrade strategy --- .travis.yml | 2 +- gradle.properties | 2 +- nitrite-bom/build.gradle | 3 +- .../dizitart/no2/mvstore/MVSpatialKey.java | 44 + .../dizitart/no2/mvstore/MVStoreConfig.java | 19 + .../dizitart/no2/mvstore/MVStoreUtils.java | 54 +- .../no2/mvstore/NitriteMVRTreeMap.java | 23 +- .../org/dizitart/no2/mvstore/Recovery.java | 4 +- .../no2/mvstore/compat/{v3 => v1}/Compat.java | 2 +- .../compat/{v3 => v1}/MVMapBuilder.java | 9 +- .../compat/{v3 => v1}/NitriteDataType.java | 13 +- .../{v3 => v1}/NitriteObjectInputStream.java | 2 +- .../UpgradeUtil.java} | 92 +- .../no2/mvstore/compat/v1/mvstore/Chunk.java | 481 +++ .../no2/mvstore/compat/v1/mvstore/Cursor.java | 163 + .../mvstore/compat/v1/mvstore/CursorPos.java | 86 + .../mvstore/compat/v1/mvstore/DataUtils.java | 1125 ++++++ .../mvstore/compat/v1/mvstore/FileStore.java | 437 ++ .../compat/v1/mvstore/FreeSpaceBitSet.java | 341 ++ .../no2/mvstore/compat/v1/mvstore/MVMap.java | 2095 ++++++++++ .../mvstore/compat/v1/mvstore/MVStore.java | 3588 +++++++++++++++++ .../no2/mvstore/compat/v1/mvstore/Page.java | 1538 +++++++ .../compat/v1/mvstore/RootReference.java | 263 ++ .../compat/v1/mvstore/SysProperties.java | 170 + .../compat/v1/mvstore/WriteBuffer.java | 333 ++ .../v1/mvstore/cache/CacheLongKeyLIRS.java | 1215 ++++++ .../v1/mvstore/cache/FilePathCache.java | 182 + .../v1/mvstore/compress/CompressDeflate.java | 84 + .../v1/mvstore/compress/CompressLZF.java | 272 ++ .../v1/mvstore/compress/Compressor.java | 21 + .../compat/v1/mvstore/fs/FileBase.java | 82 + .../v1/mvstore/fs/FileChannelInputStream.java | 56 + .../mvstore/fs/FileChannelOutputStream.java | 44 + .../compat/v1/mvstore/fs/FileDisk.java | 96 + .../mvstore/compat/v1/mvstore/fs/FileNio.java | 90 + .../compat/v1/mvstore/fs/FilePath.java | 161 + .../compat/v1/mvstore/fs/FilePathDisk.java | 368 ++ .../compat/v1/mvstore/fs/FilePathEncrypt.java | 441 ++ .../compat/v1/mvstore/fs/FilePathNio.java | 22 + .../compat/v1/mvstore/fs/FilePathWrapper.java | 133 + .../compat/v1/mvstore/fs/FileUtils.java | 178 + .../compat/v1/mvstore/type/DataType.java | 72 + .../v1/mvstore/type/ObjectDataType.java | 1552 +++++++ .../v1/mvstore/type/StringDataType.java | 57 + .../mvstore/compat/v1/mvstore/util/Bits.java | 138 + .../compat/v1/mvstore/util/IOUtils.java | 273 ++ .../v1/mvstore/util/SortedProperties.java | 164 + .../compat/v1/mvstore/util/StringUtils.java | 912 +++++ .../mvstore/compat/v3/MVMapBuilderTest.java | 32 - .../compat/v3/NitriteDataTypeTest.java | 754 ---- nitrite/src/main/resources/version | 2 +- 51 files changed, 17416 insertions(+), 874 deletions(-) create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVSpatialKey.java rename nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/{v3 => v1}/Compat.java (98%) rename nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/{v3 => v1}/MVMapBuilder.java (80%) rename nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/{v3 => v1}/NitriteDataType.java (99%) rename nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/{v3 => v1}/NitriteObjectInputStream.java (98%) rename nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/{v3/MigrationUtil.java => v1/UpgradeUtil.java} (75%) create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Chunk.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Cursor.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/CursorPos.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/DataUtils.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/FileStore.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/FreeSpaceBitSet.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/MVMap.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/MVStore.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Page.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/RootReference.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/SysProperties.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/WriteBuffer.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/cache/CacheLongKeyLIRS.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/cache/FilePathCache.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/CompressDeflate.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/CompressLZF.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/Compressor.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileBase.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileChannelInputStream.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileChannelOutputStream.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileDisk.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileNio.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePath.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathDisk.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathEncrypt.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathNio.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathWrapper.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileUtils.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/DataType.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/ObjectDataType.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/StringDataType.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/Bits.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/IOUtils.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/SortedProperties.java create mode 100644 nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/StringUtils.java delete mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/compat/v3/MVMapBuilderTest.java delete mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataTypeTest.java diff --git a/.travis.yml b/.travis.yml index 094cf0a11..056b9c803 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,5 +28,5 @@ after_success: env: global: - - NITRITE_VERSION=4.0.1-SNAPSHOT + - NITRITE_VERSION=4.1.0-SNAPSHOT - PGP_KEY_FILE=~/secring.gpg \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 0f8e3ec96..9215ee6b0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,6 +17,6 @@ org.gradle.jvmargs=-Xmx1024m org.gradle.parallel=false #android.enableSeparateAnnotationProcessing=true # artifact version -nitriteVersion=4.0.1-SNAPSHOT +nitriteVersion=4.1.0-SNAPSHOT android.useAndroidX=true android.enableJetifier=true diff --git a/nitrite-bom/build.gradle b/nitrite-bom/build.gradle index 8570bd8c1..cacfebf09 100644 --- a/nitrite-bom/build.gradle +++ b/nitrite-bom/build.gradle @@ -28,8 +28,7 @@ dependencies { api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3" api "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.3" api "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3" - api "org.mapdb:mapdb:3.0.8" - api "com.h2database:h2-mvstore:1.4.200" + api "com.h2database:h2-mvstore:2.1.214" api "com.squareup.okhttp3:okhttp:4.9.3" api "org.rocksdb:rocksdbjni:7.2.2" api "com.esotericsoftware.kryo:kryo5:5.2.1" diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVSpatialKey.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVSpatialKey.java new file mode 100644 index 000000000..c7fc552b1 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVSpatialKey.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.mvstore; + +import org.dizitart.no2.common.util.SpatialKey; +import org.h2.mvstore.rtree.Spatial; + +import java.util.Arrays; + +/** + * @author Anindya Chatterjee + */ +public class MVSpatialKey extends SpatialKey implements Spatial { + private final float[] minMax; + public MVSpatialKey(long id, float... minMax) { + super(id, minMax); + this.minMax = minMax; + } + + @Override + public Spatial clone(long id) { + return new MVSpatialKey(id, this.minMax.clone()); + } + + @Override + public boolean equalsIgnoringId(Spatial o) { + return Arrays.equals(minMax, ((MVSpatialKey)o).minMax); + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreConfig.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreConfig.java index 310a5ac0a..1a2d6bc66 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreConfig.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreConfig.java @@ -86,4 +86,23 @@ public class MVStoreConfig implements StoreConfig { public void addStoreEventListener(StoreEventListener listener) { eventListeners.add(listener); } + + public MVStoreConfig clone() { + MVStoreConfig config = new MVStoreConfig(); + config.eventListeners(new HashSet<>(eventListeners)); + config.filePath(filePath); + config.autoCommitBufferSize(autoCommitBufferSize); + config.encryptionKey(encryptionKey); + config.isReadOnly(isReadOnly); + config.compress(compress); + config.compressHigh(compressHigh); + config.autoCommit(autoCommit); + config.autoCompact(autoCompact); + config.recoveryMode(recoveryMode); + config.cacheSize(cacheSize); + config.cacheConcurrency(cacheConcurrency); + config.pageSplitSize(pageSplitSize); + config.fileStore(fileStore); + return config; + } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java index 9aee397b0..bfaf8c280 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/MVStoreUtils.java @@ -20,9 +20,10 @@ import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteIOException; -import org.dizitart.no2.mvstore.compat.v3.MigrationUtil; +import org.dizitart.no2.mvstore.compat.v1.UpgradeUtil; import org.h2.mvstore.MVMap; import org.h2.mvstore.MVStore; +import org.h2.mvstore.MVStoreException; import java.io.File; @@ -31,12 +32,13 @@ import static org.dizitart.no2.common.util.StringUtils.isNullOrEmpty; /** - * @since 4.0.0 * @author Anindya Chatterjee. + * @since 4.0.0 */ @Slf4j class MVStoreUtils { - private MVStoreUtils() { } + private MVStoreUtils() { + } static MVStore openOrCreate(MVStoreConfig storeConfig) { MVStore.Builder builder = createBuilder(storeConfig); @@ -46,32 +48,33 @@ static MVStore openOrCreate(MVStoreConfig storeConfig) { try { store = builder.open(); testForMigration(store); - } catch (IllegalStateException ise) { - if (ise.getMessage().contains("file is locked")) { + } catch (MVStoreException me) { + if (me.getMessage().contains("file is locked")) { throw new NitriteIOException("Database is already opened in other process"); } if (dbFile != null) { try { if (dbFile.isDirectory()) { - throw new NitriteIOException(storeConfig.filePath() - + " is a directory, must be a file"); + throw new NitriteIOException(storeConfig.filePath() + " is a directory, must be a file"); } if (dbFile.exists() && dbFile.isFile()) { - if (isCompatibilityError(ise)) { + if (isCompatibilityError(me)) { if (store != null) { store.closeImmediately(); } - store = tryMigrate(dbFile, builder, storeConfig); + + // try upgrading the database + store = tryUpgrade(dbFile, storeConfig); } else { - log.error("Database corruption detected. Trying to repair", ise); + log.error("Database corruption detected. Trying to repair", me); Recovery.recover(storeConfig.filePath()); store = builder.open(); } } else { if (storeConfig.isReadOnly()) { - throw new NitriteIOException("Cannot create readonly database", ise); + throw new NitriteIOException("Cannot create readonly database", me); } } } catch (InvalidOperationException | NitriteIOException ex) { @@ -80,7 +83,7 @@ static MVStore openOrCreate(MVStoreConfig storeConfig) { throw new NitriteIOException("Database file is corrupted", e); } } else { - throw new NitriteIOException("Unable to create in-memory database", ise); + throw new NitriteIOException("Unable to create in-memory database", me); } } catch (IllegalArgumentException iae) { if (dbFile != null) { @@ -100,10 +103,8 @@ static MVStore openOrCreate(MVStoreConfig storeConfig) { return store; } - private static boolean isCompatibilityError(IllegalStateException ise) { - return ise.getCause() != null - && ise.getCause().getCause() instanceof ClassNotFoundException - && ise.getCause().getCause().getMessage().contains("org.dizitart.no2"); + private static boolean isCompatibilityError(Exception e) { + return e.getMessage().contains("The write format 1 is smaller than the supported format"); } private static MVStore.Builder createBuilder(MVStoreConfig mvStoreConfig) { @@ -167,24 +168,17 @@ private static MVStore.Builder createBuilder(MVStoreConfig mvStoreConfig) { return builder; } - private static MVStore tryMigrate(File orgFile, MVStore.Builder builder, MVStoreConfig storeConfig) { - log.info("Migrating old database format to new database format"); - - // open old store with builder - MVStore oldMvStore = builder.open(); - + private static MVStore tryUpgrade(File orgFile, MVStoreConfig storeConfig) { // create new store with builder File newFile = new File(orgFile.getPath() + "_new"); - storeConfig.filePath(newFile.getPath()); - MVStore.Builder newBuilder = createBuilder(storeConfig); - MVStore newMvStore = newBuilder.open(); + MVStoreConfig newStoreConfig = storeConfig.clone(); + newStoreConfig.filePath(newFile.getPath()); + MVStore.Builder newBuilder = createBuilder(newStoreConfig); - // migrate 2 stores maps - MigrationUtil.migrate(newMvStore, oldMvStore); - switchFiles(newFile, orgFile); + UpgradeUtil.tryUpgrade(newBuilder, storeConfig); - // open new store calling openOrCreate and return - storeConfig.filePath(orgFile.getPath()); + // switch the file + switchFiles(newFile, orgFile); return openOrCreate(storeConfig); } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVRTreeMap.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVRTreeMap.java index b8c373abb..aa0a111ea 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVRTreeMap.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/NitriteMVRTreeMap.java @@ -23,7 +23,6 @@ import org.dizitart.no2.store.NitriteStore; import org.h2.mvstore.MVStore; import org.h2.mvstore.rtree.MVRTreeMap; -import org.h2.mvstore.rtree.SpatialKey; import java.util.Iterator; @@ -45,7 +44,7 @@ class NitriteMVRTreeMap implements NitriteRTree< @Override public void add(Key key, NitriteId nitriteId) { if (nitriteId != null && nitriteId.getIdValue() != null) { - SpatialKey spatialKey = getKey(key, Long.parseLong(nitriteId.getIdValue())); + MVSpatialKey spatialKey = getKey(key, Long.parseLong(nitriteId.getIdValue())); MVStore.TxCounter txCounter = mvStore.registerVersionUsage(); try { mvMap.add(spatialKey, key); @@ -58,7 +57,7 @@ public void add(Key key, NitriteId nitriteId) { @Override public void remove(Key key, NitriteId nitriteId) { if (nitriteId != null && nitriteId.getIdValue() != null) { - SpatialKey spatialKey = getKey(key, Long.parseLong(nitriteId.getIdValue())); + MVSpatialKey spatialKey = getKey(key, Long.parseLong(nitriteId.getIdValue())); MVStore.TxCounter txCounter = mvStore.registerVersionUsage(); try { mvMap.remove(spatialKey); @@ -70,15 +69,15 @@ public void remove(Key key, NitriteId nitriteId) { @Override public RecordStream findIntersectingKeys(Key key) { - SpatialKey spatialKey = getKey(key, 0L); - MVRTreeMap.RTreeCursor treeCursor = mvMap.findIntersectingKeys(spatialKey); + MVSpatialKey spatialKey = getKey(key, 0L); + MVRTreeMap.RTreeCursor treeCursor = mvMap.findIntersectingKeys(spatialKey); return getRecordStream(treeCursor); } @Override public RecordStream findContainedKeys(Key key) { - SpatialKey spatialKey = getKey(key, 0L); - MVRTreeMap.RTreeCursor treeCursor = mvMap.findContainedKeys(spatialKey); + MVSpatialKey spatialKey = getKey(key, 0L); + MVRTreeMap.RTreeCursor treeCursor = mvMap.findContainedKeys(spatialKey); return getRecordStream(treeCursor); } @@ -87,16 +86,16 @@ public long size() { return mvMap.sizeAsLong(); } - private SpatialKey getKey(Key key, long id) { + private MVSpatialKey getKey(Key key, long id) { if (key == null) { - return new SpatialKey(id); + return new MVSpatialKey(id); } else { - return new SpatialKey(id, key.getMinX(), + return new MVSpatialKey(id, key.getMinX(), key.getMaxX(), key.getMinY(), key.getMaxY()); } } - private RecordStream getRecordStream(MVRTreeMap.RTreeCursor treeCursor) { + private RecordStream getRecordStream(MVRTreeMap.RTreeCursor treeCursor) { return RecordStream.fromIterable(() -> new Iterator() { @Override public boolean hasNext() { @@ -105,7 +104,7 @@ public boolean hasNext() { @Override public NitriteId next() { - SpatialKey next = treeCursor.next(); + MVSpatialKey next = (MVSpatialKey) treeCursor.next(); return NitriteId.createId(Long.toString(next.getId())); } }); diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java index 358184810..ce9b79b23 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/Recovery.java @@ -248,11 +248,11 @@ private static Chunk readChunkHeader(ByteBuffer buff, long start) { } } catch (Exception e) { // there could be various reasons - throw DataUtils.newIllegalStateException( + throw DataUtils.newMVStoreException( DataUtils.ERROR_FILE_CORRUPT, "File corrupt reading chunk at position {0}", start, e); } - throw DataUtils.newIllegalStateException( + throw DataUtils.newMVStoreException( DataUtils.ERROR_FILE_CORRUPT, "File corrupt reading chunk at position {0}", start); } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/Compat.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/Compat.java similarity index 98% rename from nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/Compat.java rename to nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/Compat.java index a0b2d8393..9fee00913 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/Compat.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/Compat.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.dizitart.no2.mvstore.compat.v3; +package org.dizitart.no2.mvstore.compat.v1; import lombok.Data; diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MVMapBuilder.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/MVMapBuilder.java similarity index 80% rename from nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MVMapBuilder.java rename to nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/MVMapBuilder.java index b83a8c8b6..8f376e7d5 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MVMapBuilder.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/MVMapBuilder.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package org.dizitart.no2.mvstore.compat.v3; +package org.dizitart.no2.mvstore.compat.v1; -import org.h2.mvstore.MVMap; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.MVMap; /** * The type Mv map builder. @@ -32,7 +33,7 @@ class MVMapBuilder extends MVMap.Builder { * Instantiates a new Mv map builder. */ public MVMapBuilder() { - setKeyType(new org.dizitart.no2.mvstore.compat.v3.NitriteDataType()); - setValueType(new org.dizitart.no2.mvstore.compat.v3.NitriteDataType()); + setKeyType(new NitriteDataType()); + setValueType(new NitriteDataType()); } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataType.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/NitriteDataType.java similarity index 99% rename from nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataType.java rename to nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/NitriteDataType.java index 4ec5b385c..b4932cd05 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataType.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/NitriteDataType.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package org.dizitart.no2.mvstore.compat.v3; +package org.dizitart.no2.mvstore.compat.v1; -import org.h2.mvstore.DataUtils; -import org.h2.mvstore.WriteBuffer; -import org.h2.mvstore.type.DataType; -import org.h2.mvstore.type.ObjectDataType; -import org.h2.mvstore.type.StringDataType; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.DataUtils; +import org.dizitart.no2.mvstore.compat.v1.mvstore.WriteBuffer; +import org.dizitart.no2.mvstore.compat.v1.mvstore.type.DataType; +import org.dizitart.no2.mvstore.compat.v1.mvstore.type.ObjectDataType; +import org.dizitart.no2.mvstore.compat.v1.mvstore.type.StringDataType; import org.h2.util.Utils; import java.io.ByteArrayInputStream; diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteObjectInputStream.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/NitriteObjectInputStream.java similarity index 98% rename from nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteObjectInputStream.java rename to nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/NitriteObjectInputStream.java index fc20a7793..d04a328f3 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/NitriteObjectInputStream.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/NitriteObjectInputStream.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.dizitart.no2.mvstore.compat.v3; +package org.dizitart.no2.mvstore.compat.v1; import lombok.extern.slf4j.Slf4j; diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/UpgradeUtil.java similarity index 75% rename from nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java rename to nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/UpgradeUtil.java index b85aaa5e2..3d3db1743 100644 --- a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v3/MigrationUtil.java +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/UpgradeUtil.java @@ -1,34 +1,38 @@ /* - * Copyright (c) 2019-2020. Nitrite author or authors. + * Copyright (c) 2017-2022 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.mvstore.compat.v3; +package org.dizitart.no2.mvstore.compat.v1; +import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.DBNull; +import org.dizitart.no2.common.DBValue; import org.dizitart.no2.common.Fields; +import org.dizitart.no2.common.meta.Attributes; +import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ValidationException; -import org.dizitart.no2.common.DBValue; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexMeta; +import org.dizitart.no2.mvstore.MVStoreConfig; +import org.dizitart.no2.mvstore.compat.v1.mvstore.MVMap; +import org.dizitart.no2.mvstore.compat.v1.mvstore.MVStore; import org.dizitart.no2.store.UserCredential; -import org.h2.mvstore.MVMap; -import org.h2.mvstore.MVStore; import java.util.*; import java.util.concurrent.ConcurrentSkipListSet; @@ -37,45 +41,47 @@ import static org.dizitart.no2.common.Constants.INDEX_PREFIX; import static org.dizitart.no2.common.Constants.STORE_INFO; import static org.dizitart.no2.common.util.ObjectUtils.convertToObjectArray; +import static org.dizitart.no2.common.util.StringUtils.isNullOrEmpty; /** - * An utility class to migrate the. - * - * @since 4.0.0 * @author Anindya Chatterjee */ -public class MigrationUtil { - - /** - * Migrate an old 3.x compatible store to new 4.x compatible store. - * - * @param newStore the new store - * @param oldStore the old store - */ - @SuppressWarnings({"rawtypes"}) - public static void migrate(MVStore newStore, MVStore oldStore) { +@Slf4j +public class UpgradeUtil { + private UpgradeUtil() { + } + + public static void tryUpgrade(org.h2.mvstore.MVStore.Builder newBuilder, MVStoreConfig oldStoreConfig) { + log.info("Upgrading old database format to new database format"); + + MVStore.Builder oldBuilder = createBuilder(oldStoreConfig); + try (MVStore oldStore = oldBuilder.open()) { + try (org.h2.mvstore.MVStore newStore = newBuilder.open()) { + upgrade(newStore, oldStore); + } + } + } + + private static void upgrade(org.h2.mvstore.MVStore newStore, MVStore oldStore) { try { validateOldStore(oldStore); Set mapNames = oldStore.getMapNames(); for (String mapName : mapNames) { - MVMap oldMap = oldStore.openMap(mapName, new MVMapBuilder<>()); - MVMap newMap = newStore.openMap(mapName); + MVMap oldMap = oldStore.openMap(mapName, new MVMapBuilder<>()); + org.h2.mvstore.MVMap newMap = newStore.openMap(mapName); copyData(oldMap, newMap); } oldStore.commit(); newStore.commit(); } catch (Throwable t) { - throw new NitriteIOException("Migration of old data has failed", t); - } finally { - oldStore.close(); - newStore.close(); + throw new NitriteIOException("Upgrade of old database has failed", t); } } @SuppressWarnings({"unchecked", "rawtypes"}) - private static void copyData(MVMap oldMap, MVMap newMap) { + private static void copyData(MVMap oldMap, org.h2.mvstore.MVMap newMap) { if (oldMap != null) { Set entrySet = oldMap.entrySet(); for (Map.Entry entry : entrySet) { @@ -223,4 +229,36 @@ private static void validateOldStore(MVStore store) { throw new ValidationException("Database file is corrupted"); } } + + private static MVStore.Builder createBuilder(MVStoreConfig mvStoreConfig) { + MVStore.Builder builder = new MVStore.Builder(); + + // auto compact disabled github issue #41 + builder.autoCompactFillRate(0); + + if (!isNullOrEmpty(mvStoreConfig.filePath())) { + builder = builder.fileName(mvStoreConfig.filePath()); + } + + if (!mvStoreConfig.autoCommit()) { + builder = builder.autoCommitDisabled(); + } + + if (mvStoreConfig.autoCommitBufferSize() > 0) { + builder = builder.autoCommitBufferSize(mvStoreConfig.autoCommitBufferSize()); + } + + if (mvStoreConfig.isReadOnly()) { + if (isNullOrEmpty(mvStoreConfig.filePath())) { + throw new InvalidOperationException("Unable create readonly in-memory database"); + } + builder = builder.readOnly(); + } + + if (mvStoreConfig.compress()) { + builder = builder.compress(); + } + + return builder; + } } diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Chunk.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Chunk.java new file mode 100644 index 000000000..569c6f246 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Chunk.java @@ -0,0 +1,481 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Comparator; +import java.util.HashMap; + +/** + * A chunk of data, containing one or multiple pages. + *

+ * Chunks are page aligned (each page is usually 4096 bytes). + * There are at most 67 million (2^26) chunks, + * each chunk is at most 2 GB large. + */ +public class Chunk +{ + + /** + * The maximum chunk id. + */ + public static final int MAX_ID = (1 << 26) - 1; + + /** + * The maximum length of a chunk header, in bytes. + */ + static final int MAX_HEADER_LENGTH = 1024; + + /** + * The length of the chunk footer. The longest footer is: + * chunk:ffffffff,block:ffffffffffffffff, + * version:ffffffffffffffff,fletcher:ffffffff + */ + static final int FOOTER_LENGTH = 128; + + private static final String ATTR_CHUNK = "chunk"; + private static final String ATTR_BLOCK = "block"; + private static final String ATTR_LEN = "len"; + private static final String ATTR_MAP = "map"; + private static final String ATTR_MAX = "max"; + private static final String ATTR_NEXT = "next"; + private static final String ATTR_PAGES = "pages"; + private static final String ATTR_ROOT = "root"; + private static final String ATTR_TIME = "time"; + private static final String ATTR_VERSION = "version"; + private static final String ATTR_LIVE_MAX = "liveMax"; + private static final String ATTR_LIVE_PAGES = "livePages"; + private static final String ATTR_UNUSED = "unused"; + private static final String ATTR_UNUSED_AT_VERSION = "unusedAtVersion"; + private static final String ATTR_PIN_COUNT = "pinCount"; + private static final String ATTR_FLETCHER = "fletcher"; + + /** + * The chunk id. + */ + public final int id; + + /** + * The start block number within the file. + */ + public volatile long block; + + /** + * The length in number of blocks. + */ + public int len; + + /** + * The total number of pages in this chunk. + */ + int pageCount; + + /** + * The number of pages still alive. + */ + int pageCountLive; + + /** + * The sum of the max length of all pages. + */ + public long maxLen; + + /** + * The sum of the max length of all pages that are in use. + */ + public long maxLenLive; + + /** + * The garbage collection priority. Priority 0 means it needs to be + * collected, a high value means low priority. + */ + int collectPriority; + + /** + * The position of the meta root. + */ + long metaRootPos; + + /** + * The version stored in this chunk. + */ + public long version; + + /** + * When this chunk was created, in milliseconds after the store was created. + */ + public long time; + + /** + * When this chunk was no longer needed, in milliseconds after the store was + * created. After this, the chunk is kept alive a bit longer (in case it is + * referenced in older versions). + */ + public long unused; + + /** + * Version of the store at which chunk become unused and therefore can be + * considered "dead" and collected after this version is no longer in use. + */ + long unusedAtVersion; + + /** + * The last used map id. + */ + public int mapId; + + /** + * The predicted position of the next chunk. + */ + public long next; + + /** + * Number of live pinned pages. + */ + private int pinCount; + + + Chunk(int id) { + this.id = id; + } + + /** + * Read the header from the byte buffer. + * + * @param buff the source buffer + * @param start the start of the chunk in the file + * @return the chunk + */ + static Chunk readChunkHeader(ByteBuffer buff, long start) { + int pos = buff.position(); + byte[] data = new byte[Math.min(buff.remaining(), MAX_HEADER_LENGTH)]; + buff.get(data); + try { + for (int i = 0; i < data.length; i++) { + if (data[i] == '\n') { + // set the position to the start of the first page + buff.position(pos + i + 1); + String s = new String(data, 0, i, StandardCharsets.ISO_8859_1).trim(); + return fromString(s); + } + } + } catch (Exception e) { + // there could be various reasons + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_CORRUPT, + "File corrupt reading chunk at position {0}", start, e); + } + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_CORRUPT, + "File corrupt reading chunk at position {0}", start); + } + + /** + * Write the chunk header. + * + * @param buff the target buffer + * @param minLength the minimum length + */ + void writeChunkHeader(WriteBuffer buff, int minLength) { + long delimiterPosition = buff.position() + minLength - 1; + buff.put(asString().getBytes(StandardCharsets.ISO_8859_1)); + while (buff.position() < delimiterPosition) { + buff.put((byte) ' '); + } + if (minLength != 0 && buff.position() > delimiterPosition) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_INTERNAL, + "Chunk metadata too long"); + } + buff.put((byte) '\n'); + } + + /** + * Get the metadata key for the given chunk id. + * + * @param chunkId the chunk id + * @return the metadata key + */ + static String getMetaKey(int chunkId) { + return ATTR_CHUNK + "." + Integer.toHexString(chunkId); + } + + /** + * Build a block from the given string. + * + * @param s the string + * @return the block + */ + public static Chunk fromString(String s) { + HashMap map = DataUtils.parseMap(s); + int id = DataUtils.readHexInt(map, ATTR_CHUNK, 0); + Chunk c = new Chunk(id); + c.block = DataUtils.readHexLong(map, ATTR_BLOCK, 0); + c.len = DataUtils.readHexInt(map, ATTR_LEN, 0); + c.pageCount = DataUtils.readHexInt(map, ATTR_PAGES, 0); + c.pageCountLive = DataUtils.readHexInt(map, ATTR_LIVE_PAGES, c.pageCount); + c.mapId = DataUtils.readHexInt(map, ATTR_MAP, 0); + c.maxLen = DataUtils.readHexLong(map, ATTR_MAX, 0); + c.maxLenLive = DataUtils.readHexLong(map, ATTR_LIVE_MAX, c.maxLen); + c.metaRootPos = DataUtils.readHexLong(map, ATTR_ROOT, 0); + c.time = DataUtils.readHexLong(map, ATTR_TIME, 0); + c.unused = DataUtils.readHexLong(map, ATTR_UNUSED, 0); + c.unusedAtVersion = DataUtils.readHexLong(map, ATTR_UNUSED_AT_VERSION, 0); + c.version = DataUtils.readHexLong(map, ATTR_VERSION, id); + c.next = DataUtils.readHexLong(map, ATTR_NEXT, 0); + c.pinCount = DataUtils.readHexInt(map, ATTR_PIN_COUNT, 0); + return c; + } + + /** + * Calculate the fill rate in %. 0 means empty, 100 means full. + * + * @return the fill rate + */ + int getFillRate() { + assert maxLenLive <= maxLen : maxLenLive + " > " + maxLen; + if (maxLenLive <= 0) { + return 0; + } else if (maxLenLive == maxLen) { + return 100; + } + return 1 + (int) (98 * maxLenLive / maxLen); + } + + @Override + public int hashCode() { + return id; + } + + @Override + public boolean equals(Object o) { + return o instanceof Chunk && ((Chunk) o).id == id; + } + + /** + * Get the chunk data as a string. + * + * @return the string + */ + public String asString() { + StringBuilder buff = new StringBuilder(240); + DataUtils.appendMap(buff, ATTR_CHUNK, id); + DataUtils.appendMap(buff, ATTR_BLOCK, block); + DataUtils.appendMap(buff, ATTR_LEN, len); + if (maxLen != maxLenLive) { + DataUtils.appendMap(buff, ATTR_LIVE_MAX, maxLenLive); + } + if (pageCount != pageCountLive) { + DataUtils.appendMap(buff, ATTR_LIVE_PAGES, pageCountLive); + } + DataUtils.appendMap(buff, ATTR_MAP, mapId); + DataUtils.appendMap(buff, ATTR_MAX, maxLen); + if (next != 0) { + DataUtils.appendMap(buff, ATTR_NEXT, next); + } + DataUtils.appendMap(buff, ATTR_PAGES, pageCount); + DataUtils.appendMap(buff, ATTR_ROOT, metaRootPos); + DataUtils.appendMap(buff, ATTR_TIME, time); + if (unused != 0) { + DataUtils.appendMap(buff, ATTR_UNUSED, unused); + } + if (unusedAtVersion != 0) { + DataUtils.appendMap(buff, ATTR_UNUSED_AT_VERSION, unusedAtVersion); + } + DataUtils.appendMap(buff, ATTR_VERSION, version); + DataUtils.appendMap(buff, ATTR_PIN_COUNT, pinCount); + return buff.toString(); + } + + byte[] getFooterBytes() { + StringBuilder buff = new StringBuilder(FOOTER_LENGTH); + DataUtils.appendMap(buff, ATTR_CHUNK, id); + DataUtils.appendMap(buff, ATTR_BLOCK, block); + DataUtils.appendMap(buff, ATTR_VERSION, version); + byte[] bytes = buff.toString().getBytes(StandardCharsets.ISO_8859_1); + int checksum = DataUtils.getFletcher32(bytes, 0, bytes.length); + DataUtils.appendMap(buff, ATTR_FLETCHER, checksum); + while (buff.length() < FOOTER_LENGTH - 1) { + buff.append(' '); + } + buff.append('\n'); + return buff.toString().getBytes(StandardCharsets.ISO_8859_1); + } + + boolean isSaved() { + return block != Long.MAX_VALUE; + } + + boolean isLive() { + return pageCountLive > 0; + } + + boolean isRewritable() { + return isSaved() + && isLive() + && pageCountLive < pageCount // not fully occupied + && isEvacuatable(); + } + + private boolean isEvacuatable() { + return pinCount == 0; + } + + /** + * Read a page of data into a ByteBuffer. + * + * @param fileStore to use + * @param pos page pos + * @param expectedMapId expected map id for the page + * @return ByteBuffer containing page data. + */ + ByteBuffer readBufferForPage(FileStore fileStore, long pos, int expectedMapId) { + assert isSaved() : this; + while (true) { + long originalBlock = block; + try { + long filePos = originalBlock * MVStore.BLOCK_SIZE; + long maxPos = filePos + len * MVStore.BLOCK_SIZE; + filePos += DataUtils.getPageOffset(pos); + if (filePos < 0) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_CORRUPT, + "Negative position {0}; p={1}, c={2}", filePos, pos, toString()); + } + + int length = DataUtils.getPageMaxLength(pos); + if (length == DataUtils.PAGE_LARGE) { + // read the first bytes to figure out actual length + length = fileStore.readFully(filePos, 128).getInt(); + } + length = (int) Math.min(maxPos - filePos, length); + if (length < 0) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_CORRUPT, + "Illegal page length {0} reading at {1}; max pos {2} ", length, filePos, maxPos); + } + + ByteBuffer buff = fileStore.readFully(filePos, length); + + int offset = DataUtils.getPageOffset(pos); + int start = buff.position(); + int remaining = buff.remaining(); + int pageLength = buff.getInt(); + if (pageLength > remaining || pageLength < 4) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_CORRUPT, + "File corrupted in chunk {0}, expected page length 4..{1}, got {2}", id, remaining, + pageLength); + } + buff.limit(start + pageLength); + + short check = buff.getShort(); + int checkTest = DataUtils.getCheckValue(id) + ^ DataUtils.getCheckValue(offset) + ^ DataUtils.getCheckValue(pageLength); + if (check != (short) checkTest) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_CORRUPT, + "File corrupted in chunk {0}, expected check value {1}, got {2}", id, checkTest, check); + } + + int mapId = DataUtils.readVarInt(buff); + if (mapId != expectedMapId) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_CORRUPT, + "File corrupted in chunk {0}, expected map id {1}, got {2}", id, expectedMapId, mapId); + } + + if (originalBlock == block) { + return buff; + } + } catch (IllegalStateException ex) { + if (originalBlock == block) { + throw ex; + } + } + } + } + + /** + * Modifies internal state to reflect the fact that one more page is stored + * within this chunk. + * + * @param pageLengthOnDisk + * size of the page + * @param singleWriter + * indicates whether page belongs to append mode capable map + * (single writer map). Such pages are "pinned" to the chunk, + * they can't be evacuated (moved to a different chunk) while + * on-line, but they assumed to be short-lived anyway. + */ + void accountForWrittenPage(int pageLengthOnDisk, boolean singleWriter) { + maxLen += pageLengthOnDisk; + pageCount++; + maxLenLive += pageLengthOnDisk; + pageCountLive++; + if (singleWriter) { + pinCount++; + } + } + + /** + * Modifies internal state to reflect the fact that one the pages within + * this chunk was removed from the map. + * + * @param pageLength + * on disk of the removed page + * @param pinned + * whether removed page was pinned + * @param now + * is a moment in time (since creation of the store), when + * removal is recorded, and retention period starts + * @param version + * at which page was removed + * @return true if all of the pages, this chunk contains, were already + * removed, and false otherwise + */ + boolean accountForRemovedPage(int pageLength, boolean pinned, long now, long version) { + assert isSaved() : this; + maxLenLive -= pageLength; + pageCountLive--; + if (pinned) { + pinCount--; + } + + if (unusedAtVersion < version) { + unusedAtVersion = version; + } + + assert pinCount >= 0 : this; + assert pageCountLive >= 0 : this; + assert pinCount <= pageCountLive : this; + assert maxLenLive >= 0 : this; + assert (pageCountLive == 0) == (maxLenLive == 0) : this; + + if (!isLive()) { + assert isEvacuatable() : this; + unused = now; + return true; + } + return false; + } + + @Override + public String toString() { + return asString(); + } + + + public static final class PositionComparator implements Comparator { + public static final Comparator INSTANCE = new PositionComparator(); + + private PositionComparator() {} + + @Override + public int compare(Chunk one, Chunk two) { + return Long.compare(one.block, two.block); + } + } +} + diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Cursor.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Cursor.java new file mode 100644 index 000000000..80c96c8e0 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Cursor.java @@ -0,0 +1,163 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A cursor to iterate over elements in ascending order. + * + * @param the key type + * @param the value type + */ +public class Cursor implements Iterator { + private final K to; + private CursorPos cursorPos; + private CursorPos keeper; + private K current; + private K last; + private V lastValue; + private Page lastPage; + + public Cursor(Page root, K from) { + this(root, from, null); + } + + public Cursor(Page root, K from, K to) { + this.cursorPos = traverseDown(root, from); + this.to = to; + } + + @Override + @SuppressWarnings("unchecked") + public boolean hasNext() { + if (cursorPos != null) { + while (current == null) { + Page page = cursorPos.page; + int index = cursorPos.index; + if (index >= (page.isLeaf() ? page.getKeyCount() : page.map.getChildPageCount(page))) { + CursorPos tmp = cursorPos; + cursorPos = cursorPos.parent; + tmp.parent = keeper; + keeper = tmp; + if(cursorPos == null) + { + return false; + } + } else { + while (!page.isLeaf()) { + page = page.getChildPage(index); + if (keeper == null) { + cursorPos = new CursorPos(page, 0, cursorPos); + } else { + CursorPos tmp = keeper; + keeper = keeper.parent; + tmp.parent = cursorPos; + tmp.page = page; + tmp.index = 0; + cursorPos = tmp; + } + index = 0; + } + if (index < page.getKeyCount()) { + K key = (K) page.getKey(index); + if (to != null && page.map.getKeyType().compare(key, to) > 0) { + return false; + } + current = last = key; + lastValue = (V) page.getValue(index); + lastPage = page; + } + } + ++cursorPos.index; + } + } + return current != null; + } + + @Override + public K next() { + if(!hasNext()) { + throw new NoSuchElementException(); + } + current = null; + return last; + } + + /** + * Get the last read key if there was one. + * + * @return the key or null + */ + public K getKey() { + return last; + } + + /** + * Get the last read value if there was one. + * + * @return the value or null + */ + public V getValue() { + return lastValue; + } + + /** + * Get the page where last retrieved key is located. + * + * @return the page + */ + Page getPage() { + return lastPage; + } + + /** + * Skip over that many entries. This method is relatively fast (for this map + * implementation) even if many entries need to be skipped. + * + * @param n the number of entries to skip + */ + public void skip(long n) { + if (n < 10) { + while (n-- > 0 && hasNext()) { + next(); + } + } else if(hasNext()) { + assert cursorPos != null; + CursorPos cp = cursorPos; + CursorPos parent; + while ((parent = cp.parent) != null) cp = parent; + Page root = cp.page; + @SuppressWarnings("unchecked") + MVMap map = (MVMap) root.map; + long index = map.getKeyIndex(next()); + last = map.getKey(index + n); + this.cursorPos = traverseDown(root, last); + } + } + + @Override + public void remove() { + throw DataUtils.newUnsupportedOperationException( + "Removal is not supported"); + } + + /** + * Fetch the next entry that is equal or larger than the given key, starting + * from the given page. This method retains the stack. + * + * @param p the page to start from + * @param key the key to search, null means search for the first key + */ + private static CursorPos traverseDown(Page p, Object key) { + CursorPos cursorPos = key == null ? p.getPrependCursorPos(null) : CursorPos.traverseDown(p, key); + if (cursorPos.index < 0) { + cursorPos.index = -cursorPos.index - 1; + } + return cursorPos; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/CursorPos.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/CursorPos.java new file mode 100644 index 000000000..ca4bd35b8 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/CursorPos.java @@ -0,0 +1,86 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +/** + * A position in a cursor. + * Instance represents a node in the linked list, which traces path + * from a specific (target) key within a leaf node all the way up to te root + * (bottom up path). + */ +public class CursorPos +{ + /** + * The page at the current level. + */ + public Page page; + + /** + * Index of the key (within page above) used to go down to a lower level + * in case of intermediate nodes, or index of the target key for leaf a node. + * In a later case, it could be negative, if the key is not present. + */ + public int index; + + /** + * Next node in the linked list, representing the position within parent level, + * or null, if we are at the root level already. + */ + public CursorPos parent; + + + public CursorPos(Page page, int index, CursorPos parent) { + this.page = page; + this.index = index; + this.parent = parent; + } + + /** + * Searches for a given key and creates a breadcrumb trail through a B-tree + * rooted at a given Page. Resulting path starts at "insertion point" for a + * given key and goes back to the root. + * + * @param page root of the tree + * @param key the key to search for + * @return head of the CursorPos chain (insertion point) + */ + public static CursorPos traverseDown(Page page, Object key) { + CursorPos cursorPos = null; + while (!page.isLeaf()) { + int index = page.binarySearch(key) + 1; + if (index < 0) { + index = -index; + } + cursorPos = new CursorPos(page, index, cursorPos); + page = page.getChildPage(index); + } + return new CursorPos(page, page.binarySearch(key), cursorPos); + } + + /** + * Calculate the memory used by changes that are not yet stored. + * + * @param version the version + * @return the amount of memory + */ + int processRemovalInfo(long version) { + int unsavedMemory = 0; + for (CursorPos head = this; head != null; head = head.parent) { + unsavedMemory += head.page.removePage(version); + } + return unsavedMemory; + } + + @Override + public String toString() { + return "CursorPos{" + + "page=" + page + + ", index=" + index + + ", parent=" + parent + + '}'; + } +} + diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/DataUtils.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/DataUtils.java new file mode 100644 index 000000000..2d41e6fd3 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/DataUtils.java @@ -0,0 +1,1125 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.util.StringUtils; +import org.h2.engine.Constants; + +import java.io.EOFException; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Utility methods + */ +public final class DataUtils { + + /** + * An error occurred while reading from the file. + */ + public static final int ERROR_READING_FAILED = 1; + + /** + * An error occurred when trying to write to the file. + */ + public static final int ERROR_WRITING_FAILED = 2; + + /** + * An internal error occurred. This could be a bug, or a memory corruption + * (for example caused by out of memory). + */ + public static final int ERROR_INTERNAL = 3; + + /** + * The object is already closed. + */ + public static final int ERROR_CLOSED = 4; + + /** + * The file format is not supported. + */ + public static final int ERROR_UNSUPPORTED_FORMAT = 5; + + /** + * The file is corrupt or (for encrypted files) the encryption key is wrong. + */ + public static final int ERROR_FILE_CORRUPT = 6; + + /** + * The file is locked. + */ + public static final int ERROR_FILE_LOCKED = 7; + + /** + * An error occurred when serializing or de-serializing. + */ + public static final int ERROR_SERIALIZATION = 8; + + /** + * The application was trying to read data from a chunk that is no longer + * available. + */ + public static final int ERROR_CHUNK_NOT_FOUND = 9; + + /** + * The block in the stream store was not found. + */ + public static final int ERROR_BLOCK_NOT_FOUND = 50; + + /** + * The transaction store is corrupt. + */ + public static final int ERROR_TRANSACTION_CORRUPT = 100; + + /** + * An entry is still locked by another transaction. + */ + public static final int ERROR_TRANSACTION_LOCKED = 101; + + /** + * There are too many open transactions. + */ + public static final int ERROR_TOO_MANY_OPEN_TRANSACTIONS = 102; + + /** + * The transaction store is in an illegal state (for example, not yet + * initialized). + */ + public static final int ERROR_TRANSACTION_ILLEGAL_STATE = 103; + + /** + * The transaction contains too many changes. + */ + public static final int ERROR_TRANSACTION_TOO_BIG = 104; + + /** + * Deadlock discovered and one of transactions involved chosen as victim and rolled back. + */ + public static final int ERROR_TRANSACTIONS_DEADLOCK = 105; + + /** + * The type for leaf page. + */ + public static final int PAGE_TYPE_LEAF = 0; + + /** + * The type for node page. + */ + public static final int PAGE_TYPE_NODE = 1; + + /** + * The bit mask for compressed pages (compression level fast). + */ + public static final int PAGE_COMPRESSED = 2; + + /** + * The bit mask for compressed pages (compression level high). + */ + public static final int PAGE_COMPRESSED_HIGH = 2 + 4; + + /** + * The maximum length of a variable size int. + */ + public static final int MAX_VAR_INT_LEN = 5; + + /** + * The maximum length of a variable size long. + */ + public static final int MAX_VAR_LONG_LEN = 10; + + /** + * The maximum integer that needs less space when using variable size + * encoding (only 3 bytes instead of 4). + */ + public static final int COMPRESSED_VAR_INT_MAX = 0x1fffff; + + /** + * The maximum long that needs less space when using variable size + * encoding (only 7 bytes instead of 8). + */ + public static final long COMPRESSED_VAR_LONG_MAX = 0x1ffffffffffffL; + + /** + * The marker size of a very large page. + */ + public static final int PAGE_LARGE = 2 * 1024 * 1024; + + // The following are key prefixes used in meta map + + /** + * The prefix for chunks ("chunk."). This, plus the chunk id (hex encoded) + * is the key, and the serialized chunk metadata is the value. + */ + public static final String META_CHUNK = "chunk."; + + /** + * The prefix for names ("name."). This, plus the name of the map, is the + * key, and the map id (hey encoded) is the value. + */ + public static final String META_NAME = "name."; + + /** + * The prefix for maps ("map."). This, plus the map id (hex encoded) is the + * key, and the serialized in the map metadata is the value. + */ + public static final String META_MAP = "map."; + + /** + * The prefix for root positions of maps ("root."). This, plus the map id + * (hex encoded) is the key, and the position (hex encoded) is the value. + */ + public static final String META_ROOT = "root."; + + /** + * Get the length of the variable size int. + * + * @param x the value + * @return the length in bytes + */ + public static int getVarIntLen(int x) { + if ((x & (-1 << 7)) == 0) { + return 1; + } else if ((x & (-1 << 14)) == 0) { + return 2; + } else if ((x & (-1 << 21)) == 0) { + return 3; + } else if ((x & (-1 << 28)) == 0) { + return 4; + } + return 5; + } + + /** + * Get the length of the variable size long. + * + * @param x the value + * @return the length in bytes + */ + public static int getVarLongLen(long x) { + int i = 1; + while (true) { + x >>>= 7; + if (x == 0) { + return i; + } + i++; + } + } + + /** + * Read a variable size int. + * + * @param buff the source buffer + * @return the value + */ + public static int readVarInt(ByteBuffer buff) { + int b = buff.get(); + if (b >= 0) { + return b; + } + // a separate function so that this one can be inlined + return readVarIntRest(buff, b); + } + + private static int readVarIntRest(ByteBuffer buff, int b) { + int x = b & 0x7f; + b = buff.get(); + if (b >= 0) { + return x | (b << 7); + } + x |= (b & 0x7f) << 7; + b = buff.get(); + if (b >= 0) { + return x | (b << 14); + } + x |= (b & 0x7f) << 14; + b = buff.get(); + if (b >= 0) { + return x | b << 21; + } + x |= ((b & 0x7f) << 21) | (buff.get() << 28); + return x; + } + + /** + * Read a variable size long. + * + * @param buff the source buffer + * @return the value + */ + public static long readVarLong(ByteBuffer buff) { + long x = buff.get(); + if (x >= 0) { + return x; + } + x &= 0x7f; + for (int s = 7; s < 64; s += 7) { + long b = buff.get(); + x |= (b & 0x7f) << s; + if (b >= 0) { + break; + } + } + return x; + } + + /** + * Write a variable size int. + * + * @param out the output stream + * @param x the value + * @throws IOException if some data could not be written + */ + public static void writeVarInt(OutputStream out, int x) throws IOException { + while ((x & ~0x7f) != 0) { + out.write((byte) (x | 0x80)); + x >>>= 7; + } + out.write((byte) x); + } + + /** + * Write a variable size int. + * + * @param buff the source buffer + * @param x the value + */ + public static void writeVarInt(ByteBuffer buff, int x) { + while ((x & ~0x7f) != 0) { + buff.put((byte) (x | 0x80)); + x >>>= 7; + } + buff.put((byte) x); + } + + /** + * Write characters from a string (without the length). + * + * @param buff the target buffer (must be large enough) + * @param s the string + * @param len the number of characters + */ + public static void writeStringData(ByteBuffer buff, + String s, int len) { + for (int i = 0; i < len; i++) { + int c = s.charAt(i); + if (c < 0x80) { + buff.put((byte) c); + } else if (c >= 0x800) { + buff.put((byte) (0xe0 | (c >> 12))); + buff.put((byte) (((c >> 6) & 0x3f))); + buff.put((byte) (c & 0x3f)); + } else { + buff.put((byte) (0xc0 | (c >> 6))); + buff.put((byte) (c & 0x3f)); + } + } + } + + /** + * Read a string. + * + * @param buff the source buffer + * @return the value + */ + public static String readString(ByteBuffer buff) { + return readString(buff, readVarInt(buff)); + } + + /** + * Read a string. + * + * @param buff the source buffer + * @param len the number of characters + * @return the value + */ + public static String readString(ByteBuffer buff, int len) { + char[] chars = new char[len]; + for (int i = 0; i < len; i++) { + int x = buff.get() & 0xff; + if (x < 0x80) { + chars[i] = (char) x; + } else if (x >= 0xe0) { + chars[i] = (char) (((x & 0xf) << 12) + + ((buff.get() & 0x3f) << 6) + (buff.get() & 0x3f)); + } else { + chars[i] = (char) (((x & 0x1f) << 6) + (buff.get() & 0x3f)); + } + } + return new String(chars); + } + + /** + * Write a variable size long. + * + * @param buff the target buffer + * @param x the value + */ + public static void writeVarLong(ByteBuffer buff, long x) { + while ((x & ~0x7f) != 0) { + buff.put((byte) (x | 0x80)); + x >>>= 7; + } + buff.put((byte) x); + } + + /** + * Write a variable size long. + * + * @param out the output stream + * @param x the value + * @throws IOException if some data could not be written + */ + public static void writeVarLong(OutputStream out, long x) + throws IOException { + while ((x & ~0x7f) != 0) { + out.write((byte) (x | 0x80)); + x >>>= 7; + } + out.write((byte) x); + } + + /** + * Copy the elements of an array, with a gap. + * + * @param src the source array + * @param dst the target array + * @param oldSize the size of the old array + * @param gapIndex the index of the gap + */ + public static void copyWithGap(Object src, Object dst, int oldSize, + int gapIndex) { + if (gapIndex > 0) { + System.arraycopy(src, 0, dst, 0, gapIndex); + } + if (gapIndex < oldSize) { + System.arraycopy(src, gapIndex, dst, gapIndex + 1, oldSize + - gapIndex); + } + } + + /** + * Copy the elements of an array, and remove one element. + * + * @param src the source array + * @param dst the target array + * @param oldSize the size of the old array + * @param removeIndex the index of the entry to remove + */ + public static void copyExcept(Object src, Object dst, int oldSize, + int removeIndex) { + if (removeIndex > 0 && oldSize > 0) { + System.arraycopy(src, 0, dst, 0, removeIndex); + } + if (removeIndex < oldSize) { + System.arraycopy(src, removeIndex + 1, dst, removeIndex, oldSize + - removeIndex - 1); + } + } + + /** + * Read from a file channel until the buffer is full. + * The buffer is rewind after reading. + * + * @param file the file channel + * @param pos the absolute position within the file + * @param dst the byte buffer + * @throws IllegalStateException if some data could not be read + */ + public static void readFully(FileChannel file, long pos, ByteBuffer dst) { + try { + do { + int len = file.read(dst, pos); + if (len < 0) { + throw new EOFException(); + } + pos += len; + } while (dst.remaining() > 0); + dst.rewind(); + } catch (IOException e) { + long size; + try { + size = file.size(); + } catch (IOException e2) { + size = -1; + } + throw newIllegalStateException( + ERROR_READING_FAILED, + "Reading from file {0} failed at {1} (length {2}), " + + "read {3}, remaining {4}", + file, pos, size, dst.position(), dst.remaining(), e); + } + } + + /** + * Write to a file channel. + * + * @param file the file channel + * @param pos the absolute position within the file + * @param src the source buffer + */ + public static void writeFully(FileChannel file, long pos, ByteBuffer src) { + try { + int off = 0; + do { + int len = file.write(src, pos + off); + off += len; + } while (src.remaining() > 0); + } catch (IOException e) { + throw newIllegalStateException( + ERROR_WRITING_FAILED, + "Writing to {0} failed; length {1} at {2}", + file, src.remaining(), pos, e); + } + } + + /** + * Convert the length to a length code 0..31. 31 means more than 1 MB. + * + * @param len the length + * @return the length code + */ + public static int encodeLength(int len) { + if (len <= 32) { + return 0; + } + int code = Integer.numberOfLeadingZeros(len); + int remaining = len << (code + 1); + code += code; + if ((remaining & (1 << 31)) != 0) { + code--; + } + if ((remaining << 1) != 0) { + code--; + } + code = Math.min(31, 52 - code); + // alternative code (slower): + // int x = len; + // int shift = 0; + // while (x > 3) { + // shift++; + // x = (x >>> 1) + (x & 1); + // } + // shift = Math.max(0, shift - 4); + // int code = (shift << 1) + (x & 1); + // code = Math.min(31, code); + return code; + } + + /** + * Get the chunk id from the position. + * + * @param pos the position + * @return the chunk id + */ + public static int getPageChunkId(long pos) { + return (int) (pos >>> 38); + } + + /** + * Get the maximum length for the given page position. + * + * @param pos the position + * @return the maximum length + */ + public static int getPageMaxLength(long pos) { + int code = (int) ((pos >> 1) & 31); + return decodePageLength(code); + } + + /** + * Get the maximum length for the given code. + * For the code 31, PAGE_LARGE is returned. + * + * @param code encoded page length + * @return the maximum length + */ + public static int decodePageLength(int code) { + if (code == 31) { + return PAGE_LARGE; + } + return (2 + (code & 1)) << ((code >> 1) + 4); + } + + /** + * Get the offset from the position. + * + * @param pos the position + * @return the offset + */ + public static int getPageOffset(long pos) { + return (int) (pos >> 6); + } + + /** + * Get the page type from the position. + * + * @param pos the position + * @return the page type (PAGE_TYPE_NODE or PAGE_TYPE_LEAF) + */ + public static int getPageType(long pos) { + return ((int) pos) & 1; + } + + /** + * Determines whether specified file position corresponds to a leaf page + * @param pos the position + * @return true if it is a leaf, false otherwise + */ + public static boolean isLeafPosition(long pos) { + return getPageType(pos) == PAGE_TYPE_LEAF; + } + + /** + * Find out if page was saved. + * + * @param pos the position + * @return true if page has been saved + */ + public static boolean isPageSaved(long pos) { + return (pos & ~1L) != 0; + } + + /** + * Find out if page was removed. + * + * @param pos the position + * @return true if page has been removed (no longer accessible from the + * current root of the tree) + */ + static boolean isPageRemoved(long pos) { + return pos == 1L; + } + + /** + * Get the position of this page. The following information is encoded in + * the position: the chunk id, the offset, the maximum length, and the type + * (node or leaf). + * + * @param chunkId the chunk id + * @param offset the offset + * @param length the length + * @param type the page type (1 for node, 0 for leaf) + * @return the position + */ + public static long getPagePos(int chunkId, int offset, + int length, int type) { + long pos = (long) chunkId << 38; + pos |= (long) offset << 6; + pos |= encodeLength(length) << 1; + pos |= type; + return pos; + } + + /** + * Calculate a check value for the given integer. A check value is mean to + * verify the data is consistent with a high probability, but not meant to + * protect against media failure or deliberate changes. + * + * @param x the value + * @return the check value + */ + public static short getCheckValue(int x) { + return (short) ((x >> 16) ^ x); + } + + /** + * Append a map to the string builder, sorted by key. + * + * @param buff the target buffer + * @param map the map + * @return the string builder + */ + public static StringBuilder appendMap(StringBuilder buff, HashMap map) { + Object[] keys = map.keySet().toArray(); + Arrays.sort(keys); + for (Object k : keys) { + String key = (String) k; + Object value = map.get(key); + if (value instanceof Long) { + appendMap(buff, key, (long) value); + } else if (value instanceof Integer) { + appendMap(buff, key, (int) value); + } else { + appendMap(buff, key, value.toString()); + } + } + return buff; + } + + private static StringBuilder appendMapKey(StringBuilder buff, String key) { + if (buff.length() > 0) { + buff.append(','); + } + return buff.append(key).append(':'); + } + + /** + * Append a key-value pair to the string builder. Keys may not contain a + * colon. Values that contain a comma or a double quote are enclosed in + * double quotes, with special characters escaped using a backslash. + * + * @param buff the target buffer + * @param key the key + * @param value the value + */ + public static void appendMap(StringBuilder buff, String key, String value) { + appendMapKey(buff, key); + if (value.indexOf(',') < 0 && value.indexOf('\"') < 0) { + buff.append(value); + } else { + buff.append('\"'); + for (int i = 0, size = value.length(); i < size; i++) { + char c = value.charAt(i); + if (c == '\"') { + buff.append('\\'); + } + buff.append(c); + } + buff.append('\"'); + } + } + + /** + * Append a key-value pair to the string builder. Keys may not contain a + * colon. + * + * @param buff the target buffer + * @param key the key + * @param value the value + */ + public static void appendMap(StringBuilder buff, String key, long value) { + appendMapKey(buff, key).append(Long.toHexString(value)); + } + + /** + * Append a key-value pair to the string builder. Keys may not contain a + * colon. + * + * @param buff the target buffer + * @param key the key + * @param value the value + */ + public static void appendMap(StringBuilder buff, String key, int value) { + appendMapKey(buff, key).append(Integer.toHexString(value)); + } + + /** + * @param buff output buffer, should be empty + * @param s parsed string + * @param i offset to parse from + * @param size stop offset (exclusive) + * @return new offset + */ + private static int parseMapValue(StringBuilder buff, String s, int i, int size) { + while (i < size) { + char c = s.charAt(i++); + if (c == ',') { + break; + } else if (c == '\"') { + while (i < size) { + c = s.charAt(i++); + if (c == '\\') { + if (i == size) { + throw newIllegalStateException(ERROR_FILE_CORRUPT, "Not a map: {0}", s); + } + c = s.charAt(i++); + } else if (c == '\"') { + break; + } + buff.append(c); + } + } else { + buff.append(c); + } + } + return i; + } + + /** + * Parse a key-value pair list. + * + * @param s the list + * @return the map + * @throws IllegalStateException if parsing failed + */ + public static HashMap parseMap(String s) { + HashMap map = new HashMap<>(); + StringBuilder buff = new StringBuilder(); + for (int i = 0, size = s.length(); i < size;) { + int startKey = i; + i = s.indexOf(':', i); + if (i < 0) { + throw newIllegalStateException(ERROR_FILE_CORRUPT, "Not a map: {0}", s); + } + String key = s.substring(startKey, i++); + i = parseMapValue(buff, s, i, size); + map.put(key, buff.toString()); + buff.setLength(0); + } + return map; + } + + /** + * Parse a key-value pair list and checks its checksum. + * + * @param bytes encoded map + * @return the map without mapping for {@code "fletcher"}, or {@code null} if checksum is wrong + * or parameter do not represent a properly formatted map serialization + */ + static HashMap parseChecksummedMap(byte[] bytes) { + int start = 0, end = bytes.length; + while (start < end && bytes[start] <= ' ') { + start++; + } + while (start < end && bytes[end - 1] <= ' ') { + end--; + } + String s = new String(bytes, start, end - start, StandardCharsets.ISO_8859_1); + HashMap map = new HashMap<>(); + StringBuilder buff = new StringBuilder(); + for (int i = 0, size = s.length(); i < size;) { + int startKey = i; + i = s.indexOf(':', i); + if (i < 0) { + // Corrupted map + return null; + } + if (i - startKey == 8 && s.regionMatches(startKey, "fletcher", 0, 8)) { + parseMapValue(buff, s, i + 1, size); + int check = (int) Long.parseLong(buff.toString(), 16); + if (check == getFletcher32(bytes, start, startKey - 1)) { + return map; + } + // Corrupted map + return null; + } + String key = s.substring(startKey, i++); + i = parseMapValue(buff, s, i, size); + map.put(key, buff.toString()); + buff.setLength(0); + } + // Corrupted map + return null; + } + + /** + * Parse a name from key-value pair list. + * + * @param s the list + * @return value of name item, or {@code null} + * @throws IllegalStateException if parsing failed + */ + public static String getMapName(String s) { + return getFromMap(s, "name"); + } + + /** + * Parse a specified pair from key-value pair list. + * + * @param s the list + * @param key the name of the key + * @return value of the specified item, or {@code null} + * @throws IllegalStateException if parsing failed + */ + public static String getFromMap(String s, String key) { + int keyLength = key.length(); + for (int i = 0, size = s.length(); i < size;) { + int startKey = i; + i = s.indexOf(':', i); + if (i < 0) { + throw newIllegalStateException(ERROR_FILE_CORRUPT, "Not a map: {0}", s); + } + if (i++ - startKey == keyLength && s.regionMatches(startKey, key, 0, keyLength)) { + StringBuilder buff = new StringBuilder(); + parseMapValue(buff, s, i, size); + return buff.toString(); + } else { + while (i < size) { + char c = s.charAt(i++); + if (c == ',') { + break; + } else if (c == '\"') { + while (i < size) { + c = s.charAt(i++); + if (c == '\\') { + if (i++ == size) { + throw newIllegalStateException(ERROR_FILE_CORRUPT, "Not a map: {0}", s); + } + } else if (c == '\"') { + break; + } + } + } + } + } + } + return null; + } + + /** + * Calculate the Fletcher32 checksum. + * + * @param bytes the bytes + * @param offset initial offset + * @param length the message length (if odd, 0 is appended) + * @return the checksum + */ + public static int getFletcher32(byte[] bytes, int offset, int length) { + int s1 = 0xffff, s2 = 0xffff; + int i = offset, len = offset + (length & ~1); + while (i < len) { + // reduce after 360 words (each word is two bytes) + for (int end = Math.min(i + 720, len); i < end;) { + int x = ((bytes[i++] & 0xff) << 8) | (bytes[i++] & 0xff); + s2 += s1 += x; + } + s1 = (s1 & 0xffff) + (s1 >>> 16); + s2 = (s2 & 0xffff) + (s2 >>> 16); + } + if ((length & 1) != 0) { + // odd length: append 0 + int x = (bytes[i] & 0xff) << 8; + s2 += s1 += x; + } + s1 = (s1 & 0xffff) + (s1 >>> 16); + s2 = (s2 & 0xffff) + (s2 >>> 16); + return (s2 << 16) | s1; + } + + /** + * Throw an IllegalArgumentException if the argument is invalid. + * + * @param test true if the argument is valid + * @param message the message + * @param arguments the arguments + * @throws IllegalArgumentException if the argument is invalid + */ + public static void checkArgument(boolean test, String message, + Object... arguments) { + if (!test) { + throw newIllegalArgumentException(message, arguments); + } + } + + /** + * Create a new IllegalArgumentException. + * + * @param message the message + * @param arguments the arguments + * @return the exception + */ + public static IllegalArgumentException newIllegalArgumentException( + String message, Object... arguments) { + return initCause(new IllegalArgumentException( + formatMessage(0, message, arguments)), + arguments); + } + + /** + * Create a new UnsupportedOperationException. + * + * @param message the message + * @return the exception + */ + public static UnsupportedOperationException + newUnsupportedOperationException(String message) { + return new UnsupportedOperationException(formatMessage(0, message)); + } + + /** + * Create a new IllegalStateException. + * + * @param errorCode the error code + * @param message the message + * @param arguments the arguments + * @return the exception + */ + public static IllegalStateException newIllegalStateException( + int errorCode, String message, Object... arguments) { + return initCause(new IllegalStateException( + formatMessage(errorCode, message, arguments)), + arguments); + } + + private static T initCause(T e, Object... arguments) { + int size = arguments.length; + if (size > 0) { + Object o = arguments[size - 1]; + if (o instanceof Throwable) { + e.initCause((Throwable) o); + } + } + return e; + } + + /** + * Format an error message. + * + * @param errorCode the error code + * @param message the message + * @param arguments the arguments + * @return the formatted message + */ + public static String formatMessage(int errorCode, String message, + Object... arguments) { + // convert arguments to strings, to avoid locale specific formatting + arguments = arguments.clone(); + for (int i = 0; i < arguments.length; i++) { + Object a = arguments[i]; + if (!(a instanceof Exception)) { + String s = a == null ? "null" : a.toString(); + if (s.length() > 1000) { + s = s.substring(0, 1000) + "..."; + } + arguments[i] = s; + } + } + return MessageFormat.format(message, arguments) + + " [" + Constants.VERSION_MAJOR + "." + + Constants.VERSION_MINOR + "." + Constants.BUILD_ID + + "/" + errorCode + "]"; + } + + /** + * Get the error code from an exception message. + * + * @param m the message + * @return the error code, or 0 if none + */ + public static int getErrorCode(String m) { + if (m != null && m.endsWith("]")) { + int dash = m.lastIndexOf('/'); + if (dash >= 0) { + try { + return StringUtils.parseUInt31(m, dash + 1, m.length() - 1); + } catch (NumberFormatException e) { + // no error code + } + } + } + return 0; + } + + /** + * Read a hex long value from a map. + * + * @param map the map + * @param key the key + * @param defaultValue if the value is null + * @return the parsed value + * @throws IllegalStateException if parsing fails + */ + public static long readHexLong(Map map, String key, long defaultValue) { + Object v = map.get(key); + if (v == null) { + return defaultValue; + } else if (v instanceof Long) { + return (Long) v; + } + try { + return parseHexLong((String) v); + } catch (NumberFormatException e) { + throw newIllegalStateException(ERROR_FILE_CORRUPT, + "Error parsing the value {0}", v, e); + } + } + + /** + * Parse an unsigned, hex long. + * + * @param x the string + * @return the parsed value + * @throws IllegalStateException if parsing fails + */ + public static long parseHexLong(String x) { + try { + if (x.length() == 16) { + // avoid problems with overflow + // in Java 8, this special case is not needed + return (Long.parseLong(x.substring(0, 8), 16) << 32) | + Long.parseLong(x.substring(8, 16), 16); + } + return Long.parseLong(x, 16); + } catch (NumberFormatException e) { + throw newIllegalStateException(ERROR_FILE_CORRUPT, + "Error parsing the value {0}", x, e); + } + } + + /** + * Parse an unsigned, hex long. + * + * @param x the string + * @return the parsed value + * @throws IllegalStateException if parsing fails + */ + public static int parseHexInt(String x) { + try { + // avoid problems with overflow + // in Java 8, we can use Integer.parseLong(x, 16); + return (int) Long.parseLong(x, 16); + } catch (NumberFormatException e) { + throw newIllegalStateException(ERROR_FILE_CORRUPT, + "Error parsing the value {0}", x, e); + } + } + + /** + * Read a hex int value from a map. + * + * @param map the map + * @param key the key + * @param defaultValue if the value is null + * @return the parsed value + * @throws IllegalStateException if parsing fails + */ + public static int readHexInt(Map map, String key, int defaultValue) { + Object v = map.get(key); + if (v == null) { + return defaultValue; + } else if (v instanceof Integer) { + return (Integer) v; + } + try { + // support unsigned hex value + return (int) Long.parseLong((String) v, 16); + } catch (NumberFormatException e) { + throw newIllegalStateException(ERROR_FILE_CORRUPT, + "Error parsing the value {0}", v, e); + } + } + + /** + * Get the configuration parameter value, or default. + * + * @param config the configuration + * @param key the key + * @param defaultValue the default + * @return the configured value or default + */ + public static int getConfigParam(Map config, String key, int defaultValue) { + Object o = config.get(key); + if (o instanceof Number) { + return ((Number) o).intValue(); + } else if (o != null) { + try { + return Integer.decode(o.toString()); + } catch (NumberFormatException e) { + // ignore + } + } + return defaultValue; + } + +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/FileStore.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/FileStore.java new file mode 100644 index 000000000..28d552bb7 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/FileStore.java @@ -0,0 +1,437 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.cache.FilePathCache; +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePath; +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePathDisk; +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePathEncrypt; +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePathNio; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.util.concurrent.atomic.AtomicLong; + +/** + * The default storage mechanism of the MVStore. This implementation persists + * data to a file. The file store is responsible to persist data and for free + * space management. + */ +public class FileStore { + + /** + * The number of read operations. + */ + protected final AtomicLong readCount = new AtomicLong(); + + /** + * The number of read bytes. + */ + protected final AtomicLong readBytes = new AtomicLong(); + + /** + * The number of write operations. + */ + protected final AtomicLong writeCount = new AtomicLong(); + + /** + * The number of written bytes. + */ + protected final AtomicLong writeBytes = new AtomicLong(); + + /** + * The free spaces between the chunks. The first block to use is block 2 + * (the first two blocks are the store header). + */ + protected final FreeSpaceBitSet freeSpace = + new FreeSpaceBitSet(2, MVStore.BLOCK_SIZE); + + /** + * The file name. + */ + private String fileName; + + /** + * Whether this store is read-only. + */ + private boolean readOnly; + + /** + * The file size (cached). + */ + protected long fileSize; + + /** + * The file. + */ + private FileChannel file; + + /** + * The encrypted file (if encryption is used). + */ + private FileChannel encryptedFile; + + /** + * The file lock. + */ + private FileLock fileLock; + + @Override + public String toString() { + return fileName; + } + + /** + * Read from the file. + * + * @param pos the write position + * @param len the number of bytes to read + * @return the byte buffer + */ + public ByteBuffer readFully(long pos, int len) { + ByteBuffer dst = ByteBuffer.allocate(len); + DataUtils.readFully(file, pos, dst); + readCount.incrementAndGet(); + readBytes.addAndGet(len); + return dst; + } + + /** + * Write to the file. + * + * @param pos the write position + * @param src the source buffer + */ + public void writeFully(long pos, ByteBuffer src) { + int len = src.remaining(); + fileSize = Math.max(fileSize, pos + len); + DataUtils.writeFully(file, pos, src); + writeCount.incrementAndGet(); + writeBytes.addAndGet(len); + } + + /** + * Try to open the file. + * + * @param fileName the file name + * @param readOnly whether the file should only be opened in read-only mode, + * even if the file is writable + * @param encryptionKey the encryption key, or null if encryption is not + * used + */ + public void open(String fileName, boolean readOnly, char[] encryptionKey) { + if (file != null) { + return; + } + // ensure the Cache file system is registered + FilePathCache.INSTANCE.getScheme(); + FilePath p = FilePath.get(fileName); + // if no explicit scheme was specified, NIO is used + if (p instanceof FilePathDisk && + !fileName.startsWith(p.getScheme() + ":")) { + // ensure the NIO file system is registered + FilePathNio.class.getName(); + fileName = "nio:" + fileName; + } + this.fileName = fileName; + FilePath f = FilePath.get(fileName); + FilePath parent = f.getParent(); + if (parent != null && !parent.exists()) { + throw DataUtils.newIllegalArgumentException( + "Directory does not exist: {0}", parent); + } + if (f.exists() && !f.canWrite()) { + readOnly = true; + } + this.readOnly = readOnly; + try { + file = f.open(readOnly ? "r" : "rw"); + if (encryptionKey != null) { + byte[] key = FilePathEncrypt.getPasswordBytes(encryptionKey); + encryptedFile = file; + file = new FilePathEncrypt.FileEncrypt(fileName, key, file); + } + try { + if (readOnly) { + fileLock = file.tryLock(0, Long.MAX_VALUE, true); + } else { + fileLock = file.tryLock(); + } + } catch (OverlappingFileLockException e) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_LOCKED, + "The file is locked: {0}", fileName, e); + } + if (fileLock == null) { + try { close(); } catch (Exception ignore) {} + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_LOCKED, + "The file is locked: {0}", fileName); + } + fileSize = file.size(); + } catch (IOException e) { + try { close(); } catch (Exception ignore) {} + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_READING_FAILED, + "Could not open file {0}", fileName, e); + } + } + + /** + * Close this store. + */ + public void close() { + try { + if(file != null && file.isOpen()) { + if (fileLock != null) { + fileLock.release(); + } + file.close(); + } + } catch (Exception e) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_WRITING_FAILED, + "Closing failed for file {0}", fileName, e); + } finally { + fileLock = null; + file = null; + } + } + + /** + * Flush all changes. + */ + public void sync() { + if (file != null) { + try { + file.force(true); + } catch (IOException e) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_WRITING_FAILED, + "Could not sync file {0}", fileName, e); + } + } + } + + /** + * Get the file size. + * + * @return the file size + */ + public long size() { + return fileSize; + } + + /** + * Truncate the file. + * + * @param size the new file size + */ + public void truncate(long size) { + int attemptCount = 0; + while (true) { + try { + writeCount.incrementAndGet(); + file.truncate(size); + fileSize = Math.min(fileSize, size); + return; + } catch (IOException e) { + if (++attemptCount == 10) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_WRITING_FAILED, + "Could not truncate file {0} to size {1}", + fileName, size, e); + } + System.gc(); + Thread.yield(); + } + } + } + + /** + * Get the file instance in use. + *

+ * The application may read from the file (for example for online backup), + * but not write to it or truncate it. + * + * @return the file + */ + public FileChannel getFile() { + return file; + } + + /** + * Get the encrypted file instance, if encryption is used. + *

+ * The application may read from the file (for example for online backup), + * but not write to it or truncate it. + * + * @return the encrypted file, or null if encryption is not used + */ + public FileChannel getEncryptedFile() { + return encryptedFile; + } + + /** + * Get the number of write operations since this store was opened. + * For file based stores, this is the number of file write operations. + * + * @return the number of write operations + */ + public long getWriteCount() { + return writeCount.get(); + } + + /** + * Get the number of written bytes since this store was opened. + * + * @return the number of write operations + */ + public long getWriteBytes() { + return writeBytes.get(); + } + + /** + * Get the number of read operations since this store was opened. + * For file based stores, this is the number of file read operations. + * + * @return the number of read operations + */ + public long getReadCount() { + return readCount.get(); + } + + /** + * Get the number of read bytes since this store was opened. + * + * @return the number of write operations + */ + public long getReadBytes() { + return readBytes.get(); + } + + public boolean isReadOnly() { + return readOnly; + } + + /** + * Get the default retention time for this store in milliseconds. + * + * @return the retention time + */ + public int getDefaultRetentionTime() { + return 45_000; + } + + /** + * Mark the space as in use. + * + * @param pos the position in bytes + * @param length the number of bytes + */ + public void markUsed(long pos, int length) { + freeSpace.markUsed(pos, length); + } + + /** + * Allocate a number of blocks and mark them as used. + * + * @param length the number of bytes to allocate + * @param reservedLow start block index of the reserved area (inclusive) + * @param reservedHigh end block index of the reserved area (exclusive), + * special value -1 means beginning of the infinite free area + * @return the start position in bytes + */ + long allocate(int length, long reservedLow, long reservedHigh) { + return freeSpace.allocate(length, reservedLow, reservedHigh); + } + + /** + * Calculate starting position of the prospective allocation. + * + * @param blocks the number of blocks to allocate + * @param reservedLow start block index of the reserved area (inclusive) + * @param reservedHigh end block index of the reserved area (exclusive), + * special value -1 means beginning of the infinite free area + * @return the starting block index + */ + long predictAllocation(int blocks, long reservedLow, long reservedHigh) { + return freeSpace.predictAllocation(blocks, reservedLow, reservedHigh); + } + + boolean isFragmented() { + return freeSpace.isFragmented(); + } + + /** + * Mark the space as free. + * + * @param pos the position in bytes + * @param length the number of bytes + */ + public void free(long pos, int length) { + freeSpace.free(pos, length); + } + + public int getFillRate() { + return freeSpace.getFillRate(); + } + + /** + * Calculates a prospective fill rate, which store would have after rewrite + * of sparsely populated chunk(s) and evacuation of still live data into a + * new chunk. + * + * @param vacatedBlocks + * number of blocks vacated + * @return prospective fill rate (0 - 100) + */ + public int getProjectedFillRate(int vacatedBlocks) { + return freeSpace.getProjectedFillRate(vacatedBlocks); + } + + long getFirstFree() { + return freeSpace.getFirstFree(); + } + + long getFileLengthInUse() { + return freeSpace.getLastFree(); + } + + /** + * Calculates relative "priority" for chunk to be moved. + * + * @param block where chunk starts + * @return priority, bigger number indicate that chunk need to be moved sooner + */ + int getMovePriority(int block) { + return freeSpace.getMovePriority(block); + } + + long getAfterLastBlock() { + return freeSpace.getAfterLastBlock(); + } + + /** + * Mark the file as empty. + */ + public void clear() { + freeSpace.clear(); + } + + /** + * Get the file name. + * + * @return the file name + */ + public String getFileName() { + return fileName; + } + +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/FreeSpaceBitSet.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/FreeSpaceBitSet.java new file mode 100644 index 000000000..cc92ca7c3 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/FreeSpaceBitSet.java @@ -0,0 +1,341 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import org.h2.util.MathUtils; + +import java.util.BitSet; + +/** + * A free space bit set. + */ +public class FreeSpaceBitSet { + + private static final boolean DETAILED_INFO = false; + + /** + * The first usable block. + */ + private final int firstFreeBlock; + + /** + * The block size in bytes. + */ + private final int blockSize; + + /** + * The bit set. + */ + private final BitSet set = new BitSet(); + + /** + * Left-shifting register, which holds outcomes of recent allocations. Only + * allocations done in "reuseSpace" mode are recorded here. For example, + * rightmost bit set to 1 means that last allocation failed to find a hole + * big enough, and next bit set to 0 means that previous allocation request + * have found one. + */ + private int failureFlags; + + + /** + * Create a new free space map. + * + * @param firstFreeBlock the first free block + * @param blockSize the block size + */ + public FreeSpaceBitSet(int firstFreeBlock, int blockSize) { + this.firstFreeBlock = firstFreeBlock; + this.blockSize = blockSize; + clear(); + } + + /** + * Reset the list. + */ + public void clear() { + set.clear(); + set.set(0, firstFreeBlock); + } + + /** + * Check whether one of the blocks is in use. + * + * @param pos the position in bytes + * @param length the number of bytes + * @return true if a block is in use + */ + public boolean isUsed(long pos, int length) { + int start = getBlock(pos); + int blocks = getBlockCount(length); + for (int i = start; i < start + blocks; i++) { + if (!set.get(i)) { + return false; + } + } + return true; + } + + /** + * Check whether one of the blocks is free. + * + * @param pos the position in bytes + * @param length the number of bytes + * @return true if a block is free + */ + public boolean isFree(long pos, int length) { + int start = getBlock(pos); + int blocks = getBlockCount(length); + for (int i = start; i < start + blocks; i++) { + if (set.get(i)) { + return false; + } + } + return true; + } + + /** + * Allocate a number of blocks and mark them as used. + * + * @param length the number of bytes to allocate + * @return the start position in bytes + */ + public long allocate(int length) { + return allocate(length, 0, 0); + } + + /** + * Allocate a number of blocks and mark them as used. + * + * @param length the number of bytes to allocate + * @param reservedLow start block index of the reserved area (inclusive) + * @param reservedHigh end block index of the reserved area (exclusive), + * special value -1 means beginning of the infinite free area + * @return the start position in bytes + */ + long allocate(int length, long reservedLow, long reservedHigh) { + return getPos(allocate(getBlockCount(length), (int)reservedLow, (int)reservedHigh, true)); + } + + /** + * Calculate starting position of the prospective allocation. + * + * @param blocks the number of blocks to allocate + * @param reservedLow start block index of the reserved area (inclusive) + * @param reservedHigh end block index of the reserved area (exclusive), + * special value -1 means beginning of the infinite free area + * @return the starting block index + */ + long predictAllocation(int blocks, long reservedLow, long reservedHigh) { + return allocate(blocks, (int)reservedLow, (int)reservedHigh, false); + } + + boolean isFragmented() { + return Integer.bitCount(failureFlags & 0x0F) > 1; + } + + private int allocate(int blocks, int reservedLow, int reservedHigh, boolean allocate) { + int freeBlocksTotal = 0; + for (int i = 0;;) { + int start = set.nextClearBit(i); + int end = set.nextSetBit(start + 1); + int freeBlocks = end - start; + if (end < 0 || freeBlocks >= blocks) { + if ((reservedHigh < 0 || start < reservedHigh) && start + blocks > reservedLow) { // overlap detected + if (reservedHigh < 0) { + start = getAfterLastBlock(); + end = -1; + } else { + i = reservedHigh; + continue; + } + } + assert set.nextSetBit(start) == -1 || set.nextSetBit(start) >= start + blocks : + "Double alloc: " + Integer.toHexString(start) + "/" + Integer.toHexString(blocks) + " " + this; + if (allocate) { + set.set(start, start + blocks); + } else { + failureFlags <<= 1; + if (end < 0 && freeBlocksTotal > 4 * blocks) { + failureFlags |= 1; + } + } + return start; + } + freeBlocksTotal += freeBlocks; + i = end; + } + } + + /** + * Mark the space as in use. + * + * @param pos the position in bytes + * @param length the number of bytes + */ + public void markUsed(long pos, int length) { + int start = getBlock(pos); + int blocks = getBlockCount(length); + assert set.nextSetBit(start) == -1 || set.nextSetBit(start) >= start + blocks : + "Double mark: " + Integer.toHexString(start) + "/" + Integer.toHexString(blocks) + " " + this; + set.set(start, start + blocks); + } + + /** + * Mark the space as free. + * + * @param pos the position in bytes + * @param length the number of bytes + */ + public void free(long pos, int length) { + int start = getBlock(pos); + int blocks = getBlockCount(length); + assert set.nextClearBit(start) >= start + blocks : + "Double free: " + Integer.toHexString(start) + "/" + Integer.toHexString(blocks) + " " + this; + set.clear(start, start + blocks); + } + + private long getPos(int block) { + return (long) block * (long) blockSize; + } + + private int getBlock(long pos) { + return (int) (pos / blockSize); + } + + private int getBlockCount(int length) { + return MathUtils.roundUpInt(length, blockSize) / blockSize; + } + + /** + * Get the fill rate of the space in percent. The value 0 means the space is + * completely free, and 100 means it is completely full. + * + * @return the fill rate (0 - 100) + */ + int getFillRate() { + return getProjectedFillRate(0); + } + + /** + * Calculates a prospective fill rate, which store would have after rewrite + * of sparsely populated chunk(s) and evacuation of still live data into a + * new chunk. + * + * @param vacatedBlocks + * number of blocks vacated as a result of live data evacuation less + * number of blocks in prospective chunk with evacuated live data + * @return prospective fill rate (0 - 100) + */ + int getProjectedFillRate(int vacatedBlocks) { + // it's not bullet-proof against race condition but should be good enough + // to get approximation without holding a store lock + int usedBlocks; + int totalBlocks; + do { + totalBlocks = set.length(); + usedBlocks = set.cardinality(); + } while (totalBlocks != set.length() || usedBlocks > totalBlocks); + usedBlocks -= firstFreeBlock + vacatedBlocks; + totalBlocks -= firstFreeBlock; + return usedBlocks == 0 ? 0 : (int)((100L * usedBlocks + totalBlocks - 1) / totalBlocks); + } + + /** + * Get the position of the first free space. + * + * @return the position. + */ + long getFirstFree() { + return getPos(set.nextClearBit(0)); + } + + /** + * Get the position of the last (infinite) free space. + * + * @return the position. + */ + long getLastFree() { + return getPos(getAfterLastBlock()); + } + + /** + * Get the index of the first block after last occupied one. + * It marks the beginning of the last (infinite) free space. + * + * @return block index + */ + int getAfterLastBlock() { + return set.previousSetBit(set.size() - 1) + 1; + } + + /** + * Calculates relative "priority" for chunk to be moved. + * + * @param block where chunk starts + * @return priority, bigger number indicate that chunk need to be moved sooner + */ + int getMovePriority(int block) { + // The most desirable chunks to move are the ones sitting within + // a relatively short span of occupied blocks which is surrounded + // from both sides by relatively long free spans + int prevEnd = set.previousClearBit(block); + int freeSize; + if (prevEnd < 0) { + prevEnd = firstFreeBlock; + freeSize = 0; + } else { + freeSize = prevEnd - set.previousSetBit(prevEnd); + } + + int nextStart = set.nextClearBit(block); + int nextEnd = set.nextSetBit(nextStart); + if (nextEnd >= 0) { + freeSize += nextEnd - nextStart; + } + return (nextStart - prevEnd - 1) * 1000 / (freeSize + 1); + } + + @Override + public String toString() { + StringBuilder buff = new StringBuilder(); + if (DETAILED_INFO) { + int onCount = 0, offCount = 0; + int on = 0; + for (int i = 0; i < set.length(); i++) { + if (set.get(i)) { + onCount++; + on++; + } else { + offCount++; + } + if ((i & 1023) == 1023) { + buff.append(String.format("%3x", on)).append(' '); + on = 0; + } + } + buff.append('\n') + .append(" on ").append(onCount).append(" off ").append(offCount) + .append(' ').append(100 * onCount / (onCount+offCount)).append("% used "); + } + buff.append('['); + for (int i = 0;;) { + if (i > 0) { + buff.append(", "); + } + int start = set.nextClearBit(i); + buff.append(Integer.toHexString(start)).append('-'); + int end = set.nextSetBit(start + 1); + if (end < 0) { + break; + } + buff.append(Integer.toHexString(end - 1)); + i = end + 1; + } + buff.append(']'); + return buff.toString(); + } +} \ No newline at end of file diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/MVMap.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/MVMap.java new file mode 100644 index 000000000..b449e1d62 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/MVMap.java @@ -0,0 +1,2095 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.type.DataType; +import org.dizitart.no2.mvstore.compat.v1.mvstore.type.ObjectDataType; +import org.dizitart.no2.mvstore.compat.v1.mvstore.type.StringDataType; + +import java.util.AbstractList; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicReference; + +/** + * A stored map. + *

+ * All read and write operations can happen concurrently with all other + * operations, without risk of corruption. + * + * @param the key class + * @param the value class + */ +public class MVMap extends AbstractMap + implements ConcurrentMap +{ + /** + * The store. + */ + public final MVStore store; + + /** + * Reference to the current root page. + */ + private final AtomicReference root; + + private final int id; + private final long createVersion; + private final DataType keyType; + private final DataType valueType; + private final int keysPerPage; + private final boolean singleWriter; + private final K[] keysBuffer; + private final V[] valuesBuffer; + + private final Object lock = new Object(); + private volatile boolean notificationRequested; + + /** + * Whether the map is closed. Volatile so we don't accidentally write to a + * closed map in multithreaded mode. + */ + private volatile boolean closed; + private boolean readOnly; + private boolean isVolatile; + + /** + * This designates the "last stored" version for a store which was + * just open for the first time. + */ + static final long INITIAL_VERSION = -1; + + + protected MVMap(Map config) { + this((MVStore) config.get("store"), + (DataType) config.get("key"), + (DataType) config.get("val"), + DataUtils.readHexInt(config, "id", 0), + DataUtils.readHexLong(config, "createVersion", 0), + new AtomicReference(), + ((MVStore) config.get("store")).getKeysPerPage(), + config.containsKey("singleWriter") && (Boolean) config.get("singleWriter") + ); + setInitialRoot(createEmptyLeaf(), store.getCurrentVersion()); + } + + // constructor for cloneIt() + protected MVMap(MVMap source) { + this(source.store, source.keyType, source.valueType, source.id, source.createVersion, + new AtomicReference<>(source.root.get()), source.keysPerPage, source.singleWriter); + } + + // meta map constructor + MVMap(MVStore store) { + this(store, StringDataType.INSTANCE,StringDataType.INSTANCE, 0, 0, new AtomicReference(), + store.getKeysPerPage(), false); + setInitialRoot(createEmptyLeaf(), store.getCurrentVersion()); + } + + @SuppressWarnings("unchecked") + private MVMap(MVStore store, DataType keyType, DataType valueType, int id, long createVersion, + AtomicReference root, int keysPerPage, boolean singleWriter) { + this.store = store; + this.id = id; + this.createVersion = createVersion; + this.keyType = keyType; + this.valueType = valueType; + this.root = root; + this.keysPerPage = keysPerPage; + this.keysBuffer = singleWriter ? (K[]) new Object[keysPerPage] : null; + this.valuesBuffer = singleWriter ? (V[]) new Object[keysPerPage] : null; + this.singleWriter = singleWriter; + } + + /** + * Clone the current map. + * + * @return clone of this. + */ + protected MVMap cloneIt() { + return new MVMap<>(this); + } + + /** + * Get the metadata key for the root of the given map id. + * + * @param mapId the map id + * @return the metadata key + */ + static String getMapRootKey(int mapId) { + return DataUtils.META_ROOT + Integer.toHexString(mapId); + } + + /** + * Get the metadata key for the given map id. + * + * @param mapId the map id + * @return the metadata key + */ + static String getMapKey(int mapId) { + return DataUtils.META_MAP + Integer.toHexString(mapId); + } + + /** + * Add or replace a key-value pair. + * + * @param key the key (may not be null) + * @param value the value (may not be null) + * @return the old value if the key existed, or null otherwise + */ + @Override + public V put(K key, V value) { + DataUtils.checkArgument(value != null, "The value may not be null"); + return operate(key, value, DecisionMaker.PUT); + } + + /** + * Get the first key, or null if the map is empty. + * + * @return the first key, or null + */ + public final K firstKey() { + return getFirstLast(true); + } + + /** + * Get the first key of this page. + * + * @param p the page + * @return the key, or null + */ + public final K firstKey(Page p) { + return getFirstLast(p, true); + } + + /** + * Get the last key, or null if the map is empty. + * + * @return the last key, or null + */ + public final K lastKey() { + return getFirstLast(false); + } + + /** + * Get the last key of this page. + * + * @param p the page + * @return the key, or null + */ + public final K lastKey(Page p) { + return getFirstLast(p, false); + } + + /** + * Get the key at the given index. + *

+ * This is a O(log(size)) operation. + * + * @param index the index + * @return the key + */ + public final K getKey(long index) { + if (index < 0 || index >= sizeAsLong()) { + return null; + } + Page p = getRootPage(); + long offset = 0; + while (true) { + if (p.isLeaf()) { + if (index >= offset + p.getKeyCount()) { + return null; + } + @SuppressWarnings("unchecked") + K key = (K) p.getKey((int) (index - offset)); + return key; + } + int i = 0, size = getChildPageCount(p); + for (; i < size; i++) { + long c = p.getCounts(i); + if (index < c + offset) { + break; + } + offset += c; + } + if (i == size) { + return null; + } + p = p.getChildPage(i); + } + } + + /** + * Get the key list. The list is a read-only representation of all keys. + *

+ * The get and indexOf methods are O(log(size)) operations. The result of + * indexOf is cast to an int. + * + * @return the key list + */ + public final List keyList() { + return new AbstractList() { + + @Override + public K get(int index) { + return getKey(index); + } + + @Override + public int size() { + return MVMap.this.size(); + } + + @Override + @SuppressWarnings("unchecked") + public int indexOf(Object key) { + return (int) getKeyIndex((K) key); + } + + }; + } + + /** + * Get the index of the given key in the map. + *

+ * This is a O(log(size)) operation. + *

+ * If the key was found, the returned value is the index in the key array. + * If not found, the returned value is negative, where -1 means the provided + * key is smaller than any keys. See also Arrays.binarySearch. + * + * @param key the key + * @return the index + */ + public final long getKeyIndex(K key) { + Page p = getRootPage(); + if (p.getTotalCount() == 0) { + return -1; + } + long offset = 0; + while (true) { + int x = p.binarySearch(key); + if (p.isLeaf()) { + if (x < 0) { + offset = -offset; + } + return offset + x; + } + if (x++ < 0) { + x = -x; + } + for (int i = 0; i < x; i++) { + offset += p.getCounts(i); + } + p = p.getChildPage(x); + } + } + + /** + * Get the first (lowest) or last (largest) key. + * + * @param first whether to retrieve the first key + * @return the key, or null if the map is empty + */ + private K getFirstLast(boolean first) { + Page p = getRootPage(); + return getFirstLast(p, first); + } + + @SuppressWarnings("unchecked") + private K getFirstLast(Page p, boolean first) { + if (p.getTotalCount() == 0) { + return null; + } + while (true) { + if (p.isLeaf()) { + return (K) p.getKey(first ? 0 : p.getKeyCount() - 1); + } + p = p.getChildPage(first ? 0 : getChildPageCount(p) - 1); + } + } + + /** + * Get the smallest key that is larger than the given key, or null if no + * such key exists. + * + * @param key the key + * @return the result + */ + public final K higherKey(K key) { + return getMinMax(key, false, true); + } + + /** + * Get the smallest key that is larger than the given key, for the given + * root page, or null if no such key exists. + * + * @param p the root page + * @param key the key + * @return the result + */ + public final K higherKey(Page p, K key) { + return getMinMax(p, key, false, true); + } + + /** + * Get the smallest key that is larger or equal to this key. + * + * @param key the key + * @return the result + */ + public final K ceilingKey(K key) { + return getMinMax(key, false, false); + } + + /** + * Get the smallest key that is larger or equal to this key, for the given root page. + * + * @param p the root page + * @param key the key + * @return the result + */ + public final K ceilingKey(Page p, K key) { + return getMinMax(p, key, false, false); + } + + /** + * Get the largest key that is smaller or equal to this key. + * + * @param key the key + * @return the result + */ + public final K floorKey(K key) { + return getMinMax(key, true, false); + } + + /** + * Get the largest key that is smaller or equal to this key, for the given root page. + * + * @param p the root page + * @param key the key + * @return the result + */ + public final K floorKey(Page p, K key) { + return getMinMax(p, key, true, false); + } + + /** + * Get the largest key that is smaller than the given key, or null if no + * such key exists. + * + * @param key the key + * @return the result + */ + public final K lowerKey(K key) { + return getMinMax(key, true, true); + } + + /** + * Get the largest key that is smaller than the given key, for the given + * root page, or null if no such key exists. + * + * @param p the root page + * @param key the key + * @return the result + */ + public final K lowerKey(Page p, K key) { + return getMinMax(p, key, true, true); + } + + /** + * Get the smallest or largest key using the given bounds. + * + * @param key the key + * @param min whether to retrieve the smallest key + * @param excluding if the given upper/lower bound is exclusive + * @return the key, or null if no such key exists + */ + private K getMinMax(K key, boolean min, boolean excluding) { + return getMinMax(getRootPage(), key, min, excluding); + } + + @SuppressWarnings("unchecked") + private K getMinMax(Page p, K key, boolean min, boolean excluding) { + int x = p.binarySearch(key); + if (p.isLeaf()) { + if (x < 0) { + x = -x - (min ? 2 : 1); + } else if (excluding) { + x += min ? -1 : 1; + } + if (x < 0 || x >= p.getKeyCount()) { + return null; + } + return (K) p.getKey(x); + } + if (x++ < 0) { + x = -x; + } + while (true) { + if (x < 0 || x >= getChildPageCount(p)) { + return null; + } + K k = getMinMax(p.getChildPage(x), key, min, excluding); + if (k != null) { + return k; + } + x += min ? -1 : 1; + } + } + + + /** + * Get the value for the given key, or null if not found. + * + * @param key the key + * @return the value, or null if not found + * @throws ClassCastException if type of the specified key is not compatible with this map + */ + @Override + public final V get(Object key) { + return get(getRootPage(), key); + } + + /** + * Get the value for the given key from a snapshot, or null if not found. + * + * @param p the root of a snapshot + * @param key the key + * @return the value, or null if not found + * @throws ClassCastException if type of the specified key is not compatible with this map + */ + @SuppressWarnings("unchecked") + public V get(Page p, Object key) { + return (V) Page.get(p, key); + } + + @Override + public final boolean containsKey(Object key) { + return get(key) != null; + } + + /** + * Remove all entries. + */ + @Override + public void clear() { + clearIt(); + } + + /** + * Remove all entries and return the root reference. + * + * @return the new root reference + */ + RootReference clearIt() { + Page emptyRootPage = createEmptyLeaf(); + int attempt = 0; + while (true) { + RootReference rootReference = flushAndGetRoot(); + if (rootReference.getTotalCount() == 0) { + return rootReference; + } + boolean locked = rootReference.isLockedByCurrentThread(); + if (!locked) { + if (attempt++ == 0) { + beforeWrite(); + } else if (attempt > 3 || rootReference.isLocked()) { + rootReference = lockRoot(rootReference, attempt); + locked = true; + } + } + Page rootPage = rootReference.root; + long version = rootReference.version; + try { + if (!locked) { + rootReference = rootReference.updateRootPage(emptyRootPage, attempt); + if (rootReference == null) { + continue; + } + } + store.registerUnsavedMemory(rootPage.removeAllRecursive(version)); + rootPage = emptyRootPage; + return rootReference; + } finally { + if(locked) { + unlockRoot(rootPage); + } + } + } + } + + /** + * Close the map. Accessing the data is still possible (to allow concurrent + * reads), but it is marked as closed. + */ + final void close() { + closed = true; + } + + public final boolean isClosed() { + return closed; + } + + /** + * Remove a key-value pair, if the key exists. + * + * @param key the key (may not be null) + * @return the old value if the key existed, or null otherwise + * @throws ClassCastException if type of the specified key is not compatible with this map + */ + @Override + @SuppressWarnings("unchecked") + public V remove(Object key) { + return operate((K)key, null, DecisionMaker.REMOVE); + } + + /** + * Add a key-value pair if it does not yet exist. + * + * @param key the key (may not be null) + * @param value the new value + * @return the old value if the key existed, or null otherwise + */ + @Override + public final V putIfAbsent(K key, V value) { + return operate(key, value, DecisionMaker.IF_ABSENT); + } + + /** + * Remove a key-value pair if the value matches the stored one. + * + * @param key the key (may not be null) + * @param value the expected value + * @return true if the item was removed + */ + @SuppressWarnings("unchecked") + @Override + public boolean remove(Object key, Object value) { + EqualsDecisionMaker decisionMaker = new EqualsDecisionMaker<>(valueType, (V)value); + operate((K)key, null, decisionMaker); + return decisionMaker.getDecision() != Decision.ABORT; + } + + /** + * Check whether the two values are equal. + * + * @param a the first value + * @param b the second value + * @param datatype to use for comparison + * @return true if they are equal + */ + static boolean areValuesEqual(DataType datatype, Object a, Object b) { + return a == b + || a != null && b != null && datatype.compare(a, b) == 0; + } + + /** + * Replace a value for an existing key, if the value matches. + * + * @param key the key (may not be null) + * @param oldValue the expected value + * @param newValue the new value + * @return true if the value was replaced + */ + @Override + public final boolean replace(K key, V oldValue, V newValue) { + EqualsDecisionMaker decisionMaker = new EqualsDecisionMaker<>(valueType, oldValue); + V result = operate(key, newValue, decisionMaker); + boolean res = decisionMaker.getDecision() != Decision.ABORT; + assert !res || areValuesEqual(valueType, oldValue, result) : oldValue + " != " + result; + return res; + } + + private boolean rewrite(K key) { + ContainsDecisionMaker decisionMaker = new ContainsDecisionMaker<>(); + V result = operate(key, null, decisionMaker); + boolean res = decisionMaker.getDecision() != Decision.ABORT; + assert res == (result != null); + return res; + } + + /** + * Replace a value for an existing key. + * + * @param key the key (may not be null) + * @param value the new value + * @return the old value, if the value was replaced, or null + */ + @Override + public final V replace(K key, V value) { + return operate(key, value, DecisionMaker.IF_PRESENT); + } + + /** + * Compare two keys. + * + * @param a the first key + * @param b the second key + * @return -1 if the first key is smaller, 1 if bigger, 0 if equal + */ + final int compare(Object a, Object b) { + return keyType.compare(a, b); + } + + /** + * Get the key type. + * + * @return the key type + */ + public final DataType getKeyType() { + return keyType; + } + + /** + * Get the value type. + * + * @return the value type + */ + public final DataType getValueType() { + return valueType; + } + + boolean isSingleWriter() { + return singleWriter; + } + + /** + * Read a page. + * + * @param pos the position of the page + * @return the page + */ + final Page readPage(long pos) { + return store.readPage(this, pos); + } + + /** + * Set the position of the root page. + * @param rootPos the position, 0 for empty + * @param version to set for this map + * + */ + final void setRootPos(long rootPos, long version) { + Page root = readOrCreateRootPage(rootPos); + setInitialRoot(root, version); + setWriteVersion(store.getCurrentVersion()); + } + + private Page readOrCreateRootPage(long rootPos) { + Page root = rootPos == 0 ? createEmptyLeaf() : readPage(rootPos); + return root; + } + + /** + * Iterate over a number of keys. + * + * @param from the first key to return + * @return the iterator + */ + public final Iterator keyIterator(K from) { + return new Cursor(getRootPage(), from); + } + + /** + * Re-write any pages that belong to one of the chunks in the given set. + * + * @param set the set of chunk ids + * @return number of pages actually re-written + */ + final int rewrite(Set set) { + if (!singleWriter) { + return rewrite(getRootPage(), set); + } + RootReference rootReference = lockRoot(getRoot(), 1); + int appendCounter = rootReference.getAppendCounter(); + try { + if (appendCounter > 0) { + rootReference = flushAppendBuffer(rootReference, true); + assert rootReference.getAppendCounter() == 0; + } + int res = rewrite(rootReference.root, set); + return res; + } finally { + unlockRoot(); + } + } + + private int rewrite(Page p, Set set) { + if (p.isLeaf()) { + long pos = p.getPos(); + int chunkId = DataUtils.getPageChunkId(pos); + if (!set.contains(chunkId)) { + return 0; + } + assert p.getKeyCount() > 0; + return rewritePage(p) ? 1 : 0; + } + int writtenPageCount = 0; + for (int i = 0; i < getChildPageCount(p); i++) { + long childPos = p.getChildPagePos(i); + if (childPos != 0 && DataUtils.getPageType(childPos) == DataUtils.PAGE_TYPE_LEAF) { + // we would need to load the page, and it's a leaf: + // only do that if it's within the set of chunks we are + // interested in + int chunkId = DataUtils.getPageChunkId(childPos); + if (!set.contains(chunkId)) { + continue; + } + } + writtenPageCount += rewrite(p.getChildPage(i), set); + } + if (writtenPageCount == 0) { + long pos = p.getPos(); + int chunkId = DataUtils.getPageChunkId(pos); + if (set.contains(chunkId)) { + // an inner node page that is in one of the chunks, + // but only points to chunks that are not in the set: + // if no child was changed, we need to do that now + // (this is not needed if anyway one of the children + // was changed, as this would have updated this + // page as well) + while (!p.isLeaf()) { + p = p.getChildPage(0); + } + if (rewritePage(p)) { + writtenPageCount = 1; + } + } + } + return writtenPageCount; + } + + private boolean rewritePage(Page p) { + @SuppressWarnings("unchecked") + K key = (K) p.getKey(0); + if (!isClosed()) { + return rewrite(key); + } + return true; + } + + /** + * Get a cursor to iterate over a number of keys and values. + * + * @param from the first key to return + * @return the cursor + */ + public final Cursor cursor(K from) { + return new Cursor<>(getRootPage(), from); + } + + @Override + public final Set> entrySet() { + final Page root = this.getRootPage(); + return new AbstractSet>() { + + @Override + public Iterator> iterator() { + final Cursor cursor = new Cursor<>(root, null); + return new Iterator>() { + + @Override + public boolean hasNext() { + return cursor.hasNext(); + } + + @Override + public Entry next() { + K k = cursor.next(); + return new SimpleImmutableEntry<>(k, cursor.getValue()); + } + + @Override + public void remove() { + throw DataUtils.newUnsupportedOperationException( + "Removing is not supported"); + } + }; + + } + + @Override + public int size() { + return MVMap.this.size(); + } + + @Override + public boolean contains(Object o) { + return MVMap.this.containsKey(o); + } + + }; + + } + + @Override + public Set keySet() { + final Page root = getRootPage(); + return new AbstractSet() { + + @Override + public Iterator iterator() { + return new Cursor(root, null); + } + + @Override + public int size() { + return MVMap.this.size(); + } + + @Override + public boolean contains(Object o) { + return MVMap.this.containsKey(o); + } + + }; + } + + /** + * Get the map name. + * + * @return the name + */ + public final String getName() { + return store.getMapName(id); + } + + public final MVStore getStore() { + return store; + } + + protected final boolean isPersistent() { + return store.getFileStore() != null && !isVolatile; + } + + /** + * Get the map id. Please note the map id may be different after compacting + * a store. + * + * @return the map id + */ + public final int getId() { + return id; + } + + /** + * The current root page (may not be null). + * + * @return the root page + */ + public final Page getRootPage() { + return flushAndGetRoot().root; + } + + public RootReference getRoot() { + return root.get(); + } + + /** + * Get the root reference, flushing any current append buffer. + * + * @return current root reference + */ + public RootReference flushAndGetRoot() { + RootReference rootReference = getRoot(); + if (singleWriter && rootReference.getAppendCounter() > 0) { + return flushAppendBuffer(rootReference, true); + } + return rootReference; + } + + /** + * Set the initial root. + * + * @param rootPage root page + * @param version initial version + */ + final void setInitialRoot(Page rootPage, long version) { + root.set(new RootReference(rootPage, version)); + } + + /** + * Compare and set the root reference. + * + * @param expectedRootReference the old (expected) + * @param updatedRootReference the new + * @return whether updating worked + */ + final boolean compareAndSetRoot(RootReference expectedRootReference, RootReference updatedRootReference) { + return root.compareAndSet(expectedRootReference, updatedRootReference); + } + + /** + * Rollback to the given version. + * + * @param version the version + */ + final void rollbackTo(long version) { + // check if the map was removed and re-created later ? + if (version > createVersion) { + rollbackRoot(version); + } + } + + /** + * Roll the root back to the specified version. + * + * @param version to rollback to + * @return true if rollback was a success, false if there was not enough in-memory history + */ + boolean rollbackRoot(long version) + { + RootReference rootReference = flushAndGetRoot(); + RootReference previous; + while (rootReference.version >= version && (previous = rootReference.previous) != null) { + if (root.compareAndSet(rootReference, previous)) { + rootReference = previous; + closed = false; + } + } + setWriteVersion(version); + return rootReference.version < version; + } + + /** + * Use the new root page from now on. + * @param expectedRootReference expected current root reference + * @param newRootPage the new root page + * @param attemptUpdateCounter how many attempt (including current) + * were made to update root + * @return new RootReference or null if update failed + */ + protected static boolean updateRoot(RootReference expectedRootReference, Page newRootPage, + int attemptUpdateCounter) { + return expectedRootReference.updateRootPage(newRootPage, attemptUpdateCounter) != null; + } + + /** + * Forget those old versions that are no longer needed. + * @param rootReference to inspect + */ + private void removeUnusedOldVersions(RootReference rootReference) { + rootReference.removeUnusedOldVersions(store.getOldestVersionToKeep()); + } + + public final boolean isReadOnly() { + return readOnly; + } + + /** + * Set the volatile flag of the map. + * + * @param isVolatile the volatile flag + */ + public final void setVolatile(boolean isVolatile) { + this.isVolatile = isVolatile; + } + + /** + * Whether this is volatile map, meaning that changes + * are not persisted. By default (even if the store is not persisted), + * maps are not volatile. + * + * @return whether this map is volatile + */ + public final boolean isVolatile() { + return isVolatile; + } + + /** + * This method is called before writing to the map. The default + * implementation checks whether writing is allowed, and tries + * to detect concurrent modification. + * + * @throws UnsupportedOperationException if the map is read-only, + * or if another thread is concurrently writing + */ + protected final void beforeWrite() { + assert !getRoot().isLockedByCurrentThread() : getRoot(); + if (closed) { + int id = getId(); + String mapName = store.getMapName(id); + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_CLOSED, "Map {0}({1}) is closed. {2}", mapName, id, store.getPanicException()); + } + if (readOnly) { + throw DataUtils.newUnsupportedOperationException( + "This map is read-only"); + } + store.beforeWrite(this); + } + + @Override + public final int hashCode() { + return id; + } + + @Override + public final boolean equals(Object o) { + return this == o; + } + + /** + * Get the number of entries, as a integer. {@link Integer#MAX_VALUE} is + * returned if there are more than this entries. + * + * @return the number of entries, as an integer + * @see #sizeAsLong() + */ + @Override + public final int size() { + long size = sizeAsLong(); + return size > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) size; + } + + /** + * Get the number of entries, as a long. + * + * @return the number of entries + */ + public final long sizeAsLong() { + return getRoot().getTotalCount(); + } + + @Override + public boolean isEmpty() { + return sizeAsLong() == 0; + } + + public final long getCreateVersion() { + return createVersion; + } + + /** + * Open an old version for the given map. + * It will restore map at last known state of the version specified. + * (at the point right before the commit() call, which advanced map to the next version) + * Map is opened in read-only mode. + * + * @param version the version + * @return the map + */ + public final MVMap openVersion(long version) { + if (readOnly) { + throw DataUtils.newUnsupportedOperationException( + "This map is read-only; need to call " + + "the method on the writable map"); + } + DataUtils.checkArgument(version >= createVersion, + "Unknown version {0}; this map was created in version is {1}", + version, createVersion); + RootReference rootReference = flushAndGetRoot(); + removeUnusedOldVersions(rootReference); + RootReference previous; + while ((previous = rootReference.previous) != null && previous.version >= version) { + rootReference = previous; + } + if (previous == null && version < store.getOldestVersionToKeep()) { + throw DataUtils.newIllegalArgumentException("Unknown version {0}", version); + } + MVMap m = openReadOnly(rootReference.root, version); + assert m.getVersion() <= version : m.getVersion() + " <= " + version; + return m; + } + + /** + * Open a copy of the map in read-only mode. + * + * @param rootPos position of the root page + * @param version to open + * @return the opened map + */ + final MVMap openReadOnly(long rootPos, long version) { + Page root = readOrCreateRootPage(rootPos); + return openReadOnly(root, version); + } + + private MVMap openReadOnly(Page root, long version) { + MVMap m = cloneIt(); + m.readOnly = true; + m.setInitialRoot(root, version); + return m; + } + + /** + * Get version of the map, which is the version of the store, + * at the moment when map was modified last time. + * + * @return version + */ + public final long getVersion() { + return getRoot().getVersion(); + } + + /** + * Does the root have changes since the specified version? + * + * @param version root version + * @return true if has changes + */ + final boolean hasChangesSince(long version) { + return getRoot().hasChangesSince(version); + } + + /** + * Get the child page count for this page. This is to allow another map + * implementation to override the default, in case the last child is not to + * be used. + * + * @param p the page + * @return the number of direct children + */ + protected int getChildPageCount(Page p) { + return p.getRawChildPageCount(); + } + + /** + * Get the map type. When opening an existing map, the map type must match. + * + * @return the map type + */ + public String getType() { + return null; + } + + /** + * Get the map metadata as a string. + * + * @param name the map name (or null) + * @return the string + */ + protected String asString(String name) { + StringBuilder buff = new StringBuilder(); + if (name != null) { + DataUtils.appendMap(buff, "name", name); + } + if (createVersion != 0) { + DataUtils.appendMap(buff, "createVersion", createVersion); + } + String type = getType(); + if (type != null) { + DataUtils.appendMap(buff, "type", type); + } + return buff.toString(); + } + + final RootReference setWriteVersion(long writeVersion) { + int attempt = 0; + while(true) { + RootReference rootReference = flushAndGetRoot(); + if(rootReference.version >= writeVersion) { + return rootReference; + } else if (isClosed()) { + // map was closed a while back and can not possibly be in use by now + // it's time to remove it completely from the store (it was anonymous already) + if (rootReference.getVersion() + 1 < store.getOldestVersionToKeep()) { + store.deregisterMapRoot(id); + return null; + } + } + + RootReference lockedRootReference = null; + if (++attempt > 3 || rootReference.isLocked()) { + lockedRootReference = lockRoot(rootReference, attempt); + rootReference = flushAndGetRoot(); + } + + try { + rootReference = rootReference.tryUnlockAndUpdateVersion(writeVersion, attempt); + if (rootReference != null) { + lockedRootReference = null; + removeUnusedOldVersions(rootReference); + return rootReference; + } + } finally { + if (lockedRootReference != null) { + unlockRoot(); + } + } + } + } + + /** + * Create empty leaf node page. + * + * @return new page + */ + protected Page createEmptyLeaf() { + return Page.createEmptyLeaf(this); + } + + /** + * Create empty internal node page. + * + * @return new page + */ + protected Page createEmptyNode() { + return Page.createEmptyNode(this); + } + + /** + * Copy a map. All pages are copied. + * + * @param sourceMap the source map + */ + final void copyFrom(MVMap sourceMap) { + MVStore.TxCounter txCounter = store.registerVersionUsage(); + try { + beforeWrite(); + copy(sourceMap.getRootPage(), null, 0); + } finally { + store.deregisterVersionUsage(txCounter); + } + } + + private void copy(Page source, Page parent, int index) { + Page target = source.copy(this); + if (parent == null) { + setInitialRoot(target, INITIAL_VERSION); + } else { + parent.setChild(index, target); + } + if (!source.isLeaf()) { + for (int i = 0; i < getChildPageCount(target); i++) { + if (source.getChildPagePos(i) != 0) { + // position 0 means no child + // (for example the last entry of an r-tree node) + // (the MVMap is also used for r-trees for compacting) + copy(source.getChildPage(i), target, i); + } + } + target.setComplete(); + } + store.registerUnsavedMemory(target.getMemory()); + if (store.isSaveNeeded()) { + store.commit(); + } + } + + /** + * If map was used in append mode, this method will ensure that append buffer + * is flushed - emptied with all entries inserted into map as a new leaf. + * @param rootReference current RootReference + * @param fullFlush whether buffer should be completely flushed, + * otherwise just a single empty slot is required + * @return potentially updated RootReference + */ + private RootReference flushAppendBuffer(RootReference rootReference, boolean fullFlush) { + boolean preLocked = rootReference.isLockedByCurrentThread(); + boolean locked = preLocked; + int keysPerPage = store.getKeysPerPage(); + try { + IntValueHolder unsavedMemoryHolder = new IntValueHolder(); + int attempt = 0; + int keyCount; + int availabilityThreshold = fullFlush ? 0 : keysPerPage - 1; + while ((keyCount = rootReference.getAppendCounter()) > availabilityThreshold) { + if (!locked) { + // instead of just calling lockRoot() we loop here and check if someone else + // already flushed the buffer, then we don't need a lock + rootReference = tryLock(rootReference, ++attempt); + if (rootReference == null) { + rootReference = getRoot(); + continue; + } + locked = true; + } + + Page rootPage = rootReference.root; + long version = rootReference.version; + CursorPos pos = rootPage.getAppendCursorPos(null); + assert pos != null; + assert pos.index < 0 : pos.index; + int index = -pos.index - 1; + assert index == pos.page.getKeyCount() : index + " != " + pos.page.getKeyCount(); + Page p = pos.page; + CursorPos tip = pos; + pos = pos.parent; + + int remainingBuffer = 0; + Page page = null; + int available = keysPerPage - p.getKeyCount(); + if (available > 0) { + p = p.copy(); + if (keyCount <= available) { + p.expand(keyCount, keysBuffer, valuesBuffer); + } else { + p.expand(available, keysBuffer, valuesBuffer); + keyCount -= available; + if (fullFlush) { + Object[] keys = new Object[keyCount]; + Object[] values = new Object[keyCount]; + System.arraycopy(keysBuffer, available, keys, 0, keyCount); + System.arraycopy(valuesBuffer, available, values, 0, keyCount); + page = Page.createLeaf(this, keys, values, 0); + } else { + System.arraycopy(keysBuffer, available, keysBuffer, 0, keyCount); + System.arraycopy(valuesBuffer, available, valuesBuffer, 0, keyCount); + remainingBuffer = keyCount; + } + } + } else { + tip = tip.parent; + page = Page.createLeaf(this, + Arrays.copyOf(keysBuffer, keyCount), + Arrays.copyOf(valuesBuffer, keyCount), + 0); + } + + unsavedMemoryHolder.value = 0; + if (page != null) { + assert page.map == this; + assert page.getKeyCount() > 0; + Object key = page.getKey(0); + unsavedMemoryHolder.value += page.getMemory(); + while (true) { + if (pos == null) { + if (p.getKeyCount() == 0) { + p = page; + } else { + Object[] keys = new Object[]{key}; + Page.PageReference[] children = new Page.PageReference[]{ + new Page.PageReference(p), + new Page.PageReference(page)}; + unsavedMemoryHolder.value += p.getMemory(); + p = Page.createNode(this, keys, children, p.getTotalCount() + page.getTotalCount(), 0); + } + break; + } + Page c = p; + p = pos.page; + index = pos.index; + pos = pos.parent; + p = p.copy(); + p.setChild(index, page); + p.insertNode(index, key, c); + keyCount = p.getKeyCount(); + int at = keyCount - (p.isLeaf() ? 1 : 2); + if (keyCount <= keysPerPage && + (p.getMemory() < store.getMaxPageSize() || at <= 0)) { + break; + } + key = p.getKey(at); + page = p.split(at); + unsavedMemoryHolder.value += p.getMemory() + page.getMemory(); + } + } + p = replacePage(pos, p, unsavedMemoryHolder); + rootReference = rootReference.updatePageAndLockedStatus(p, preLocked || isPersistent(), + remainingBuffer); + if (rootReference != null) { + // should always be the case, except for spurious failure? + locked = preLocked || isPersistent(); + if (isPersistent() && tip != null) { + store.registerUnsavedMemory(unsavedMemoryHolder.value + tip.processRemovalInfo(version)); + } + assert rootReference.getAppendCounter() <= availabilityThreshold; + break; + } + rootReference = getRoot(); + } + } finally { + if (locked && !preLocked) { + rootReference = unlockRoot(); + } + } + return rootReference; + } + + private static Page replacePage(CursorPos path, Page replacement, IntValueHolder unsavedMemoryHolder) { + int unsavedMemory = replacement.isSaved() ? 0 : replacement.getMemory(); + while (path != null) { + Page parent = path.page; + // condition below should always be true, but older versions (up to 1.4.197) + // may create single-childed (with no keys) internal nodes, which we skip here + if (parent.getKeyCount() > 0) { + Page child = replacement; + replacement = parent.copy(); + replacement.setChild(path.index, child); + unsavedMemory += replacement.getMemory(); + } + path = path.parent; + } + unsavedMemoryHolder.value += unsavedMemory; + return replacement; + } + + /** + * Appends entry to this map. this method is NOT thread safe and can not be used + * neither concurrently, nor in combination with any method that updates this map. + * Non-updating method may be used concurrently, but latest appended values + * are not guaranteed to be visible. + * @param key should be higher in map's order than any existing key + * @param value to be appended + */ + public void append(K key, V value) { + if (singleWriter) { + beforeWrite(); + RootReference rootReference = lockRoot(getRoot(), 1); + int appendCounter = rootReference.getAppendCounter(); + try { + if (appendCounter >= keysPerPage) { + rootReference = flushAppendBuffer(rootReference, false); + appendCounter = rootReference.getAppendCounter(); + assert appendCounter < keysPerPage; + } + keysBuffer[appendCounter] = key; + valuesBuffer[appendCounter] = value; + ++appendCounter; + } finally { + unlockRoot(appendCounter); + } + } else { + put(key, value); + } + } + + /** + * Removes last entry from this map. this method is NOT thread safe and can not be used + * neither concurrently, nor in combination with any method that updates this map. + * Non-updating method may be used concurrently, but latest removal may not be visible. + */ + public void trimLast() { + if (singleWriter) { + RootReference rootReference = getRoot(); + int appendCounter = rootReference.getAppendCounter(); + boolean useRegularRemove = appendCounter == 0; + if (!useRegularRemove) { + rootReference = lockRoot(rootReference, 1); + try { + appendCounter = rootReference.getAppendCounter(); + useRegularRemove = appendCounter == 0; + if (!useRegularRemove) { + --appendCounter; + } + } finally { + unlockRoot(appendCounter); + } + } + if (useRegularRemove) { + Page lastLeaf = rootReference.root.getAppendCursorPos(null).page; + assert lastLeaf.isLeaf(); + assert lastLeaf.getKeyCount() > 0; + Object key = lastLeaf.getKey(lastLeaf.getKeyCount() - 1); + remove(key); + } + } else { + remove(lastKey()); + } + } + + @Override + public final String toString() { + return asString(null); + } + + /** + * A builder for maps. + * + * @param the map type + * @param the key type + * @param the value type + */ + public interface MapBuilder, K, V> { + + /** + * Create a new map of the given type. + * @param store which will own this map + * @param config configuration + * + * @return the map + */ + M create(MVStore store, Map config); + + DataType getKeyType(); + + DataType getValueType(); + + void setKeyType(DataType dataType); + + void setValueType(DataType dataType); + + } + + /** + * A builder for this class. + * + * @param the key type + * @param the value type + */ + public abstract static class BasicBuilder, K, V> implements MapBuilder { + + private DataType keyType; + private DataType valueType; + + /** + * Create a new builder with the default key and value data types. + */ + protected BasicBuilder() { + // ignore + } + + @Override + public DataType getKeyType() { + return keyType; + } + + @Override + public DataType getValueType() { + return valueType; + } + + @Override + public void setKeyType(DataType keyType) { + this.keyType = keyType; + } + + @Override + public void setValueType(DataType valueType) { + this.valueType = valueType; + } + + /** + * Set the key data type. + * + * @param keyType the key type + * @return this + */ + public BasicBuilder keyType(DataType keyType) { + this.keyType = keyType; + return this; + } + + /** + * Set the value data type. + * + * @param valueType the value type + * @return this + */ + public BasicBuilder valueType(DataType valueType) { + this.valueType = valueType; + return this; + } + + @Override + public M create(MVStore store, Map config) { + if (getKeyType() == null) { + setKeyType(new ObjectDataType()); + } + if (getValueType() == null) { + setValueType(new ObjectDataType()); + } + DataType keyType = getKeyType(); + DataType valueType = getValueType(); + config.put("store", store); + config.put("key", keyType); + config.put("val", valueType); + return create(config); + } + + /** + * Create map from config. + * @param config config map + * @return new map + */ + protected abstract M create(Map config); + + } + + /** + * A builder for this class. + * + * @param the key type + * @param the value type + */ + public static class Builder extends BasicBuilder, K, V> { + private boolean singleWriter; + + public Builder() {} + + @Override + public Builder keyType(DataType dataType) { + setKeyType(dataType); + return this; + } + + @Override + public Builder valueType(DataType dataType) { + setValueType(dataType); + return this; + } + + /** + * Set up this Builder to produce MVMap, which can be used in append mode + * by a single thread. + * @see MVMap#append(Object, Object) + * @return this Builder for chained execution + */ + public Builder singleWriter() { + singleWriter = true; + return this; + } + + @Override + protected MVMap create(Map config) { + config.put("singleWriter", singleWriter); + Object type = config.get("type"); + if(type == null || type.equals("rtree")) { + return new MVMap<>(config); + } + throw new IllegalArgumentException("Incompatible map type"); + } + } + + public enum Decision { ABORT, REMOVE, PUT, REPEAT } + + /** + * Class DecisionMaker provides callback interface (and should become a such in Java 8) + * for MVMap.operate method. + * It provides control logic to make a decision about how to proceed with update + * at the point in execution when proper place and possible existing value + * for insert/update/delete key is found. + * Revised value for insert/update is also provided based on original input value + * and value currently existing in the map. + * + * @param value type of the map + */ + public abstract static class DecisionMaker + { + /** + * Decision maker for transaction rollback. + */ + public static final DecisionMaker DEFAULT = new DecisionMaker() { + @Override + public Decision decide(Object existingValue, Object providedValue) { + return providedValue == null ? Decision.REMOVE : Decision.PUT; + } + + @Override + public String toString() { + return "default"; + } + }; + + /** + * Decision maker for put(). + */ + public static final DecisionMaker PUT = new DecisionMaker() { + @Override + public Decision decide(Object existingValue, Object providedValue) { + return Decision.PUT; + } + + @Override + public String toString() { + return "put"; + } + }; + + /** + * Decision maker for remove(). + */ + public static final DecisionMaker REMOVE = new DecisionMaker() { + @Override + public Decision decide(Object existingValue, Object providedValue) { + return Decision.REMOVE; + } + + @Override + public String toString() { + return "remove"; + } + }; + + /** + * Decision maker for putIfAbsent() key/value. + */ + static final DecisionMaker IF_ABSENT = new DecisionMaker() { + @Override + public Decision decide(Object existingValue, Object providedValue) { + return existingValue == null ? Decision.PUT : Decision.ABORT; + } + + @Override + public String toString() { + return "if_absent"; + } + }; + + /** + * Decision maker for replace(). + */ + static final DecisionMaker IF_PRESENT= new DecisionMaker() { + @Override + public Decision decide(Object existingValue, Object providedValue) { + return existingValue != null ? Decision.PUT : Decision.ABORT; + } + + @Override + public String toString() { + return "if_present"; + } + }; + + /** + * Makes a decision about how to proceed with the update. + * @param existingValue value currently exists in the map + * @param providedValue original input value + * @return PUT if a new value need to replace existing one or + * new value to be inserted if there is none + * REMOVE if existing value should be deleted + * ABORT if update operation should be aborted + */ + public abstract Decision decide(V existingValue, V providedValue); + + /** + * Provides revised value for insert/update based on original input value + * and value currently existing in the map. + * This method is only invoked after call to decide(), if it returns PUT. + * @param existingValue value currently exists in the map + * @param providedValue original input value + * @param value type + * @return value to be used by insert/update + */ + public T selectValue(T existingValue, T providedValue) { + return providedValue; + } + + /** + * Resets internal state (if any) of a this DecisionMaker to it's initial state. + * This method is invoked whenever concurrent update failure is encountered, + * so we can re-start update process. + */ + public void reset() {} + } + + /** + * Add, replace or remove a key-value pair. + * + * @param key the key (may not be null) + * @param value new value, it may be null when removal is intended + * @param decisionMaker command object to make choices during transaction. + * @return previous value, if mapping for that key existed, or null otherwise + */ + @SuppressWarnings("unchecked") + public V operate(K key, V value, DecisionMaker decisionMaker) { + IntValueHolder unsavedMemoryHolder = new IntValueHolder(); + int attempt = 0; + while(true) { + RootReference rootReference = flushAndGetRoot(); + boolean locked = rootReference.isLockedByCurrentThread(); + if (!locked) { + if (attempt++ == 0) { + beforeWrite(); + } else if (attempt > 3 || rootReference.isLocked()) { + rootReference = lockRoot(rootReference, attempt); + locked = true; + } + } + Page rootPage = rootReference.root; + long version = rootReference.version; + CursorPos tip; + V result; + unsavedMemoryHolder.value = 0; + try { + CursorPos pos = CursorPos.traverseDown(rootPage, key); + if(!locked && rootReference != getRoot()) { + continue; + } + Page p = pos.page; + int index = pos.index; + tip = pos; + pos = pos.parent; + result = index < 0 ? null : (V)p.getValue(index); + Decision decision = decisionMaker.decide(result, value); + + switch (decision) { + case REPEAT: + decisionMaker.reset(); + continue; + case ABORT: + if(!locked && rootReference != getRoot()) { + decisionMaker.reset(); + continue; + } + return result; + case REMOVE: { + if (index < 0) { + if(!locked && rootReference != getRoot()) { + decisionMaker.reset(); + continue; + } + return null; + } + + if (p.getTotalCount() == 1 && pos != null) { + int keyCount; + do { + p = pos.page; + index = pos.index; + pos = pos.parent; + keyCount = p.getKeyCount(); + // condition below should always be false, but older + // versions (up to 1.4.197) may create + // single-childed (with no keys) internal nodes, + // which we skip here + } while (keyCount == 0 && pos != null); + + if (keyCount <= 1) { + if (keyCount == 1) { + assert index <= 1; + p = p.getChildPage(1 - index); + } else { + // if root happens to be such single-childed + // (with no keys) internal node, then just + // replace it with empty leaf + p = Page.createEmptyLeaf(this); + } + break; + } + } + p = p.copy(); + p.remove(index); + break; + } + case PUT: { + value = decisionMaker.selectValue(result, value); + p = p.copy(); + if (index < 0) { + p.insertLeaf(-index - 1, key, value); + int keyCount; + while ((keyCount = p.getKeyCount()) > store.getKeysPerPage() + || p.getMemory() > store.getMaxPageSize() + && keyCount > (p.isLeaf() ? 1 : 2)) { + long totalCount = p.getTotalCount(); + int at = keyCount >> 1; + Object k = p.getKey(at); + Page split = p.split(at); + unsavedMemoryHolder.value += p.getMemory() + split.getMemory(); + if (pos == null) { + Object[] keys = { k }; + Page.PageReference[] children = { + new Page.PageReference(p), + new Page.PageReference(split) + }; + p = Page.createNode(this, keys, children, totalCount, 0); + break; + } + Page c = p; + p = pos.page; + index = pos.index; + pos = pos.parent; + p = p.copy(); + p.setChild(index, split); + p.insertNode(index, k, c); + } + } else { + p.setValue(index, value); + } + break; + } + } + rootPage = replacePage(pos, p, unsavedMemoryHolder); + if (!locked) { + rootReference = rootReference.updateRootPage(rootPage, attempt); + if (rootReference == null) { + decisionMaker.reset(); + continue; + } + } + store.registerUnsavedMemory(unsavedMemoryHolder.value + tip.processRemovalInfo(version)); + return result; + } finally { + if(locked) { + unlockRoot(rootPage); + } + } + } + } + + private RootReference lockRoot(RootReference rootReference, int attempt) { + while(true) { + RootReference lockedRootReference = tryLock(rootReference, attempt++); + if (lockedRootReference != null) { + return lockedRootReference; + } + rootReference = getRoot(); + } + } + + /** + * Try to lock the root. + * + * @param rootReference the old root reference + * @param attempt the number of attempts so far + * @return the new root reference + */ + protected RootReference tryLock(RootReference rootReference, int attempt) { + RootReference lockedRootReference = rootReference.tryLock(attempt); + if (lockedRootReference != null) { + return lockedRootReference; + } + + RootReference oldRootReference = rootReference.previous; + int contention = 1; + if (oldRootReference != null) { + long updateAttemptCounter = rootReference.updateAttemptCounter - + oldRootReference.updateAttemptCounter; + assert updateAttemptCounter >= 0 : updateAttemptCounter; + long updateCounter = rootReference.updateCounter - oldRootReference.updateCounter; + assert updateCounter >= 0 : updateCounter; + assert updateAttemptCounter >= updateCounter : updateAttemptCounter + " >= " + updateCounter; + contention += (int)((updateAttemptCounter+1) / (updateCounter+1)); + } + + if(attempt > 4) { + if (attempt <= 12) { + Thread.yield(); + } else if (attempt <= 70 - 2 * contention) { + try { + Thread.sleep(contention); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + } else { + synchronized (lock) { + notificationRequested = true; + try { + lock.wait(5); + } catch (InterruptedException ignore) { + } + } + } + } + return null; + } + + /** + * Unlock the root page, the new root being null. + * + * @return the new root reference (never null) + */ + private RootReference unlockRoot() { + return unlockRoot(null, -1); + } + + /** + * Unlock the root page. + * + * @param newRootPage the new root + * @return the new root reference (never null) + */ + protected RootReference unlockRoot(Page newRootPage) { + return unlockRoot(newRootPage, -1); + } + + private void unlockRoot(int appendCounter) { + unlockRoot(null, appendCounter); + } + + private RootReference unlockRoot(Page newRootPage, int appendCounter) { + RootReference updatedRootReference; + do { + RootReference rootReference = getRoot(); + assert rootReference.isLockedByCurrentThread(); + updatedRootReference = rootReference.updatePageAndLockedStatus( + newRootPage == null ? rootReference.root : newRootPage, + false, + appendCounter == -1 ? rootReference.getAppendCounter() : appendCounter + ); + } while(updatedRootReference == null); + + notifyWaiters(); + return updatedRootReference; + } + + private void notifyWaiters() { + if (notificationRequested) { + synchronized (lock) { + notificationRequested = false; + lock.notify(); + } + } + } + + private static final class EqualsDecisionMaker extends DecisionMaker { + private final DataType dataType; + private final V expectedValue; + private Decision decision; + + EqualsDecisionMaker(DataType dataType, V expectedValue) { + this.dataType = dataType; + this.expectedValue = expectedValue; + } + + @Override + public Decision decide(V existingValue, V providedValue) { + assert decision == null; + decision = !areValuesEqual(dataType, expectedValue, existingValue) ? Decision.ABORT : + providedValue == null ? Decision.REMOVE : Decision.PUT; + return decision; + } + + @Override + public void reset() { + decision = null; + } + + Decision getDecision() { + return decision; + } + + @Override + public String toString() { + return "equals_to "+expectedValue; + } + } + + private static final class ContainsDecisionMaker extends DecisionMaker { + private Decision decision; + + ContainsDecisionMaker() {} + + @Override + public Decision decide(V existingValue, V providedValue) { + assert decision == null; + decision = existingValue == null ? Decision.ABORT : Decision.PUT; + return decision; + } + + @Override + public T selectValue(T existingValue, T providedValue) { + return existingValue; + } + + @Override + public void reset() { + decision = null; + } + + Decision getDecision() { + return decision; + } + + @Override + public String toString() { + return "contains"; + } + } + + private static final class IntValueHolder { + int value; + + IntValueHolder() {} + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/MVStore.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/MVStore.java new file mode 100644 index 000000000..e76ddf4ee --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/MVStore.java @@ -0,0 +1,3588 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.cache.CacheLongKeyLIRS; +import org.dizitart.no2.mvstore.compat.v1.mvstore.compress.CompressDeflate; +import org.dizitart.no2.mvstore.compat.v1.mvstore.compress.CompressLZF; +import org.dizitart.no2.mvstore.compat.v1.mvstore.compress.Compressor; +import org.h2.util.MathUtils; +import org.h2.util.Utils; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.ReentrantLock; + +import static org.dizitart.no2.mvstore.compat.v1.mvstore.MVMap.INITIAL_VERSION; + +/* + +TODO: + +Documentation +- rolling docs review: at "Metadata Map" +- better document that writes are in background thread +- better document how to do non-unique indexes +- document pluggable store and OffHeapStore + +TransactionStore: +- ability to disable the transaction log, + if there is only one connection + +MVStore: +- better and clearer memory usage accounting rules + (heap memory versus disk memory), so that even there is + never an out of memory + even for a small heap, and so that chunks + are still relatively big on average +- make sure serialization / deserialization errors don't corrupt the file +- test and possibly improve compact operation (for large dbs) +- automated 'kill process' and 'power failure' test +- defragment (re-creating maps, specially those with small pages) +- store number of write operations per page (maybe defragment + if much different than count) +- r-tree: nearest neighbor search +- use a small object value cache (StringCache), test on Android + for default serialization +- MVStoreTool.dump should dump the data if possible; + possibly using a callback for serialization +- implement a sharded map (in one store, multiple stores) + to support concurrent updates and writes, and very large maps +- to save space when persisting very small transactions, + use a transaction log where only the deltas are stored +- serialization for lists, sets, sets, sorted sets, maps, sorted maps +- maybe rename 'rollback' to 'revert' to distinguish from transactions +- support other compression algorithms (deflate, LZ4,...) +- remove features that are not really needed; simplify the code + possibly using a separate layer or tools + (retainVersion?) +- optional pluggable checksum mechanism (per page), which + requires that everything is a page (including headers) +- rename "store" to "save", as "store" is used in "storeVersion" +- rename setStoreVersion to setDataVersion, setSchemaVersion or similar +- temporary file storage +- simple rollback method (rollback to last committed version) +- MVMap to implement SortedMap, then NavigableMap +- storage that splits database into multiple files, + to speed up compact and allow using trim + (by truncating / deleting empty files) +- add new feature to the file system API to avoid copying data + (reads that returns a ByteBuffer instead of writing into one) + for memory mapped files and off-heap storage +- support log structured merge style operations (blind writes) + using one map per level plus bloom filter +- have a strict call order MVStore -> MVMap -> Page -> FileStore +- autocommit commits, stores, and compacts from time to time; + the background thread should wait at least 90% of the + configured write delay to store changes +- compact* should also store uncommitted changes (if there are any) +- write a LSM-tree (log structured merge tree) utility on top of the MVStore + with blind writes and/or a bloom filter that + internally uses regular maps and merge sort +- chunk metadata: maybe split into static and variable, + or use a small page size for metadata +- data type "string": maybe use prefix compression for keys +- test chunk id rollover +- feature to auto-compact from time to time and on close +- compact very small chunks +- Page: to save memory, combine keys & values into one array + (also children & counts). Maybe remove some other + fields (childrenCount for example) +- Support SortedMap for MVMap +- compact: copy whole pages (without having to open all maps) +- maybe change the length code to have lower gaps +- test with very low limits (such as: short chunks, small pages) +- maybe allow to read beyond the retention time: + when compacting, move live pages in old chunks + to a map (possibly the metadata map) - + this requires a change in the compaction code, plus + a map lookup when reading old data; also, this + old data map needs to be cleaned up somehow; + maybe using an additional timeout +*/ + +/** + * A persistent storage for maps. + */ +public class MVStore implements AutoCloseable +{ + // The following are attribute names (keys) in store header map + private static final String HDR_H = "H"; + private static final String HDR_BLOCK_SIZE = "blockSize"; + private static final String HDR_FORMAT = "format"; + private static final String HDR_CREATED = "created"; + private static final String HDR_FORMAT_READ = "formatRead"; + private static final String HDR_CHUNK = "chunk"; + private static final String HDR_BLOCK = "block"; + private static final String HDR_VERSION = "version"; + private static final String HDR_CLEAN = "clean"; + private static final String HDR_FLETCHER = "fletcher"; + + + /** + * The block size (physical sector size) of the disk. The store header is + * written twice, one copy in each block, to ensure it survives a crash. + */ + static final int BLOCK_SIZE = 4 * 1024; + + private static final int FORMAT_WRITE = 1; + private static final int FORMAT_READ = 1; + + /** + * Store is open. + */ + private static final int STATE_OPEN = 0; + + /** + * Store is about to close now, but is still operational. + * Outstanding store operation by background writer or other thread may be in progress. + * New updates must not be initiated, unless they are part of a closing procedure itself. + */ + private static final int STATE_STOPPING = 1; + + /** + * Store is closing now, and any operation on it may fail. + */ + private static final int STATE_CLOSING = 2; + + /** + * Store is closed. + */ + private static final int STATE_CLOSED = 3; + + /** + * Lock which governs access to major store operations: store(), close(), ... + * It should used in a non-reentrant fashion. + * It serves as a replacement for synchronized(this), except it allows for + * non-blocking lock attempts. + */ + private final ReentrantLock storeLock = new ReentrantLock(true); + + /** + * Reference to a background thread, which is expected to be running, if any. + */ + private final AtomicReference backgroundWriterThread = new AtomicReference<>(); + + private volatile boolean reuseSpace = true; + + private volatile int state; + + private final FileStore fileStore; + + private final boolean fileStoreIsProvided; + + private final int pageSplitSize; + + private final int keysPerPage; + + /** + * The page cache. The default size is 16 MB, and the average size is 2 KB. + * It is split in 16 segments. The stack move distance is 2% of the expected + * number of entries. + */ + private final CacheLongKeyLIRS cache; + + /** + * The newest chunk. If nothing was stored yet, this field is not set. + */ + private Chunk lastChunk; + + /** + * The map of chunks. + */ + private final ConcurrentHashMap chunks = new ConcurrentHashMap<>(); + + private final Queue removedPages = new PriorityBlockingQueue<>(); + + private final Deque deadChunks = new ArrayDeque<>(); + + private long updateCounter = 0; + private long updateAttemptCounter = 0; + + /** + * The metadata map. Write access to this map needs to be done under storeLock. + */ + private final MVMap meta; + + private final ConcurrentHashMap> maps = new ConcurrentHashMap<>(); + + private final HashMap storeHeader = new HashMap<>(); + + private WriteBuffer writeBuffer; + + private final AtomicInteger lastMapId = new AtomicInteger(); + + private int versionsToKeep = 5; + + /** + * The compression level for new pages (0 for disabled, 1 for fast, 2 for + * high). Even if disabled, the store may contain (old) compressed pages. + */ + private final int compressionLevel; + + private Compressor compressorFast; + + private Compressor compressorHigh; + + private final boolean recoveryMode; + + private final UncaughtExceptionHandler backgroundExceptionHandler; + + private volatile long currentVersion; + + /** + * The version of the last stored chunk, or -1 if nothing was stored so far. + */ + private volatile long lastStoredVersion = INITIAL_VERSION; + + /** + * Oldest store version in use. All version beyond this can be safely dropped + */ + private final AtomicLong oldestVersionToKeep = new AtomicLong(); + + /** + * Ordered collection of all version usage counters for all versions starting + * from oldestVersionToKeep and up to current. + */ + private final Deque versions = new LinkedList<>(); + + /** + * Counter of open transactions for the latest (current) store version + */ + private volatile TxCounter currentTxCounter = new TxCounter(currentVersion); + + /** + * The estimated memory used by unsaved pages. This number is not accurate, + * also because it may be changed concurrently, and because temporary pages + * are counted. + */ + private int unsavedMemory; + private final int autoCommitMemory; + private volatile boolean saveNeeded; + + /** + * The time the store was created, in milliseconds since 1970. + */ + private long creationTime; + + /** + * How long to retain old, persisted chunks, in milliseconds. For larger or + * equal to zero, a chunk is never directly overwritten if unused, but + * instead, the unused field is set. If smaller zero, chunks are directly + * overwritten if unused. + */ + private int retentionTime; + + private long lastCommitTime; + + /** + * The version of the current store operation (if any). + */ + private volatile long currentStoreVersion = -1; + + private volatile boolean metaChanged; + + /** + * The delay in milliseconds to automatically commit and write changes. + */ + private int autoCommitDelay; + + private final int autoCompactFillRate; + private long autoCompactLastFileOpCount; + + private volatile IllegalStateException panicException; + + private long lastTimeAbsolute; + + + /** + * Create and open the store. + * + * @param config the configuration to use + * @throws IllegalStateException if the file is corrupt, or an exception + * occurred while opening + * @throws IllegalArgumentException if the directory does not exist + */ + MVStore(Map config) { + recoveryMode = config.containsKey("recoveryMode"); + compressionLevel = DataUtils.getConfigParam(config, "compress", 0); + String fileName = (String) config.get("fileName"); + FileStore fileStore = (FileStore) config.get("fileStore"); + fileStoreIsProvided = fileStore != null; + if(fileStore == null && fileName != null) { + fileStore = new FileStore(); + } + this.fileStore = fileStore; + + int pgSplitSize = 48; // for "mem:" case it is # of keys + CacheLongKeyLIRS.Config cc = null; + if (this.fileStore != null) { + int mb = DataUtils.getConfigParam(config, "cacheSize", 16); + if (mb > 0) { + cc = new CacheLongKeyLIRS.Config(); + cc.maxMemory = mb * 1024L * 1024L; + Object o = config.get("cacheConcurrency"); + if (o != null) { + cc.segmentCount = (Integer)o; + } + } + pgSplitSize = 16 * 1024; + } + if (cc != null) { + cache = new CacheLongKeyLIRS<>(cc); + } else { + cache = null; + } + + pgSplitSize = DataUtils.getConfigParam(config, "pageSplitSize", pgSplitSize); + // Make sure pages will fit into cache + if (cache != null && pgSplitSize > cache.getMaxItemSize()) { + pgSplitSize = (int)cache.getMaxItemSize(); + } + pageSplitSize = pgSplitSize; + keysPerPage = DataUtils.getConfigParam(config, "keysPerPage", 48); + backgroundExceptionHandler = + (UncaughtExceptionHandler)config.get("backgroundExceptionHandler"); + meta = new MVMap<>(this); + if (this.fileStore != null) { + retentionTime = this.fileStore.getDefaultRetentionTime(); + // 19 KB memory is about 1 KB storage + int kb = Math.max(1, Math.min(19, Utils.scaleForAvailableMemory(64))) * 1024; + kb = DataUtils.getConfigParam(config, "autoCommitBufferSize", kb); + autoCommitMemory = kb * 1024; + autoCompactFillRate = DataUtils.getConfigParam(config, "autoCompactFillRate", 90); + char[] encryptionKey = (char[]) config.get("encryptionKey"); + try { + if (!fileStoreIsProvided) { + boolean readOnly = config.containsKey("readOnly"); + this.fileStore.open(fileName, readOnly, encryptionKey); + } + if (this.fileStore.size() == 0) { + creationTime = getTimeAbsolute(); + lastCommitTime = creationTime; + storeHeader.put(HDR_H, 2); + storeHeader.put(HDR_BLOCK_SIZE, BLOCK_SIZE); + storeHeader.put(HDR_FORMAT, FORMAT_WRITE); + storeHeader.put(HDR_CREATED, creationTime); + writeStoreHeader(); + } else { + // there is no need to lock store here, since it is not opened yet, + // just to make some assertions happy, when they ensure single-threaded access + storeLock.lock(); + try { + readStoreHeader(); + } finally { + storeLock.unlock(); + } + } + } catch (IllegalStateException e) { + panic(e); + } finally { + if (encryptionKey != null) { + Arrays.fill(encryptionKey, (char) 0); + } + } + lastCommitTime = getTimeSinceCreation(); + + scrubMetaMap(); + + // setAutoCommitDelay starts the thread, but only if + // the parameter is different from the old value + int delay = DataUtils.getConfigParam(config, "autoCommitDelay", 1000); + setAutoCommitDelay(delay); + } else { + autoCommitMemory = 0; + autoCompactFillRate = 0; + } + } + + private void scrubMetaMap() { + Set keysToRemove = new HashSet<>(); + + // ensure that there is only one name mapped to this id + // this could be a leftover of an unfinished map rename + for (Iterator it = meta.keyIterator(DataUtils.META_NAME); it.hasNext();) { + String key = it.next(); + if (!key.startsWith(DataUtils.META_NAME)) { + break; + } + String mapName = key.substring(DataUtils.META_NAME.length()); + int mapId = DataUtils.parseHexInt(meta.get(key)); + String realMapName = getMapName(mapId); + if(!mapName.equals(realMapName)) { + keysToRemove.add(key); + } + } + + // remove roots of non-existent maps (leftover after unfinished map removal) + for (Iterator it = meta.keyIterator(DataUtils.META_ROOT); it.hasNext();) { + String key = it.next(); + if (!key.startsWith(DataUtils.META_ROOT)) { + break; + } + String mapIdStr = key.substring(key.lastIndexOf('.') + 1); + if(!meta.containsKey(DataUtils.META_MAP + mapIdStr)) { + meta.remove(key); + markMetaChanged(); + keysToRemove.add(key); + } + } + + for (String key : keysToRemove) { + meta.remove(key); + markMetaChanged(); + } + + for (Iterator it = meta.keyIterator(DataUtils.META_MAP); it.hasNext();) { + String key = it.next(); + if (!key.startsWith(DataUtils.META_MAP)) { + break; + } + String mapName = DataUtils.getMapName(meta.get(key)); + String mapIdStr = key.substring(DataUtils.META_MAP.length()); + // ensure that last map id is not smaller than max of any existing map ids + int mapId = DataUtils.parseHexInt(mapIdStr); + if (mapId > lastMapId.get()) { + lastMapId.set(mapId); + } + // each map should have a proper name + if(!mapIdStr.equals(meta.get(DataUtils.META_NAME + mapName))) { + meta.put(DataUtils.META_NAME + mapName, mapIdStr); + markMetaChanged(); + } + } + } + + private void panic(IllegalStateException e) { + if (isOpen()) { + handleException(e); + panicException = e; + closeImmediately(); + } + throw e; + } + + public IllegalStateException getPanicException() { + return panicException; + } + + /** + * Open a store in exclusive mode. For a file-based store, the parent + * directory must already exist. + * + * @param fileName the file name (null for in-memory) + * @return the store + */ + public static MVStore open(String fileName) { + HashMap config = new HashMap<>(); + config.put("fileName", fileName); + return new MVStore(config); + } + + /** + * Open a map with the default settings. The map is automatically create if + * it does not yet exist. If a map with this name is already open, this map + * is returned. + * + * @param the key type + * @param the value type + * @param name the name of the map + * @return the map + */ + public MVMap openMap(String name) { + return openMap(name, new MVMap.Builder()); + } + + /** + * Open a map with the given builder. The map is automatically create if it + * does not yet exist. If a map with this name is already open, this map is + * returned. + * + * @param the map type + * @param the key type + * @param the value type + * @param name the name of the map + * @param builder the map builder + * @return the map + */ + public , K, V> M openMap(String name, MVMap.MapBuilder builder) { + int id = getMapId(name); + M map; + if (id >= 0) { + map = openMap(id, builder); + assert builder.getKeyType() == null || map.getKeyType().getClass().equals(builder.getKeyType().getClass()); + assert builder.getValueType() == null || map.getValueType().getClass().equals(builder.getValueType() + .getClass()); + } else { + HashMap c = new HashMap<>(); + id = lastMapId.incrementAndGet(); + assert getMap(id) == null; + c.put("id", id); + c.put("createVersion", currentVersion); + map = builder.create(this, c); + String x = Integer.toHexString(id); + meta.put(MVMap.getMapKey(id), map.asString(name)); + meta.put(DataUtils.META_NAME + name, x); + map.setRootPos(0, lastStoredVersion); + markMetaChanged(); + @SuppressWarnings("unchecked") + M existingMap = (M) maps.putIfAbsent(id, map); + if (existingMap != null) { + map = existingMap; + } + } + return map; + } + + private , K, V> M openMap(int id, MVMap.MapBuilder builder) { + storeLock.lock(); + try { + @SuppressWarnings("unchecked") + M map = (M) getMap(id); + if (map == null) { + String configAsString = meta.get(MVMap.getMapKey(id)); + HashMap config; + if (configAsString != null) { + config = new HashMap(DataUtils.parseMap(configAsString)); + } else { + config = new HashMap<>(); + } + config.put("id", id); + map = builder.create(this, config); + long root = getRootPos(meta, id); + map.setRootPos(root, lastStoredVersion); + maps.put(id, map); + } + return map; + } finally { + storeLock.unlock(); + } + } + + /** + * Get map by id. + * + * @param the key type + * @param the value type + * @param id map id + * @return Map + */ + public MVMap getMap(int id) { + checkOpen(); + @SuppressWarnings("unchecked") + MVMap map = (MVMap) maps.get(id); + return map; + } + + /** + * Get the set of all map names. + * + * @return the set of names + */ + public Set getMapNames() { + HashSet set = new HashSet<>(); + checkOpen(); + for (Iterator it = meta.keyIterator(DataUtils.META_NAME); it.hasNext();) { + String x = it.next(); + if (!x.startsWith(DataUtils.META_NAME)) { + break; + } + String mapName = x.substring(DataUtils.META_NAME.length()); + set.add(mapName); + } + return set; + } + + /** + * Get the metadata map. This data is for informational purposes only. The + * data is subject to change in future versions. + *

+ * The data in this map should not be modified (changing system data may + * corrupt the store). If modifications are needed, they need be + * synchronized on the store. + *

+ * The metadata map contains the following entries: + *

+     * chunk.{chunkId} = {chunk metadata}
+     * name.{name} = {mapId}
+     * map.{mapId} = {map metadata}
+     * root.{mapId} = {root position}
+     * setting.storeVersion = {version}
+     * 
+ * + * @return the metadata map + */ + public MVMap getMetaMap() { + checkOpen(); + return meta; + } + + private MVMap getMetaMap(long version) { + Chunk c = getChunkForVersion(version); + DataUtils.checkArgument(c != null, "Unknown version {0}", version); + long block = c.block; + c = readChunkHeader(block); + MVMap oldMeta = meta.openReadOnly(c.metaRootPos, version); + return oldMeta; + } + + private Chunk getChunkForVersion(long version) { + Chunk newest = null; + for (Chunk c : chunks.values()) { + if (c.version <= version) { + if (newest == null || c.id > newest.id) { + newest = c; + } + } + } + return newest; + } + + /** + * Check whether a given map exists. + * + * @param name the map name + * @return true if it exists + */ + public boolean hasMap(String name) { + return meta.containsKey(DataUtils.META_NAME + name); + } + + /** + * Check whether a given map exists and has data. + * + * @param name the map name + * @return true if it exists and has data. + */ + public boolean hasData(String name) { + return hasMap(name) && getRootPos(meta, getMapId(name)) != 0; + } + + private void markMetaChanged() { + // changes in the metadata alone are usually not detected, as the meta + // map is changed after storing + metaChanged = true; + } + + private void readStoreHeader() { + Chunk newest = null; + boolean assumeCleanShutdown = true; + boolean validStoreHeader = false; + // find out which chunk and version are the newest + // read the first two blocks + ByteBuffer fileHeaderBlocks = fileStore.readFully(0, 2 * BLOCK_SIZE); + byte[] buff = new byte[BLOCK_SIZE]; + for (int i = 0; i <= BLOCK_SIZE; i += BLOCK_SIZE) { + fileHeaderBlocks.get(buff); + // the following can fail for various reasons + try { + HashMap m = DataUtils.parseChecksummedMap(buff); + if (m == null) { + assumeCleanShutdown = false; + continue; + } + long version = DataUtils.readHexLong(m, HDR_VERSION, 0); + // if both header blocks do agree on version + // we'll continue on happy path - assume that previous shutdown was clean + assumeCleanShutdown = assumeCleanShutdown && (newest == null || version == newest.version); + if (newest == null || version > newest.version) { + validStoreHeader = true; + storeHeader.putAll(m); + creationTime = DataUtils.readHexLong(m, HDR_CREATED, 0); + int chunkId = DataUtils.readHexInt(m, HDR_CHUNK, 0); + long block = DataUtils.readHexLong(m, HDR_BLOCK, 0); + Chunk test = readChunkHeaderAndFooter(block, chunkId); + if (test != null) { + newest = test; + } + } + } catch (Exception ignore) { + assumeCleanShutdown = false; + } + } + + if (!validStoreHeader) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_CORRUPT, + "Store header is corrupt: {0}", fileStore); + } + int blockSize = DataUtils.readHexInt(storeHeader, HDR_BLOCK_SIZE, BLOCK_SIZE); + if (blockSize != BLOCK_SIZE) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_UNSUPPORTED_FORMAT, + "Block size {0} is currently not supported", + blockSize); + } + long format = DataUtils.readHexLong(storeHeader, HDR_FORMAT, 1); + if (format > FORMAT_WRITE && !fileStore.isReadOnly()) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_UNSUPPORTED_FORMAT, + "The write format {0} is larger " + + "than the supported format {1}, " + + "and the file was not opened in read-only mode", + format, FORMAT_WRITE); + } + format = DataUtils.readHexLong(storeHeader, HDR_FORMAT_READ, format); + if (format > FORMAT_READ) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_UNSUPPORTED_FORMAT, + "The read format {0} is larger " + + "than the supported format {1}", + format, FORMAT_READ); + } + + assumeCleanShutdown = assumeCleanShutdown && newest != null + && DataUtils.readHexInt(storeHeader, HDR_CLEAN, 0) != 0 + && !recoveryMode; + lastStoredVersion = INITIAL_VERSION; + chunks.clear(); + long now = System.currentTimeMillis(); + // calculate the year (doesn't have to be exact; + // we assume 365.25 days per year, * 4 = 1461) + int year = 1970 + (int) (now / (1000L * 60 * 60 * 6 * 1461)); + if (year < 2014) { + // if the year is before 2014, + // we assume the system doesn't have a real-time clock, + // and we set the creationTime to the past, so that + // existing chunks are overwritten + creationTime = now - fileStore.getDefaultRetentionTime(); + } else if (now < creationTime) { + // the system time was set to the past: + // we change the creation time + creationTime = now; + storeHeader.put(HDR_CREATED, creationTime); + } + + long fileSize = fileStore.size(); + long blocksInStore = fileSize / BLOCK_SIZE; + + Comparator chunkComparator = new Comparator() { + @Override + public int compare(Chunk one, Chunk two) { + int result = Long.compare(two.version, one.version); + if (result == 0) { + // out of two copies of the same chunk we prefer the one + // close to the beginning of file (presumably later version) + result = Long.compare(one.block, two.block); + } + return result; + } + }; + + if (!assumeCleanShutdown) { + Chunk tailChunk = discoverChunk(blocksInStore); + if (tailChunk != null) { + blocksInStore = tailChunk.block; // for a possible full scan later on + if (newest == null || tailChunk.version > newest.version) { + newest = tailChunk; + } + } + } + + Map validChunksByLocation = new HashMap<>(); + if (newest != null) { + // read the chunk header and footer, + // and follow the chain of next chunks + while (true) { + validChunksByLocation.put(newest.block, newest); + if (newest.next == 0 || newest.next >= blocksInStore) { + // no (valid) next + break; + } + Chunk test = readChunkHeaderAndFooter(newest.next, newest.id + 1); + if (test == null || test.version <= newest.version) { + break; + } + // if shutdown was really clean then chain should be empty + assumeCleanShutdown = false; + newest = test; + } + } + + if (assumeCleanShutdown) { + setLastChunk(newest); + // quickly check latest 20 chunks referenced in meta table + Queue chunksToVerify = new PriorityQueue<>(20, Collections.reverseOrder(chunkComparator)); + try { + // load the chunk metadata: although meta's root page resides in the lastChunk, + // traversing meta map might recursively load another chunk(s) + Cursor cursor = meta.cursor(DataUtils.META_CHUNK); + while (cursor.hasNext() && cursor.next().startsWith(DataUtils.META_CHUNK)) { + Chunk c = Chunk.fromString(cursor.getValue()); + assert c.version <= currentVersion; + // might be there already, due to meta traversal + // see readPage() ... getChunkIfFound() + chunks.putIfAbsent(c.id, c); + chunksToVerify.offer(c); + if (chunksToVerify.size() == 20) { + chunksToVerify.poll(); + } + } + Chunk c; + while (assumeCleanShutdown && (c = chunksToVerify.poll()) != null) { + assumeCleanShutdown = readChunkHeaderAndFooter(c.block, c.id) != null; + } + } catch(IllegalStateException ignored) { + assumeCleanShutdown = false; + } + } + + if (!assumeCleanShutdown) { + boolean quickRecovery = false; + if (!recoveryMode) { + // now we know, that previous shutdown did not go well and file + // is possibly corrupted but there is still hope for a quick + // recovery + + // this collection will hold potential candidates for lastChunk to fall back to, + // in order from the most to least likely + Chunk[] lastChunkCandidates = validChunksByLocation.values().toArray(new Chunk[0]); + Arrays.sort(lastChunkCandidates, chunkComparator); + Map validChunksById = new HashMap<>(); + for (Chunk chunk : lastChunkCandidates) { + validChunksById.put(chunk.id, chunk); + } + quickRecovery = findLastChunkWithCompleteValidChunkSet(lastChunkCandidates, validChunksByLocation, + validChunksById, false); + } + + if (!quickRecovery) { + // scan whole file and try to fetch chunk header and/or footer out of every block + // matching pairs with nothing in-between are considered as valid chunk + long block = blocksInStore; + Chunk tailChunk; + while ((tailChunk = discoverChunk(block)) != null) { + block = tailChunk.block; + validChunksByLocation.put(block, tailChunk); + } + + // this collection will hold potential candidates for lastChunk to fall back to, + // in order from the most to least likely + Chunk[] lastChunkCandidates = validChunksByLocation.values().toArray(new Chunk[0]); + Arrays.sort(lastChunkCandidates, chunkComparator); + Map validChunksById = new HashMap<>(); + for (Chunk chunk : lastChunkCandidates) { + validChunksById.put(chunk.id, chunk); + } + findLastChunkWithCompleteValidChunkSet(lastChunkCandidates, validChunksByLocation, + validChunksById, true); + } + } + + fileStore.clear(); + // build the free space list + for (Chunk c : chunks.values()) { + if (c.isSaved()) { + long start = c.block * BLOCK_SIZE; + int length = c.len * BLOCK_SIZE; + fileStore.markUsed(start, length); + } + if (!c.isLive()) { + deadChunks.offer(c); + } + } + assert validateFileLength("on open"); + setWriteVersion(currentVersion); + if (lastStoredVersion == INITIAL_VERSION) { + lastStoredVersion = currentVersion - 1; + } + } + + private boolean findLastChunkWithCompleteValidChunkSet(Chunk[] lastChunkCandidates, + Map validChunksByLocation, + Map validChunksById, + boolean afterFullScan) { + // Try candidates for "last chunk" in order from newest to oldest + // until suitable is found. Suitable one should have meta map + // where all chunk references point to valid locations. + for (Chunk chunk : lastChunkCandidates) { + boolean verified = true; + try { + setLastChunk(chunk); + // load the chunk metadata: although meta's root page resides in the lastChunk, + // traversing meta map might recursively load another chunk(s) + Cursor cursor = meta.cursor(DataUtils.META_CHUNK); + while (cursor.hasNext() && cursor.next().startsWith(DataUtils.META_CHUNK)) { + Chunk c = Chunk.fromString(cursor.getValue()); + assert c.version <= currentVersion; + // might be there already, due to meta traversal + // see readPage() ... getChunkIfFound() + Chunk test = chunks.putIfAbsent(c.id, c); + if (test != null) { + c = test; + } + assert chunks.get(c.id) == c; + if ((test = validChunksByLocation.get(c.block)) == null || test.id != c.id) { + if ((test = validChunksById.get(c.id)) != null) { + // We do not have a valid chunk at that location, + // but there is a copy of same chunk from original + // location. + // Chunk header at original location does not have + // any dynamic (occupancy) metadata, so it can't be + // used here as is, re-point our chunk to original + // location instead. + c.block = test.block; + } else if (!c.isLive()) { + // we can just remove entry from meta, referencing to this chunk, + // but store maybe R/O, and it's not properly started yet, + // so lets make this chunk "dead" and taking no space, + // and it will be automatically removed later. + c.block = Long.MAX_VALUE; + c.len = Integer.MAX_VALUE; + if (c.unused == 0) { + c.unused = creationTime; + } + if (c.unusedAtVersion == 0) { + c.unusedAtVersion = INITIAL_VERSION; + } + } else if (afterFullScan || readChunkHeaderAndFooter(c.block, c.id) == null) { + // chunk reference is invalid + // this "last chunk" candidate is not suitable + verified = false; + break; + } + } + } + } catch(Exception ignored) { + verified = false; + } + if (verified) { + return true; + } + } + return false; + } + + private void setLastChunk(Chunk last) { + chunks.clear(); + lastChunk = last; + if (last == null) { + // no valid chunk + lastMapId.set(0); + currentVersion = 0; + lastStoredVersion = INITIAL_VERSION; + meta.setRootPos(0, INITIAL_VERSION); + } else { + lastMapId.set(last.mapId); + currentVersion = last.version; + chunks.put(last.id, last); + lastStoredVersion = currentVersion - 1; + meta.setRootPos(last.metaRootPos, lastStoredVersion); + } + } + + /** + * Discover a valid chunk, searching file backwards from the given block + * + * @param block to start search from (found chunk footer should be no + * further than block-1) + * @return valid chunk or null if none found + */ + private Chunk discoverChunk(long block) { + long candidateLocation = Long.MAX_VALUE; + Chunk candidate = null; + while (true) { + if (block == candidateLocation) { + return candidate; + } + if (block == 2) { // number of blocks occupied by headers + return null; + } + Chunk test = readChunkFooter(block); + if (test != null) { + // if we encounter chunk footer (with or without corresponding header) + // in the middle of prospective chunk, stop considering it + candidateLocation = Long.MAX_VALUE; + test = readChunkHeaderOptionally(test.block, test.id); + if (test != null) { + // if that footer has a corresponding header, + // consider them as a new candidate for a valid chunk + candidate = test; + candidateLocation = test.block; + } + } + + // if we encounter chunk header without corresponding footer + // (due to incomplete write?) in the middle of prospective + // chunk, stop considering it + if (--block > candidateLocation && readChunkHeaderOptionally(block) != null) { + candidateLocation = Long.MAX_VALUE; + } + } + } + + + /** + * Read a chunk header and footer, and verify the stored data is consistent. + * + * @param block the block + * @param expectedId of the chunk + * @return the chunk, or null if the header or footer don't match or are not + * consistent + */ + private Chunk readChunkHeaderAndFooter(long block, int expectedId) { + Chunk header = readChunkHeaderOptionally(block, expectedId); + if (header != null) { + Chunk footer = readChunkFooter(block + header.len); + if (footer == null || footer.id != expectedId || footer.block != header.block) { + return null; + } + } + return header; + } + + /** + * Try to read a chunk footer. + * + * @param block the index of the next block after the chunk + * @return the chunk, or null if not successful + */ + private Chunk readChunkFooter(long block) { + // the following can fail for various reasons + try { + // read the chunk footer of the last block of the file + long pos = block * BLOCK_SIZE - Chunk.FOOTER_LENGTH; + if(pos < 0) { + return null; + } + ByteBuffer lastBlock = fileStore.readFully(pos, Chunk.FOOTER_LENGTH); + byte[] buff = new byte[Chunk.FOOTER_LENGTH]; + lastBlock.get(buff); + HashMap m = DataUtils.parseChecksummedMap(buff); + if (m != null) { + int chunk = DataUtils.readHexInt(m, HDR_CHUNK, 0); + Chunk c = new Chunk(chunk); + c.version = DataUtils.readHexLong(m, HDR_VERSION, 0); + c.block = DataUtils.readHexLong(m, HDR_BLOCK, 0); + return c; + } + } catch (Exception e) { + // ignore + } + return null; + } + + private void writeStoreHeader() { + StringBuilder buff = new StringBuilder(112); + if (lastChunk != null) { + storeHeader.put(HDR_BLOCK, lastChunk.block); + storeHeader.put(HDR_CHUNK, lastChunk.id); + storeHeader.put(HDR_VERSION, lastChunk.version); + } + DataUtils.appendMap(buff, storeHeader); + byte[] bytes = buff.toString().getBytes(StandardCharsets.ISO_8859_1); + int checksum = DataUtils.getFletcher32(bytes, 0, bytes.length); + DataUtils.appendMap(buff, HDR_FLETCHER, checksum); + buff.append('\n'); + bytes = buff.toString().getBytes(StandardCharsets.ISO_8859_1); + ByteBuffer header = ByteBuffer.allocate(2 * BLOCK_SIZE); + header.put(bytes); + header.position(BLOCK_SIZE); + header.put(bytes); + header.rewind(); + write(0, header); + } + + private void write(long pos, ByteBuffer buffer) { + try { + fileStore.writeFully(pos, buffer); + } catch (IllegalStateException e) { + panic(e); + } + } + + /** + * Close the file and the store. Unsaved changes are written to disk first. + */ + @Override + public void close() { + closeStore(true, 0); + } + + /** + * Close the file and the store. Unsaved changes are written to disk first, + * and compaction (up to a specified number of milliseconds) is attempted. + * + * @param allowedCompactionTime the allowed time for compaction (in + * milliseconds) + */ + public void close(long allowedCompactionTime) { + closeStore(true, allowedCompactionTime); + } + + /** + * Close the file and the store, without writing anything. + * This will try to stop the background thread (without waiting for it). + * This method ignores all errors. + */ + public void closeImmediately() { + try { + closeStore(false, 0); + } catch (Throwable e) { + handleException(e); + } + } + + private void closeStore(boolean normalShutdown, long allowedCompactionTime) { + // If any other thead have already initiated closure procedure, + // isClosed() would wait until closure is done and then we jump out of the loop. + // This is a subtle difference between !isClosed() and isOpen(). + while (!isClosed()) { + stopBackgroundThread(normalShutdown); + storeLock.lock(); + try { + if (state == STATE_OPEN) { + state = STATE_STOPPING; + try { + try { + if (normalShutdown && fileStore != null && !fileStore.isReadOnly()) { + for (MVMap map : maps.values()) { + if (map.isClosed()) { + deregisterMapRoot(map.getId()); + } + } + setRetentionTime(0); + commit(); + if (allowedCompactionTime > 0) { + compactFile(allowedCompactionTime); + } else if (allowedCompactionTime < 0) { + doMaintenance(autoCompactFillRate); + } + shrinkFileIfPossible(0); + storeHeader.put(HDR_CLEAN, 1); + writeStoreHeader(); + sync(); + assert validateFileLength("on close"); + } + + state = STATE_CLOSING; + + // release memory early - this is important when called + // because of out of memory + clearCaches(); + for (MVMap m : new ArrayList<>(maps.values())) { + m.close(); + } + chunks.clear(); + maps.clear(); + } finally { + if (fileStore != null && !fileStoreIsProvided) { + fileStore.close(); + } + } + } finally { + state = STATE_CLOSED; + } + } + } finally { + storeLock.unlock(); + } + } + } + + /** + * Read a page of data into a ByteBuffer. + * + * @param pos page pos + * @param expectedMapId expected map id for the page + * @return ByteBuffer containing page data. + */ + private ByteBuffer readBufferForPage(long pos, int expectedMapId) { + return getChunk(pos).readBufferForPage(fileStore, pos, expectedMapId); + } + + /** + * Get the chunk for the given position. + * + * @param pos the position + * @return the chunk + */ + private Chunk getChunk(long pos) { + int chunkId = DataUtils.getPageChunkId(pos); + Chunk c = chunks.get(chunkId); + if (c == null) { + checkOpen(); + String s = meta.get(Chunk.getMetaKey(chunkId)); + if (s == null) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_CHUNK_NOT_FOUND, + "Chunk {0} not found", chunkId); + } + c = Chunk.fromString(s); + if (!c.isSaved()) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_CORRUPT, + "Chunk {0} is invalid", chunkId); + } + chunks.put(c.id, c); + } + return c; + } + + private void setWriteVersion(long version) { + for (Iterator> iter = maps.values().iterator(); iter.hasNext(); ) { + MVMap map = iter.next(); + assert map != meta; + if (map.setWriteVersion(version) == null) { + iter.remove(); + } + } + meta.setWriteVersion(version); + onVersionChange(version); + } + + /** + * Unlike regular commit this method returns immediately if there is commit + * in progress on another thread, otherwise it acts as regular commit. + * + * This method may return BEFORE this thread changes are actually persisted! + * + * @return the new version (incremented if there were changes) + */ + public long tryCommit() { + // we need to prevent re-entrance, which may be possible, + // because meta map is modified within storeNow() and that + // causes beforeWrite() call with possibility of going back here + if ((!storeLock.isHeldByCurrentThread() || currentStoreVersion < 0) && + storeLock.tryLock()) { + try { + store(); + } finally { + storeLock.unlock(); + } + } + return currentVersion; + } + + /** + * Commit the changes. + *

+ * This method does nothing if there are no unsaved changes, + * otherwise it increments the current version + * and stores the data (for file based stores). + *

+ * It is not necessary to call this method when auto-commit is enabled (the default + * setting), as in this case it is automatically called from time to time or + * when enough changes have accumulated. However, it may still be called to + * flush all changes to disk. + *

+ * At most one store operation may run at any time. + * + * @return the new version (incremented if there were changes) + */ + public long commit() { + // we need to prevent re-entrance, which may be possible, + // because meta map is modified within storeNow() and that + // causes beforeWrite() call with possibility of going back here + if(!storeLock.isHeldByCurrentThread() || currentStoreVersion < 0) { + storeLock.lock(); + try { + store(); + } finally { + storeLock.unlock(); + } + } + return currentVersion; + } + + private void store() { + store(0, reuseSpace ? 0 : getAfterLastBlock()); + } + + private void store(long reservedLow, long reservedHigh) { + assert storeLock.isHeldByCurrentThread(); + if (isOpenOrStopping()) { + if (hasUnsavedChanges()) { + dropUnusedChunks(); + try { + currentStoreVersion = currentVersion; + if (fileStore == null) { + lastStoredVersion = currentVersion; + //noinspection NonAtomicOperationOnVolatileField + ++currentVersion; + setWriteVersion(currentVersion); + metaChanged = false; + } else { + if (fileStore.isReadOnly()) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_WRITING_FAILED, "This store is read-only"); + } + try { + storeNow(reservedLow, reservedHigh); + } catch (IllegalStateException e) { + panic(e); + } catch (Throwable e) { + panic(DataUtils.newIllegalStateException(DataUtils.ERROR_INTERNAL, "{0}", e.toString(), + e)); + } + } + } finally { + // in any case reset the current store version, + // to allow closing the store + currentStoreVersion = -1; + } + } + } + } + + private void storeNow(long reservedLow, long reservedHigh) { + long time = getTimeSinceCreation(); + int currentUnsavedPageCount = unsavedMemory; + long storeVersion = currentStoreVersion; + long version = ++currentVersion; + lastCommitTime = time; + + // the metadata of the last chunk was not stored so far, and needs to be + // set now (it's better not to update right after storing, because that + // would modify the meta map again) + int lastChunkId; + if (lastChunk == null) { + lastChunkId = 0; + } else { + lastChunkId = lastChunk.id; + meta.put(Chunk.getMetaKey(lastChunkId), lastChunk.asString()); + markMetaChanged(); + // never go backward in time + time = Math.max(lastChunk.time, time); + } + int newChunkId = lastChunkId; + while (true) { + newChunkId = (newChunkId + 1) & Chunk.MAX_ID; + Chunk old = chunks.get(newChunkId); + if (old == null) { + break; + } + if (!old.isSaved()) { + IllegalStateException e = DataUtils.newIllegalStateException( + DataUtils.ERROR_INTERNAL, + "Last block {0} not stored, possibly due to out-of-memory", old); + panic(e); + } + } + Chunk c = new Chunk(newChunkId); + c.pageCount = 0; + c.pageCountLive = 0; + c.maxLen = 0; + c.maxLenLive = 0; + c.metaRootPos = Long.MAX_VALUE; + c.block = Long.MAX_VALUE; + c.len = Integer.MAX_VALUE; + c.time = time; + c.version = version; + c.next = Long.MAX_VALUE; + chunks.put(c.id, c); + ArrayList changed = new ArrayList<>(); + for (Iterator> iter = maps.values().iterator(); iter.hasNext(); ) { + MVMap map = iter.next(); + RootReference rootReference = map.setWriteVersion(version); + if (rootReference == null) { + iter.remove(); + } else if (map.getCreateVersion() <= storeVersion && // if map was created after storing started, skip it + !map.isVolatile() && + map.hasChangesSince(lastStoredVersion)) { + assert rootReference.version <= version : rootReference.version + " > " + version; + Page rootPage = rootReference.root; + if (!rootPage.isSaved() || + // after deletion previously saved leaf + // may pop up as a root, but we still need + // to save new root pos in meta + rootPage.isLeaf()) { + changed.add(rootPage); + } + } + } + WriteBuffer buff = getWriteBuffer(); + // need to patch the header later + c.writeChunkHeader(buff, 0); + int headerLength = buff.position() + 44; + buff.position(headerLength); + for (Page p : changed) { + String key = MVMap.getMapRootKey(p.getMapId()); + if (p.getTotalCount() == 0) { + meta.remove(key); + } else { + p.writeUnsavedRecursive(c, buff); + long root = p.getPos(); + meta.put(key, Long.toHexString(root)); + } + } + + acceptChunkOccupancyChanges(time, version); + + RootReference metaRootReference = meta.setWriteVersion(version); + assert metaRootReference != null; + assert metaRootReference.version == version : metaRootReference.version + " != " + version; + metaChanged = false; + + acceptChunkOccupancyChanges(time, version); + + onVersionChange(version); + + Page metaRoot = metaRootReference.root; + metaRoot.writeUnsavedRecursive(c, buff); + + // last allocated map id should be captured after the meta map was saved, because + // this will ensure that concurrently created map, which made it into meta before save, + // will have it's id reflected in mapid field of currently written chunk + c.mapId = lastMapId.get(); + + int chunkLength = buff.position(); + + // add the store header and round to the next block + int length = MathUtils.roundUpInt(chunkLength + + Chunk.FOOTER_LENGTH, BLOCK_SIZE); + buff.limit(length); + + long filePos = fileStore.allocate(length, reservedLow, reservedHigh); + c.block = filePos / BLOCK_SIZE; + c.len = length / BLOCK_SIZE; + assert validateFileLength(c.asString()); + c.metaRootPos = metaRoot.getPos(); + // calculate and set the likely next position + if (reservedLow > 0 || reservedHigh == reservedLow) { + c.next = fileStore.predictAllocation(c.len, 0, 0); + } else { + // just after this chunk + c.next = 0; + } + assert c.pageCountLive == c.pageCount : c; + buff.position(0); + c.writeChunkHeader(buff, headerLength); + + buff.position(buff.limit() - Chunk.FOOTER_LENGTH); + buff.put(c.getFooterBytes()); + + buff.position(0); + write(filePos, buff.getBuffer()); + releaseWriteBuffer(buff); + + // whether we need to write the store header + boolean writeStoreHeader = false; + // end of the used space is not necessarily the end of the file + boolean storeAtEndOfFile = filePos + length >= fileStore.size(); + if (!storeAtEndOfFile) { + if (lastChunk == null) { + writeStoreHeader = true; + } else if (lastChunk.next != c.block) { + // the last prediction did not matched + writeStoreHeader = true; + } else { + long headerVersion = DataUtils.readHexLong(storeHeader, HDR_VERSION, 0); + if (lastChunk.version - headerVersion > 20) { + // we write after at least every 20 versions + writeStoreHeader = true; + } else { + int chunkId = DataUtils.readHexInt(storeHeader, HDR_CHUNK, 0); + while (true) { + Chunk old = chunks.get(chunkId); + if (old == null) { + // one of the chunks in between + // was removed + writeStoreHeader = true; + break; + } + if (chunkId == lastChunk.id) { + break; + } + chunkId++; + } + } + } + } + + if (storeHeader.remove(HDR_CLEAN) != null) { + writeStoreHeader = true; + } + + lastChunk = c; + if (writeStoreHeader) { + writeStoreHeader(); + } + if (!storeAtEndOfFile) { + // may only shrink after the store header was written + shrinkFileIfPossible(1); + } + for (Page p : changed) { + p.writeEnd(); + } + metaRoot.writeEnd(); + + // some pages might have been changed in the meantime (in the newest + // version) + saveNeeded = false; + unsavedMemory = Math.max(0, unsavedMemory - currentUnsavedPageCount); + lastStoredVersion = storeVersion; + } + + /** + * Get a buffer for writing. This caller must synchronize on the store + * before calling the method and until after using the buffer. + * + * @return the buffer + */ + private WriteBuffer getWriteBuffer() { + WriteBuffer buff; + if (writeBuffer != null) { + buff = writeBuffer; + buff.clear(); + } else { + buff = new WriteBuffer(); + } + return buff; + } + + /** + * Release a buffer for writing. This caller must synchronize on the store + * before calling the method and until after using the buffer. + * + * @param buff the buffer than can be re-used + */ + private void releaseWriteBuffer(WriteBuffer buff) { + if (buff.capacity() <= 4 * 1024 * 1024) { + writeBuffer = buff; + } + } + + private static boolean canOverwriteChunk(Chunk c, long oldestVersionToKeep) { + return !c.isLive() && c.unusedAtVersion < oldestVersionToKeep; + } + + private boolean isSeasonedChunk(Chunk chunk, long time) { + return retentionTime < 0 || chunk.time + retentionTime <= time; + } + + private long getTimeSinceCreation() { + return Math.max(0, getTimeAbsolute() - creationTime); + } + + private long getTimeAbsolute() { + long now = System.currentTimeMillis(); + if (lastTimeAbsolute != 0 && now < lastTimeAbsolute) { + // time seems to have run backwards - this can happen + // when the system time is adjusted, for example + // on a leap second + now = lastTimeAbsolute; + } else { + lastTimeAbsolute = now; + } + return now; + } + + /** + * Apply the freed space to the chunk metadata. The metadata is updated, but + * completely free chunks are not removed from the set of chunks, and the + * disk space is not yet marked as free. They are queued instead and wait until + * their usage is over. + */ + private void acceptChunkOccupancyChanges(long time, long version) { + Set modifiedChunks = new HashSet<>(); + while (true) { + RemovedPageInfo rpi; + while ((rpi = removedPages.peek()) != null && rpi.version < version) { + rpi = removedPages.poll(); // could be different from the peeked one + assert rpi != null; // since nobody else retrieves from queue + assert rpi.version < version : rpi + " < " + version; + int chunkId = rpi.getPageChunkId(); + Chunk chunk = chunks.get(chunkId); + assert chunk != null; + modifiedChunks.add(chunk); + if (chunk.accountForRemovedPage(rpi.getPageLength(), rpi.isPinned(), time, rpi.version)) { + deadChunks.offer(chunk); + } + } + if (modifiedChunks.isEmpty()) { + return; + } + for (Chunk chunk : modifiedChunks) { + int chunkId = chunk.id; + meta.put(Chunk.getMetaKey(chunkId), chunk.asString()); + } + markMetaChanged(); + modifiedChunks.clear(); + } + } + + /** + * Shrink the file if possible, and if at least a given percentage can be + * saved. + * + * @param minPercent the minimum percentage to save + */ + private void shrinkFileIfPossible(int minPercent) { + if (fileStore.isReadOnly()) { + return; + } + long end = getFileLengthInUse(); + long fileSize = fileStore.size(); + if (end >= fileSize) { + return; + } + if (minPercent > 0 && fileSize - end < BLOCK_SIZE) { + return; + } + int savedPercent = (int) (100 - (end * 100 / fileSize)); + if (savedPercent < minPercent) { + return; + } + if (isOpenOrStopping()) { + sync(); + } + fileStore.truncate(end); + } + + /** + * Get the position right after the last used byte. + * + * @return the position + */ + private long getFileLengthInUse() { + long result = fileStore.getFileLengthInUse(); + assert result == measureFileLengthInUse() : result + " != " + measureFileLengthInUse(); + return result; + } + + /** + * Get the index of the first block after last occupied one. + * It marks the beginning of the last (infinite) free space. + * + * @return block index + */ + private long getAfterLastBlock() { + return fileStore.getAfterLastBlock(); + } + + private long measureFileLengthInUse() { + long size = 2; + for (Chunk c : chunks.values()) { + if (c.isSaved()) { + size = Math.max(size, c.block + c.len); + } + } + return size * BLOCK_SIZE; + } + + /** + * Check whether there are any unsaved changes. + * + * @return if there are any changes + */ + public boolean hasUnsavedChanges() { + if (metaChanged) { + return true; + } + for (MVMap m : maps.values()) { + if (!m.isClosed()) { + if(m.hasChangesSince(lastStoredVersion)) { + return true; + } + } + } + return false; + } + + private Chunk readChunkHeader(long block) { + long p = block * BLOCK_SIZE; + ByteBuffer buff = fileStore.readFully(p, Chunk.MAX_HEADER_LENGTH); + return Chunk.readChunkHeader(buff, p); + } + + private Chunk readChunkHeaderOptionally(long block) { + try { + Chunk chunk = readChunkHeader(block); + return chunk.block != block ? null : chunk; + } catch (Exception ignore) { + return null; + } + } + + private Chunk readChunkHeaderOptionally(long block, int expectedId) { + Chunk chunk = readChunkHeaderOptionally(block); + return chunk == null || chunk.id != expectedId ? null : chunk; + } + + /** + * Compact by moving all chunks next to each other. + */ + public void compactMoveChunks() { + compactMoveChunks(100, Long.MAX_VALUE); + } + + /** + * Compact the store by moving all chunks next to each other, if there is + * free space between chunks. This might temporarily increase the file size. + * Chunks are overwritten irrespective of the current retention time. Before + * overwriting chunks and before resizing the file, syncFile() is called. + * + * @param targetFillRate do nothing if the file store fill rate is higher + * than this + * @param moveSize the number of bytes to move + */ + private void compactMoveChunks(int targetFillRate, long moveSize) { + storeLock.lock(); + try { + checkOpen(); + if (lastChunk != null && reuseSpace) { + int oldRetentionTime = retentionTime; + boolean oldReuse = reuseSpace; + try { + retentionTime = -1; + if (getFillRate() <= targetFillRate) { + compactMoveChunks(moveSize); + } + } finally { + reuseSpace = oldReuse; + retentionTime = oldRetentionTime; + } + } + } finally { + storeLock.unlock(); + } + } + + private boolean compactMoveChunks(long moveSize) { + dropUnusedChunks(); + long start = fileStore.getFirstFree() / BLOCK_SIZE; + Iterable chunksToMove = findChunksToMove(start, moveSize); + if (chunksToMove == null) { + return false; + } + compactMoveChunks(chunksToMove); + return true; + } + + private Iterable findChunksToMove(long startBlock, long moveSize) { + long maxBlocksToMove = moveSize / BLOCK_SIZE; + Iterable result = null; + if (maxBlocksToMove > 0) { + PriorityQueue queue = new PriorityQueue<>(chunks.size() / 2 + 1, + new Comparator() { + @Override + public int compare(Chunk o1, Chunk o2) { + // instead of selection just closest to beginning of the file, + // pick smaller chunk(s) which sit in between bigger holes + int res = Integer.compare(o2.collectPriority, o1.collectPriority); + if (res != 0) { + return res; + } + return Long.signum(o2.block - o1.block); + } + }); + long size = 0; + for (Chunk chunk : chunks.values()) { + if (chunk.isSaved() && chunk.block > startBlock) { + chunk.collectPriority = getMovePriority(chunk); + queue.offer(chunk); + size += chunk.len; + while (size > maxBlocksToMove) { + Chunk removed = queue.poll(); + if (removed == null) { + break; + } + size -= removed.len; + } + } + } + if (!queue.isEmpty()) { + ArrayList list = new ArrayList<>(queue); + Collections.sort(list, Chunk.PositionComparator.INSTANCE); + result = list; + } + } + return result; + } + + private int getMovePriority(Chunk chunk) { + return fileStore.getMovePriority((int)chunk.block); + } + + private void compactMoveChunks(Iterable move) { + assert storeLock.isHeldByCurrentThread(); + if (move != null) { + assert lastChunk != null; + // this will ensure better recognition of the last chunk + // in case of power failure, since we are going to move older chunks + // to the end of the file + writeStoreHeader(); + sync(); + + Iterator iterator = move.iterator(); + assert iterator.hasNext(); + long leftmostBlock = iterator.next().block; + long originalBlockCount = getAfterLastBlock(); + // we need to ensure that chunks moved within the following loop + // do not overlap with space just released by chunks moved before them, + // hence the need to reserve this area [leftmostBlock, originalBlockCount) + for (Chunk chunk : move) { + moveChunk(chunk, leftmostBlock, originalBlockCount); + } + // update the metadata (hopefully within the file) + store(leftmostBlock, originalBlockCount); + sync(); + + Chunk chunkToMove = lastChunk; + long postEvacuationBlockCount = getAfterLastBlock(); + + boolean chunkToMoveIsAlreadyInside = chunkToMove.block < leftmostBlock; + boolean movedToEOF = !chunkToMoveIsAlreadyInside; + // move all chunks, which previously did not fit before reserved area + // now we can re-use previously reserved area [leftmostBlock, originalBlockCount), + // but need to reserve [originalBlockCount, postEvacuationBlockCount) + for (Chunk c : move) { + if (c.block >= originalBlockCount && + moveChunk(c, originalBlockCount, postEvacuationBlockCount)) { + assert c.block < originalBlockCount; + movedToEOF = true; + } + } + assert postEvacuationBlockCount >= getAfterLastBlock(); + + if (movedToEOF) { + boolean moved = moveChunkInside(chunkToMove, originalBlockCount); + + // store a new chunk with updated metadata (hopefully within a file) + store(originalBlockCount, postEvacuationBlockCount); + sync(); + // if chunkToMove did not fit within originalBlockCount (move is + // false), and since now previously reserved area + // [originalBlockCount, postEvacuationBlockCount) also can be + // used, lets try to move that chunk into this area, closer to + // the beginning of the file + long lastBoundary = moved || chunkToMoveIsAlreadyInside ? + postEvacuationBlockCount : chunkToMove.block; + moved = !moved && moveChunkInside(chunkToMove, lastBoundary); + if (moveChunkInside(lastChunk, lastBoundary) || moved) { + store(lastBoundary, -1); + } + } + + shrinkFileIfPossible(0); + sync(); + } + } + + private boolean moveChunkInside(Chunk chunkToMove, long boundary) { + boolean res = chunkToMove.block >= boundary && + fileStore.predictAllocation(chunkToMove.len, boundary, -1) < boundary && + moveChunk(chunkToMove, boundary, -1); + assert !res || chunkToMove.block + chunkToMove.len <= boundary; + return res; + } + + /** + * Move specified chunk into free area of the file. "Reserved" area + * specifies file interval to be avoided, when un-allocated space will be + * chosen for a new chunk's location. + * + * @param chunk to move + * @param reservedAreaLow low boundary of reserved area, inclusive + * @param reservedAreaHigh high boundary of reserved area, exclusive + * @return true if block was moved, false otherwise + */ + private boolean moveChunk(Chunk chunk, long reservedAreaLow, long reservedAreaHigh) { + // ignore if already removed during the previous store operations + // those are possible either as explicit commit calls + // or from meta map updates at the end of this method + if (!chunks.containsKey(chunk.id)) { + return false; + } + WriteBuffer buff = getWriteBuffer(); + long start = chunk.block * BLOCK_SIZE; + int length = chunk.len * BLOCK_SIZE; + buff.limit(length); + ByteBuffer readBuff = fileStore.readFully(start, length); + Chunk chunkFromFile = Chunk.readChunkHeader(readBuff, start); + int chunkHeaderLen = readBuff.position(); + buff.position(chunkHeaderLen); + buff.put(readBuff); + long pos = fileStore.allocate(length, reservedAreaLow, reservedAreaHigh); + long block = pos / BLOCK_SIZE; + // in the absence of a reserved area, + // block should always move closer to the beginning of the file + assert reservedAreaHigh > 0 || block <= chunk.block : block + " " + chunk; + buff.position(0); + // can not set chunk's new block/len until it's fully written at new location, + // because concurrent reader can pick it up prematurely, + // also occupancy accounting fields should not leak into header + chunkFromFile.block = block; + chunkFromFile.next = 0; + chunkFromFile.writeChunkHeader(buff, chunkHeaderLen); + buff.position(length - Chunk.FOOTER_LENGTH); + buff.put(chunkFromFile.getFooterBytes()); + buff.position(0); + write(pos, buff.getBuffer()); + releaseWriteBuffer(buff); + fileStore.free(start, length); + chunk.block = block; + chunk.next = 0; + meta.put(Chunk.getMetaKey(chunk.id), chunk.asString()); + markMetaChanged(); + return true; + } + + /** + * Force all stored changes to be written to the storage. The default + * implementation calls FileChannel.force(true). + */ + public void sync() { + checkOpen(); + FileStore f = fileStore; + if (f != null) { + f.sync(); + } + } + + /** + * Compact store file, that is, compact blocks that have a low + * fill rate, and move chunks next to each other. This will typically + * shrink the file. Changes are flushed to the file, and old + * chunks are overwritten. + * + * @param maxCompactTime the maximum time in milliseconds to compact + */ + public void compactFile(long maxCompactTime) { + setRetentionTime(0); + long start = System.nanoTime(); + while (compact(95, 16 * 1024 * 1024)) { + sync(); + compactMoveChunks(95, 16 * 1024 * 1024); + long time = System.nanoTime() - start; + if (time > TimeUnit.MILLISECONDS.toNanos(maxCompactTime)) { + break; + } + } + } + + /** + * Try to increase the fill rate by re-writing partially full chunks. Chunks + * with a low number of live items are re-written. + *

+ * If the current fill rate is higher than the target fill rate, nothing is + * done. + *

+ * Please note this method will not necessarily reduce the file size, as + * empty chunks are not overwritten. + *

+ * Only data of open maps can be moved. For maps that are not open, the old + * chunk is still referenced. Therefore, it is recommended to open all maps + * before calling this method. + * + * @param targetFillRate the minimum percentage of live entries + * @param write the minimum number of bytes to write + * @return if a chunk was re-written + */ + public boolean compact(int targetFillRate, int write) { + if (reuseSpace && lastChunk != null) { + checkOpen(); + if (targetFillRate > 0 && getChunksFillRate() < targetFillRate) { + // We can't wait forever for the lock here, + // because if called from the background thread, + // it might go into deadlock with concurrent database closure + // and attempt to stop this thread. + try { + if (storeLock.tryLock(10, TimeUnit.MILLISECONDS)) { + try { + return rewriteChunks(write); + } finally { + storeLock.unlock(); + } + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + return false; + } + + private boolean rewriteChunks(int writeLimit) { + TxCounter txCounter = registerVersionUsage(); + try { + Iterable old = findOldChunks(writeLimit); + if (old != null) { + HashSet idSet = createIdSet(old); + return !idSet.isEmpty() && compactRewrite(idSet) > 0; + } + } finally { + deregisterVersionUsage(txCounter); + } + return false; + } + + /** + * Get the current fill rate (percentage of used space in the file). Unlike + * the fill rate of the store, here we only account for chunk data; the fill + * rate here is how much of the chunk data is live (still referenced). Young + * chunks are considered live. + * + * @return the fill rate, in percent (100 is completely full) + */ + public int getChunksFillRate() { + long maxLengthSum = 1; + long maxLengthLiveSum = 1; + for (Chunk c : chunks.values()) { + assert c.maxLen >= 0; + maxLengthSum += c.maxLen; + maxLengthLiveSum += c.maxLenLive; + } + // the fill rate of all chunks combined + int fillRate = (int) (100 * maxLengthLiveSum / maxLengthSum); + return fillRate; + } + + private int getProjectedFillRate() { + int vacatedBlocks = 0; + long maxLengthSum = 1; + long maxLengthLiveSum = 1; + long time = getTimeSinceCreation(); + for (Chunk c : chunks.values()) { + assert c.maxLen >= 0; + if (isRewritable(c, time)) { + assert c.maxLenLive >= c.maxLenLive; + vacatedBlocks += c.len; + maxLengthSum += c.maxLen; + maxLengthLiveSum += c.maxLenLive; + } + } + int additionalBlocks = (int) (vacatedBlocks * maxLengthLiveSum / maxLengthSum); + int fillRate = fileStore.getProjectedFillRate(vacatedBlocks - additionalBlocks); + return fillRate; + } + + public int getFillRate() { + return fileStore.getFillRate(); + } + + private Iterable findOldChunks(int writeLimit) { + assert lastChunk != null; + long time = getTimeSinceCreation(); + + // the queue will contain chunks we want to free up + PriorityQueue queue = new PriorityQueue<>(this.chunks.size() / 4 + 1, + new Comparator() { + @Override + public int compare(Chunk o1, Chunk o2) { + int comp = Integer.compare(o2.collectPriority, o1.collectPriority); + if (comp == 0) { + comp = Long.compare(o2.maxLenLive, o2.maxLenLive); + } + return comp; + } + }); + + long totalSize = 0; + long latestVersion = lastChunk.version + 1; + for (Chunk chunk : chunks.values()) { + // only look at chunk older than the retention time + // (it's possible to compact chunks earlier, but right + // now we don't do that) + if (isRewritable(chunk, time)) { + long age = latestVersion - chunk.version; + chunk.collectPriority = (int) (chunk.getFillRate() * 1000 / age); + totalSize += chunk.maxLenLive; + queue.offer(chunk); + while (totalSize > writeLimit) { + Chunk removed = queue.poll(); + if (removed == null) { + break; + } + totalSize -= removed.maxLenLive; + } + } + } + + return queue.isEmpty() ? null : queue; + } + + private boolean isRewritable(Chunk chunk, long time) { + return chunk.isRewritable() && isSeasonedChunk(chunk, time); + } + + private int compactRewrite(Set set) { + assert storeLock.isHeldByCurrentThread(); + // this will ensure better recognition of the last chunk + // in case of power failure, since we are going to move older chunks + // to the end of the file + writeStoreHeader(); + sync(); + + int rewrittenPageCount = 0; + storeLock.unlock(); + try { + for (MVMap map : maps.values()) { + if (!map.isClosed() && !map.isSingleWriter()) { + try { + rewrittenPageCount += map.rewrite(set); + } catch(IllegalStateException ex) { + if (!map.isClosed()) { + throw ex; + } + } + } + } + int rewriteMetaCount = meta.rewrite(set); + if (rewriteMetaCount > 0) { + markMetaChanged(); + rewrittenPageCount += rewriteMetaCount; + } + } finally { + storeLock.lock(); + } + commit(); + assert validateRewrite(set); + return rewrittenPageCount; + } + + private boolean validateRewrite(Set set) { + for (Integer chunkId : set) { + Chunk chunk = chunks.get(chunkId); + if (chunk != null && chunk.isLive()) { + int pageCountLive = chunk.pageCountLive; + RemovedPageInfo[] removedPageInfos = removedPages.toArray(new RemovedPageInfo[0]); + for (RemovedPageInfo rpi : removedPageInfos) { + if (rpi.getPageChunkId() == chunk.id) { + --pageCountLive; + } + } + if (pageCountLive != 0) { + for (String mapName : getMapNames()) { + if (!mapName.startsWith("undoLog") && hasData(mapName)) { // non-singleWriter map has data + int mapId = getMapId(mapName); + if (!maps.containsKey(mapId)) { // map is not open + // all bets are off + return true; + } + } + } + assert pageCountLive != 0 : chunk + " " + Arrays.toString(removedPageInfos); + } + } + } + return true; + } + + private static HashSet createIdSet(Iterable toCompact) { + HashSet set = new HashSet<>(); + for (Chunk c : toCompact) { + set.add(c.id); + } + return set; + } + + /** + * Read a page. + * + * @param map the map + * @param pos the page position + * @return the page + */ + Page readPage(MVMap map, long pos) { + try { + if (!DataUtils.isPageSaved(pos)) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_CORRUPT, "Position 0"); + } + Page p = cache == null ? null : cache.get(pos); + if (p == null) { + ByteBuffer buff = readBufferForPage(pos, map.getId()); + try { + p = Page.read(buff, pos, map); + } catch (Exception e) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_CORRUPT, + "Unable to read the page at position {0}", pos, e); + } + cachePage(p); + } + return p; + } catch (IllegalStateException e) { + if (recoveryMode) { + return map.createEmptyLeaf(); + } + throw e; + } + } + + /** + * Remove a page. + * + * @param pos the position of the page + * @param version at which page was removed + * @param pinned whether page is considered pinned + */ + void accountForRemovedPage(long pos, long version, boolean pinned) { + assert DataUtils.isPageSaved(pos); + RemovedPageInfo rpi = new RemovedPageInfo(pos, pinned, version); + removedPages.add(rpi); + } + + Compressor getCompressorFast() { + if (compressorFast == null) { + compressorFast = new CompressLZF(); + } + return compressorFast; + } + + Compressor getCompressorHigh() { + if (compressorHigh == null) { + compressorHigh = new CompressDeflate(); + } + return compressorHigh; + } + + int getCompressionLevel() { + return compressionLevel; + } + + public int getPageSplitSize() { + return pageSplitSize; + } + + public int getKeysPerPage() { + return keysPerPage; + } + + public long getMaxPageSize() { + return cache == null ? Long.MAX_VALUE : cache.getMaxItemSize() >> 4; + } + + public boolean getReuseSpace() { + return reuseSpace; + } + + /** + * Whether empty space in the file should be re-used. If enabled, old data + * is overwritten (default). If disabled, writes are appended at the end of + * the file. + *

+ * This setting is specially useful for online backup. To create an online + * backup, disable this setting, then copy the file (starting at the + * beginning of the file). In this case, concurrent backup and write + * operations are possible (obviously the backup process needs to be faster + * than the write operations). + * + * @param reuseSpace the new value + */ + public void setReuseSpace(boolean reuseSpace) { + this.reuseSpace = reuseSpace; + } + + public int getRetentionTime() { + return retentionTime; + } + + /** + * How long to retain old, persisted chunks, in milliseconds. Chunks that + * are older may be overwritten once they contain no live data. + *

+ * The default value is 45000 (45 seconds) when using the default file + * store. It is assumed that a file system and hard disk will flush all + * write buffers within this time. Using a lower value might be dangerous, + * unless the file system and hard disk flush the buffers earlier. To + * manually flush the buffers, use + * MVStore.getFile().force(true), however please note that + * according to various tests this does not always work as expected + * depending on the operating system and hardware. + *

+ * The retention time needs to be long enough to allow reading old chunks + * while traversing over the entries of a map. + *

+ * This setting is not persisted. + * + * @param ms how many milliseconds to retain old chunks (0 to overwrite them + * as early as possible) + */ + public void setRetentionTime(int ms) { + this.retentionTime = ms; + } + + /** + * How many versions to retain for in-memory stores. If not set, 5 old + * versions are retained. + * + * @param count the number of versions to keep + */ + public void setVersionsToKeep(int count) { + this.versionsToKeep = count; + } + + /** + * Get the oldest version to retain in memory (for in-memory stores). + * + * @return the version + */ + public long getVersionsToKeep() { + return versionsToKeep; + } + + /** + * Get the oldest version to retain. + * We keep at least number of previous versions specified by "versionsToKeep" + * configuration parameter (default 5). + * Previously it was used only in case of non-persistent MVStore. + * Now it's honored in all cases (although H2 always sets it to zero). + * Oldest version determination also takes into account calls (de)registerVersionUsage(), + * an will not release the version, while version is still in use. + * + * @return the version + */ + long getOldestVersionToKeep() { + long v = oldestVersionToKeep.get(); + v = Math.max(v - versionsToKeep, INITIAL_VERSION); + if (fileStore != null) { + long storeVersion = lastStoredVersion; + if (storeVersion != INITIAL_VERSION && storeVersion < v) { + v = storeVersion; + } + } + return v; + } + + private void setOldestVersionToKeep(long oldestVersionToKeep) { + boolean success; + do { + long current = this.oldestVersionToKeep.get(); + // Oldest version may only advance, never goes back + success = oldestVersionToKeep <= current || + this.oldestVersionToKeep.compareAndSet(current, oldestVersionToKeep); + } while (!success); + } + + /** + * Check whether all data can be read from this version. This requires that + * all chunks referenced by this version are still available (not + * overwritten). + * + * @param version the version + * @return true if all data can be read + */ + private boolean isKnownVersion(long version) { + if (version > currentVersion || version < 0) { + return false; + } + if (version == currentVersion || chunks.isEmpty()) { + // no stored data + return true; + } + // need to check if a chunk for this version exists + Chunk c = getChunkForVersion(version); + if (c == null) { + return false; + } + // also, all chunks referenced by this version + // need to be available in the file + MVMap oldMeta = getMetaMap(version); + try { + for (Iterator it = oldMeta.keyIterator(DataUtils.META_CHUNK); it.hasNext();) { + String chunkKey = it.next(); + if (!chunkKey.startsWith(DataUtils.META_CHUNK)) { + break; + } + if (!meta.containsKey(chunkKey)) { + String s = oldMeta.get(chunkKey); + Chunk c2 = Chunk.fromString(s); + Chunk test = readChunkHeaderAndFooter(c2.block, c2.id); + if (test == null) { + return false; + } + } + } + } catch (IllegalStateException e) { + // the chunk missing where the metadata is stored + return false; + } + return true; + } + + /** + * Adjust amount of "unsaved memory" meaning amount of RAM occupied by pages + * not saved yet to the file. This is the amount which triggers auto-commit. + * + * @param memory adjustment + */ + public void registerUnsavedMemory(int memory) { + // this counter was intentionally left unprotected against race + // condition for performance reasons + // TODO: evaluate performance impact of atomic implementation, + // since updates to unsavedMemory are largely aggregated now + unsavedMemory += memory; + int newValue = unsavedMemory; + if (newValue > autoCommitMemory && autoCommitMemory > 0) { + saveNeeded = true; + } + } + + boolean isSaveNeeded() { + return saveNeeded; + } + + /** + * This method is called before writing to a map. + * + * @param map the map + */ + void beforeWrite(MVMap map) { + if (saveNeeded && fileStore != null && isOpenOrStopping() && + // condition below is to prevent potential deadlock, + // because we should never seek storeLock while holding + // map root lock + (storeLock.isHeldByCurrentThread() || !map.getRoot().isLockedByCurrentThread()) && + // to avoid infinite recursion via store() -> dropUnusedChunks() -> meta.remove() + map != meta) { + + saveNeeded = false; + // check again, because it could have been written by now + if (unsavedMemory > autoCommitMemory && autoCommitMemory > 0) { + // if unsaved memory creation rate is to high, + // some back pressure need to be applied + // to slow things down and avoid OOME + if (3 * unsavedMemory > 4 * autoCommitMemory && !map.isSingleWriter()) { + commit(); + } else { + tryCommit(); + } + } + } + } + + /** + * Get the store version. The store version is usually used to upgrade the + * structure of the store after upgrading the application. Initially the + * store version is 0, until it is changed. + * + * @return the store version + */ + public int getStoreVersion() { + checkOpen(); + String x = meta.get("setting.storeVersion"); + return x == null ? 0 : DataUtils.parseHexInt(x); + } + + /** + * Update the store version. + * + * @param version the new store version + */ + public void setStoreVersion(int version) { + storeLock.lock(); + try { + checkOpen(); + markMetaChanged(); + meta.put("setting.storeVersion", Integer.toHexString(version)); + } finally { + storeLock.unlock(); + } + } + + /** + * Revert to the beginning of the current version, reverting all uncommitted + * changes. + */ + public void rollback() { + rollbackTo(currentVersion); + } + + /** + * Revert to the beginning of the given version. All later changes (stored + * or not) are forgotten. All maps that were created later are closed. A + * rollback to a version before the last stored version is immediately + * persisted. Rollback to version 0 means all data is removed. + * + * @param version the version to revert to + */ + public void rollbackTo(long version) { + storeLock.lock(); + try { + checkOpen(); + if (version == 0) { + // special case: remove all data + meta.setInitialRoot(meta.createEmptyLeaf(), INITIAL_VERSION); + deadChunks.clear(); + removedPages.clear(); + chunks.clear(); + clearCaches(); + if (fileStore != null) { + fileStore.clear(); + } + lastChunk = null; + versions.clear(); + currentVersion = version; + setWriteVersion(version); + metaChanged = false; + lastStoredVersion = INITIAL_VERSION; + for (MVMap m : maps.values()) { + m.close(); + } + return; + } + DataUtils.checkArgument( + isKnownVersion(version), + "Unknown version {0}", version); + + TxCounter txCounter; + while ((txCounter = versions.peekLast()) != null && txCounter.version >= version) { + versions.removeLast(); + } + currentTxCounter = new TxCounter(version); + + meta.rollbackTo(version); + metaChanged = false; + // find out which chunks to remove, + // and which is the newest chunk to keep + // (the chunk list can have gaps) + ArrayList remove = new ArrayList<>(); + Chunk keep = null; + for (Chunk c : chunks.values()) { + if (c.version > version) { + remove.add(c.id); + } else if (keep == null || keep.version < c.version) { + keep = c; + } + } + if (!remove.isEmpty()) { + // remove the youngest first, so we don't create gaps + // (in case we remove many chunks) + Collections.sort(remove, Collections.reverseOrder()); + for (int id : remove) { + Chunk c = chunks.remove(id); + if (c != null) { + long start = c.block * BLOCK_SIZE; + int length = c.len * BLOCK_SIZE; + freeFileSpace(start, length); + // overwrite the chunk, + // so it is not be used later on + WriteBuffer buff = getWriteBuffer(); + buff.limit(length); + // buff.clear() does not set the data + Arrays.fill(buff.getBuffer().array(), (byte) 0); + write(start, buff.getBuffer()); + releaseWriteBuffer(buff); + // only really needed if we remove many chunks, when writes are + // re-ordered - but we do it always, because rollback is not + // performance critical + sync(); + } + } + lastChunk = keep; + writeStoreHeader(); + readStoreHeader(); + } + deadChunks.clear(); + removedPages.clear(); + clearCaches(); + currentVersion = version; + if (lastStoredVersion == INITIAL_VERSION) { + lastStoredVersion = currentVersion - 1; + } + for (MVMap m : new ArrayList<>(maps.values())) { + int id = m.getId(); + if (m.getCreateVersion() >= version) { + m.close(); + maps.remove(id); + } else { + if (!m.rollbackRoot(version)) { + m.setRootPos(getRootPos(meta, id), version); + } + } + } + } finally { + storeLock.unlock(); + } + } + + private void clearCaches() { + if (cache != null) { + cache.clear(); + } + } + + private static long getRootPos(MVMap map, int mapId) { + String root = map.get(MVMap.getMapRootKey(mapId)); + return root == null ? 0 : DataUtils.parseHexLong(root); + } + + /** + * Get the current version of the data. When a new store is created, the + * version is 0. + * + * @return the version + */ + public long getCurrentVersion() { + return currentVersion; + } + + /** + * Get the file store. + * + * @return the file store + */ + public FileStore getFileStore() { + return fileStore; + } + + /** + * Get the store header. This data is for informational purposes only. The + * data is subject to change in future versions. The data should not be + * modified (doing so may corrupt the store). + * + * @return the store header + */ + public Map getStoreHeader() { + return storeHeader; + } + + private void checkOpen() { + if (!isOpenOrStopping()) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_CLOSED, + "This store is closed", panicException); + } + } + + /** + * Rename a map. + * + * @param map the map + * @param newName the new name + */ + public void renameMap(MVMap map, String newName) { + checkOpen(); + DataUtils.checkArgument(map != meta, + "Renaming the meta map is not allowed"); + int id = map.getId(); + String oldName = getMapName(id); + if (oldName != null && !oldName.equals(newName)) { + String idHexStr = Integer.toHexString(id); + // at first create a new name as an "alias" + String existingIdHexStr = meta.putIfAbsent(DataUtils.META_NAME + newName, idHexStr); + // we need to cope with the case of previously unfinished rename + DataUtils.checkArgument( + existingIdHexStr == null || existingIdHexStr.equals(idHexStr), + "A map named {0} already exists", newName); + // switch roles of a new and old names - old one is an alias now + meta.put(MVMap.getMapKey(id), map.asString(newName)); + // get rid of the old name completely + meta.remove(DataUtils.META_NAME + oldName); + markMetaChanged(); + } + } + + /** + * Remove a map from the current version of the store. + * + * @param map the map to remove + */ + public void removeMap(MVMap map) { + storeLock.lock(); + try { + checkOpen(); + DataUtils.checkArgument(map != meta, + "Removing the meta map is not allowed"); + RootReference rootReference = map.clearIt(); + map.close(); + + updateCounter += rootReference.updateCounter; + updateAttemptCounter += rootReference.updateAttemptCounter; + + int id = map.getId(); + String name = getMapName(id); + if (meta.remove(MVMap.getMapKey(id)) != null) { + markMetaChanged(); + } + if (meta.remove(DataUtils.META_NAME + name) != null) { + markMetaChanged(); + } + } finally { + storeLock.unlock(); + } + } + + /** + * Performs final stage of map removal - delete root location info from the meta table. + * Map is supposedly closed and anonymous and has no outstanding usage by now. + * + * @param mapId to deregister + */ + void deregisterMapRoot(int mapId) { + if (meta.remove(MVMap.getMapRootKey(mapId)) != null) { + markMetaChanged(); + } + } + + /** + * Remove map by name. + * + * @param name the map name + */ + public void removeMap(String name) { + int id = getMapId(name); + if(id > 0) { + MVMap map = getMap(id); + if (map == null) { + map = openMap(name); + } + removeMap(map); + } + } + + /** + * Get the name of the given map. + * + * @param id the map id + * @return the name, or null if not found + */ + public String getMapName(int id) { + checkOpen(); + String m = meta.get(MVMap.getMapKey(id)); + return m == null ? null : DataUtils.getMapName(m); + } + + private int getMapId(String name) { + String m = meta.get(DataUtils.META_NAME + name); + return m == null ? -1 : DataUtils.parseHexInt(m); + } + + /** + * Commit and save all changes, if there are any, and compact the store if + * needed. + */ + void writeInBackground() { + try { + if (!isOpenOrStopping() || isReadOnly()) { + return; + } + + // could also commit when there are many unsaved pages, + // but according to a test it doesn't really help + + long time = getTimeSinceCreation(); + if (time > lastCommitTime + autoCommitDelay) { + tryCommit(); + if (autoCompactFillRate < 0) { + compact(-getTargetFillRate(), autoCommitMemory); + } + } + int targetFillRate; + int projectedFillRate; + if (isIdle()) { + doMaintenance(autoCompactFillRate); + } else if (fileStore.isFragmented()) { + if (storeLock.tryLock(10, TimeUnit.MILLISECONDS)) { + try { + compactMoveChunks(autoCommitMemory * 4); + } finally { + storeLock.unlock(); + } + } + } else if (lastChunk != null && getFillRate() > (targetFillRate = getTargetFillRate()) + && (projectedFillRate = getProjectedFillRate()) < targetFillRate) { + if (storeLock.tryLock(10, TimeUnit.MILLISECONDS)) { + try { + int writeLimit = autoCommitMemory * targetFillRate / Math.max(projectedFillRate, 1); + if (rewriteChunks(writeLimit)) { + dropUnusedChunks(); + } + } finally { + storeLock.unlock(); + } + } + } + autoCompactLastFileOpCount = fileStore.getWriteCount() + fileStore.getReadCount(); + } catch (InterruptedException ignore) { + } catch (Throwable e) { + handleException(e); + if (backgroundExceptionHandler == null) { + throw e; + } + } + } + + private void doMaintenance(int targetFillRate) { + if (autoCompactFillRate > 0 && lastChunk != null && reuseSpace) { + try { + int lastProjectedFillRate = -1; + for (int cnt = 0; cnt < 5; cnt++) { + int fillRate = getFillRate(); + int projectedFillRate = fillRate; + if (fillRate > targetFillRate) { + projectedFillRate = getProjectedFillRate(); + if (projectedFillRate > targetFillRate || projectedFillRate <= lastProjectedFillRate) { + break; + } + } + lastProjectedFillRate = projectedFillRate; + // We can't wait forever for the lock here, + // because if called from the background thread, + // it might go into deadlock with concurrent database closure + // and attempt to stop this thread. + if (!storeLock.tryLock(10, TimeUnit.MILLISECONDS)) { + break; + } + try { + int writeLimit = autoCommitMemory * targetFillRate / Math.max(projectedFillRate, 1); + if (projectedFillRate < fillRate) { + if ((!rewriteChunks(writeLimit) || dropUnusedChunks() == 0) && cnt > 0) { + break; + } + } + if (!compactMoveChunks(writeLimit)) { + break; + } + } finally { + storeLock.unlock(); + } + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + private int getTargetFillRate() { + int targetRate = autoCompactFillRate; + // use a lower fill rate if there were any file operations since the last time + if (!isIdle()) { + targetRate /= 3; + } + return targetRate; + } + + private boolean isIdle() { + return autoCompactLastFileOpCount == fileStore.getWriteCount() + fileStore.getReadCount(); + } + + private void handleException(Throwable ex) { + if (backgroundExceptionHandler != null) { + try { + backgroundExceptionHandler.uncaughtException(Thread.currentThread(), ex); + } catch(Throwable ignore) { + if (ex != ignore) { // OOME may be the same + ex.addSuppressed(ignore); + } + } + } + } + + /** + * Set the read cache size in MB. + * + * @param mb the cache size in MB. + */ + public void setCacheSize(int mb) { + final long bytes = (long) mb * 1024 * 1024; + if (cache != null) { + cache.setMaxMemory(bytes); + cache.clear(); + } + } + + private boolean isOpen() { + return state == STATE_OPEN; + } + + /** + * Determine that store is open, or wait for it to be closed (by other thread) + * @return true if store is open, false otherwise + */ + public boolean isClosed() { + if (isOpen()) { + return false; + } + storeLock.lock(); + try { + assert state == STATE_CLOSED; + return true; + } finally { + storeLock.unlock(); + } + } + + private boolean isOpenOrStopping() { + return state <= STATE_STOPPING; + } + + private void stopBackgroundThread(boolean waitForIt) { + // Loop here is not strictly necessary, except for case of a spurious failure, + // which should not happen with non-weak flavour of CAS operation, + // but I've seen it, so just to be safe... + BackgroundWriterThread t; + while ((t = backgroundWriterThread.get()) != null) { + if (backgroundWriterThread.compareAndSet(t, null)) { + // if called from within the thread itself - can not join + if (t != Thread.currentThread()) { + synchronized (t.sync) { + t.sync.notifyAll(); + } + + if (waitForIt) { + try { + t.join(); + } catch (Exception e) { + // ignore + } + } + } + break; + } + } + } + + /** + * Set the maximum delay in milliseconds to auto-commit changes. + *

+ * To disable auto-commit, set the value to 0. In this case, changes are + * only committed when explicitly calling commit. + *

+ * The default is 1000, meaning all changes are committed after at most one + * second. + * + * @param millis the maximum delay + */ + public void setAutoCommitDelay(int millis) { + if (autoCommitDelay == millis) { + return; + } + autoCommitDelay = millis; + if (fileStore == null || fileStore.isReadOnly()) { + return; + } + stopBackgroundThread(true); + // start the background thread if needed + if (millis > 0 && isOpen()) { + int sleep = Math.max(1, millis / 10); + BackgroundWriterThread t = + new BackgroundWriterThread(this, sleep, + fileStore.toString()); + if (backgroundWriterThread.compareAndSet(null, t)) { + t.start(); + } + } + } + + public boolean isBackgroundThread() { + return Thread.currentThread() == backgroundWriterThread.get(); + } + + /** + * Get the auto-commit delay. + * + * @return the delay in milliseconds, or 0 if auto-commit is disabled. + */ + public int getAutoCommitDelay() { + return autoCommitDelay; + } + + /** + * Get the maximum memory (in bytes) used for unsaved pages. If this number + * is exceeded, unsaved changes are stored to disk. + * + * @return the memory in bytes + */ + public int getAutoCommitMemory() { + return autoCommitMemory; + } + + /** + * Get the estimated memory (in bytes) of unsaved data. If the value exceeds + * the auto-commit memory, the changes are committed. + *

+ * The returned value is an estimation only. + * + * @return the memory in bytes + */ + public int getUnsavedMemory() { + return unsavedMemory; + } + + /** + * Put the page in the cache. + * @param page the page + */ + void cachePage(Page page) { + if (cache != null) { + cache.put(page.getPos(), page, page.getMemory()); + } + } + + /** + * Get the amount of memory used for caching, in MB. + * Note that this does not include the page chunk references cache, which is + * 25% of the size of the page cache. + * + * @return the amount of memory used for caching + */ + public int getCacheSizeUsed() { + if (cache == null) { + return 0; + } + return (int) (cache.getUsedMemory() >> 20); + } + + /** + * Get the maximum cache size, in MB. + * Note that this does not include the page chunk references cache, which is + * 25% of the size of the page cache. + * + * @return the cache size + */ + public int getCacheSize() { + if (cache == null) { + return 0; + } + return (int) (cache.getMaxMemory() >> 20); + } + + /** + * Get the cache. + * + * @return the cache + */ + public CacheLongKeyLIRS getCache() { + return cache; + } + + /** + * Whether the store is read-only. + * + * @return true if it is + */ + public boolean isReadOnly() { + return fileStore != null && fileStore.isReadOnly(); + } + + public int getCacheHitRatio() { + if (cache == null) { + return 0; + } + long hits = cache.getHits(); + return (int) (100 * hits / (hits + cache.getMisses() + 1)); + } + + public double getUpdateFailureRatio() { + long updateCounter = this.updateCounter; + long updateAttemptCounter = this.updateAttemptCounter; + RootReference rootReference = meta.getRoot(); + updateCounter += rootReference.updateCounter; + updateAttemptCounter += rootReference.updateAttemptCounter; + for (MVMap map : maps.values()) { + RootReference root = map.getRoot(); + updateCounter += root.updateCounter; + updateAttemptCounter += root.updateAttemptCounter; + } + return updateAttemptCounter == 0 ? 0 : 1 - ((double)updateCounter / updateAttemptCounter); + } + + /** + * Register opened operation (transaction). + * This would increment usage counter for the current version. + * This version (and all after it) should not be dropped until all + * transactions involved are closed and usage counter goes to zero. + * @return TxCounter to be decremented when operation finishes (transaction closed). + */ + public TxCounter registerVersionUsage() { + TxCounter txCounter; + while(true) { + txCounter = currentTxCounter; + if(txCounter.incrementAndGet() > 0) { + return txCounter; + } + // The only way for counter to be negative + // if it was retrieved right before onVersionChange() + // and now onVersionChange() is done. + // This version is eligible for reclamation now + // and should not be used here, so restore count + // not to upset accounting and try again with a new + // version (currentTxCounter should have changed). + assert txCounter != currentTxCounter : txCounter; + txCounter.decrementAndGet(); + } + } + + /** + * De-register (close) completed operation (transaction). + * This will decrement usage counter for the corresponding version. + * If counter reaches zero, that version (and all unused after it) + * can be dropped immediately. + * + * @param txCounter to be decremented, obtained from registerVersionUsage() + */ + public void deregisterVersionUsage(TxCounter txCounter) { + if(txCounter != null) { + if(txCounter.decrementAndGet() <= 0) { + if (storeLock.isHeldByCurrentThread()) { + dropUnusedVersions(); + } else if (storeLock.tryLock()) { + try { + dropUnusedVersions(); + } finally { + storeLock.unlock(); + } + } + } + } + } + + private void onVersionChange(long version) { + TxCounter txCounter = currentTxCounter; + assert txCounter.get() >= 0; + versions.add(txCounter); + currentTxCounter = new TxCounter(version); + txCounter.decrementAndGet(); + dropUnusedVersions(); + } + + private void dropUnusedVersions() { + assert storeLock.isHeldByCurrentThread(); + TxCounter txCounter; + while ((txCounter = versions.peek()) != null + && txCounter.get() < 0) { + versions.poll(); + } + setOldestVersionToKeep((txCounter != null ? txCounter : currentTxCounter).version); + } + + private int dropUnusedChunks() { + assert storeLock.isHeldByCurrentThread(); + int count = 0; + if (!deadChunks.isEmpty()) { + long oldestVersionToKeep = getOldestVersionToKeep(); + long time = getTimeSinceCreation(); + Chunk chunk; + while ((chunk = deadChunks.poll()) != null && + (isSeasonedChunk(chunk, time) && canOverwriteChunk(chunk, oldestVersionToKeep) || + // if chunk is not ready yet, put it back and exit + // since this deque is inbounded, offerFirst() always return true + !deadChunks.offerFirst(chunk))) { + + if (chunks.remove(chunk.id) != null) { + if (meta.remove(Chunk.getMetaKey(chunk.id)) != null) { + markMetaChanged(); + } + if (chunk.isSaved()) { + freeChunkSpace(chunk); + } + ++count; + } + } + } + return count; + } + + private void freeChunkSpace(Chunk chunk) { + long start = chunk.block * BLOCK_SIZE; + int length = chunk.len * BLOCK_SIZE; + freeFileSpace(start, length); + } + + private void freeFileSpace(long start, int length) { + fileStore.free(start, length); + assert validateFileLength(start + ":" + length); + } + + private boolean validateFileLength(String msg) { + assert fileStore.getFileLengthInUse() == measureFileLengthInUse() : + fileStore.getFileLengthInUse() + " != " + measureFileLengthInUse() + " " + msg; + return true; + } + + /** + * Class TxCounter is a simple data structure to hold version of the store + * along with the counter of open transactions, + * which are still operating on this version. + */ + public static final class TxCounter { + + /** + * Version of a store, this TxCounter is related to + */ + public final long version; + + /** + * Counter of outstanding operation on this version of a store + */ + private volatile int counter; + + private static final AtomicIntegerFieldUpdater counterUpdater = + AtomicIntegerFieldUpdater.newUpdater(TxCounter.class, "counter"); + + + TxCounter(long version) { + this.version = version; + } + + int get() { + return counter; + } + + /** + * Increment and get the counter value. + * + * @return the new value + */ + int incrementAndGet() { + return counterUpdater.incrementAndGet(this); + } + + /** + * Decrement and get the counter values. + * + * @return the new value + */ + int decrementAndGet() { + return counterUpdater.decrementAndGet(this); + } + + @Override + public String toString() { + return "v=" + version + " / cnt=" + counter; + } + } + + /** + * A background writer thread to automatically store changes from time to + * time. + */ + private static class BackgroundWriterThread extends Thread { + + public final Object sync = new Object(); + private final MVStore store; + private final int sleep; + + BackgroundWriterThread(MVStore store, int sleep, String fileStoreName) { + super("MVStore background writer " + fileStoreName); + this.store = store; + this.sleep = sleep; + setDaemon(true); + } + + @Override + public void run() { + while (store.isBackgroundThread()) { + synchronized (sync) { + try { + sync.wait(sleep); + } catch (InterruptedException ignore) { + } + } + if (!store.isBackgroundThread()) { + break; + } + store.writeInBackground(); + } + } + } + + private static class RemovedPageInfo implements Comparable + { + final long version; + final int removedPageInfo; + + RemovedPageInfo(long pagePos, boolean pinned, long version) { + this.removedPageInfo = createRemovedPageInfo(pagePos, pinned); + this.version = version; + } + + @Override + public int compareTo(RemovedPageInfo other) { + return Long.compare(version, other.version); + } + + int getPageChunkId() { + return removedPageInfo >>> 6; + } + + int getPageLength() { + return DataUtils.decodePageLength((removedPageInfo >> 1) & 0x1F); + } + + /** + * Find out if removed page was pinned (can not be evacuated to a new chunk). + * @return true if page has been pinned + */ + boolean isPinned() { + return (removedPageInfo & 1) == 1; + } + + /** + * Transforms saved page position into removed page info, by eliminating page offset + * and replacing "page type" bit with "pinned page" flag. + * 0 "pinned" flag + * 1-5 encoded page length + * 6-31 chunk id + * @param pagePos of the saved page + * @param isPinned whether page belong to a "single writer" map + * @return removed page info that contains chunk id, page length and pinned flag + */ + private static int createRemovedPageInfo(long pagePos, boolean isPinned) { + int result = ((int) (pagePos >>> 32)) & ~0x3F | ((int) pagePos) & 0x3E; + if (isPinned) { + result |= 1; + } + return result; + } + + @Override + public String toString() { + return "RemovedPageInfo{" + + "version=" + version + + ", chunk=" + getPageChunkId() + + ", len=" + getPageLength() + + (isPinned() ? ", pinned" : "") + + '}'; + } + } + + /** + * A builder for an MVStore. + */ + public static final class Builder { + + private final HashMap config; + + private Builder(HashMap config) { + this.config = config; + } + + /** + * Creates new instance of MVStore.Builder. + */ + public Builder() { + config = new HashMap<>(); + } + + private Builder set(String key, Object value) { + config.put(key, value); + return this; + } + + /** + * Disable auto-commit, by setting the auto-commit delay and auto-commit + * buffer size to 0. + * + * @return this + */ + public Builder autoCommitDisabled() { + // we have a separate config option so that + // no thread is started if the write delay is 0 + // (if we only had a setter in the MVStore, + // the thread would need to be started in any case) + //set("autoCommitBufferSize", 0); + return set("autoCommitDelay", 0); + } + + /** + * Set the size of the write buffer, in KB disk space (for file-based + * stores). Unless auto-commit is disabled, changes are automatically + * saved if there are more than this amount of changes. + *

+ * The default is 1024 KB. + *

+ * When the value is set to 0 or lower, data is not automatically + * stored. + * + * @param kb the write buffer size, in kilobytes + * @return this + */ + public Builder autoCommitBufferSize(int kb) { + return set("autoCommitBufferSize", kb); + } + + /** + * Set the auto-compact target fill rate. If the average fill rate (the + * percentage of the storage space that contains active data) of the + * chunks is lower, then the chunks with a low fill rate are re-written. + * Also, if the percentage of empty space between chunks is higher than + * this value, then chunks at the end of the file are moved. Compaction + * stops if the target fill rate is reached. + *

+ * The default value is 40 (40%). The value 0 disables auto-compacting. + *

+ * + * @param percent the target fill rate + * @return this + */ + public Builder autoCompactFillRate(int percent) { + return set("autoCompactFillRate", percent); + } + + /** + * Use the following file name. If the file does not exist, it is + * automatically created. The parent directory already must exist. + * + * @param fileName the file name + * @return this + */ + public Builder fileName(String fileName) { + return set("fileName", fileName); + } + + /** + * Encrypt / decrypt the file using the given password. This method has + * no effect for in-memory stores. The password is passed as a + * char array so that it can be cleared as soon as possible. Please note + * there is still a small risk that password stays in memory (due to + * Java garbage collection). Also, the hashed encryption key is kept in + * memory as long as the file is open. + * + * @param password the password + * @return this + */ + public Builder encryptionKey(char[] password) { + return set("encryptionKey", password); + } + + /** + * Open the file in read-only mode. In this case, a shared lock will be + * acquired to ensure the file is not concurrently opened in write mode. + *

+ * If this option is not used, the file is locked exclusively. + *

+ * Please note a store may only be opened once in every JVM (no matter + * whether it is opened in read-only or read-write mode), because each + * file may be locked only once in a process. + * + * @return this + */ + public Builder readOnly() { + return set("readOnly", 1); + } + + /** + * Open the file in recovery mode, where some errors may be ignored. + * + * @return this + */ + public Builder recoveryMode() { + return set("recoveryMode", 1); + } + + /** + * Set the read cache size in MB. The default is 16 MB. + * + * @param mb the cache size in megabytes + * @return this + */ + public Builder cacheSize(int mb) { + return set("cacheSize", mb); + } + + /** + * Set the read cache concurrency. The default is 16, meaning 16 + * segments are used. + * + * @param concurrency the cache concurrency + * @return this + */ + public Builder cacheConcurrency(int concurrency) { + return set("cacheConcurrency", concurrency); + } + + /** + * Compress data before writing using the LZF algorithm. This will save + * about 50% of the disk space, but will slow down read and write + * operations slightly. + *

+ * This setting only affects writes; it is not necessary to enable + * compression when reading, even if compression was enabled when + * writing. + * + * @return this + */ + public Builder compress() { + return set("compress", 1); + } + + /** + * Compress data before writing using the Deflate algorithm. This will + * save more disk space, but will slow down read and write operations + * quite a bit. + *

+ * This setting only affects writes; it is not necessary to enable + * compression when reading, even if compression was enabled when + * writing. + * + * @return this + */ + public Builder compressHigh() { + return set("compress", 2); + } + + /** + * Set the amount of memory a page should contain at most, in bytes, + * before it is split. The default is 16 KB for persistent stores and 4 + * KB for in-memory stores. This is not a limit in the page size, as + * pages with one entry can get larger. It is just the point where pages + * that contain more than one entry are split. + * + * @param pageSplitSize the page size + * @return this + */ + public Builder pageSplitSize(int pageSplitSize) { + return set("pageSplitSize", pageSplitSize); + } + + /** + * Set the listener to be used for exceptions that occur when writing in + * the background thread. + * + * @param exceptionHandler the handler + * @return this + */ + public Builder backgroundExceptionHandler( + Thread.UncaughtExceptionHandler exceptionHandler) { + return set("backgroundExceptionHandler", exceptionHandler); + } + + /** + * Use the provided file store instead of the default one. + *

+ * File stores passed in this way need to be open. They are not closed + * when closing the store. + *

+ * Please note that any kind of store (including an off-heap store) is + * considered a "persistence", while an "in-memory store" means objects + * are not persisted and fully kept in the JVM heap. + * + * @param store the file store + * @return this + */ + public Builder fileStore(FileStore store) { + return set("fileStore", store); + } + + /** + * Open the store. + * + * @return the opened store + */ + public MVStore open() { + return new MVStore(config); + } + + @Override + public String toString() { + return DataUtils.appendMap(new StringBuilder(), config).toString(); + } + + /** + * Read the configuration from a string. + * + * @param s the string representation + * @return the builder + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static Builder fromString(String s) { + // Cast from HashMap to HashMap is safe + return new Builder((HashMap) DataUtils.parseMap(s)); + } + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Page.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Page.java new file mode 100644 index 000000000..ceb9d57dc --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/Page.java @@ -0,0 +1,1538 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.compress.Compressor; +import org.dizitart.no2.mvstore.compat.v1.mvstore.type.DataType; +import org.h2.util.Utils; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; + +import static org.dizitart.no2.mvstore.compat.v1.mvstore.DataUtils.PAGE_TYPE_LEAF; +import static org.h2.engine.Constants.*; + +/** + * A page (a node or a leaf). + *

+ * For b-tree nodes, the key at a given index is larger than the largest key of + * the child at the same index. + *

+ * File format: + * page length (including length): int + * check value: short + * map id: varInt + * number of keys: varInt + * type: byte (0: leaf, 1: node; +2: compressed) + * compressed: bytes saved (varInt) + * keys + * leaf: values (one for each key) + * node: children (1 more than keys) + */ +public abstract class Page implements Cloneable { + /** + * Map this page belongs to + */ + public final MVMap map; + + /** + * Position of this page's saved image within a Chunk + * or 0 if this page has not been saved yet + * or 1 if this page has not been saved yet, but already removed + * This "removed" flag is to keep track of pages that concurrently + * changed while they are being stored, in which case the live bookkeeping + * needs to be aware of such cases. + * Field need to be volatile to avoid races between saving thread setting it + * and other thread reading it to access the page. + * On top of this update atomicity is required so removal mark and saved position + * can be set concurrently + */ + private volatile long pos; + + /** + * The last result of a find operation is cached. + */ + private int cachedCompare; + + /** + * The estimated memory used in persistent case, IN_MEMORY marker value otherwise. + */ + private int memory; + + /** + * Amount of used disk space by this page only in persistent case. + */ + private int diskSpaceUsed; + + /** + * The keys. + */ + private Object[] keys; + + /** + * Updater for pos field, which can be updated when page is saved, + * but can be concurrently marked as removed + */ + private static final AtomicLongFieldUpdater posUpdater = AtomicLongFieldUpdater.newUpdater(Page.class, "pos"); + /** + * The estimated number of bytes used per child entry. + */ + static final int PAGE_MEMORY_CHILD = MEMORY_POINTER + 16; // 16 = two longs + + /** + * The estimated number of bytes used per base page. + */ + private static final int PAGE_MEMORY = MEMORY_OBJECT + // this + 2 * MEMORY_POINTER + // map, keys + MEMORY_ARRAY + // Object[] keys + 17; // pos, cachedCompare, memory, removedInMemory + /** + * The estimated number of bytes used per empty internal page object. + */ + static final int PAGE_NODE_MEMORY = PAGE_MEMORY + // super + MEMORY_POINTER + // children + MEMORY_ARRAY + // Object[] children + 8; // totalCount + + /** + * The estimated number of bytes used per empty leaf page. + */ + static final int PAGE_LEAF_MEMORY = PAGE_MEMORY + // super + MEMORY_POINTER + // values + MEMORY_ARRAY; // Object[] values + + /** + * An empty object array. + */ + private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + + /** + * Marker value for memory field, meaning that memory accounting is replaced by key count. + */ + private static final int IN_MEMORY = Integer.MIN_VALUE; + + private static final PageReference[] SINGLE_EMPTY = {PageReference.EMPTY}; + + + Page(MVMap map) { + this.map = map; + } + + Page(MVMap map, Page source) { + this(map, source.keys); + memory = source.memory; + } + + Page(MVMap map, Object[] keys) { + this.map = map; + this.keys = keys; + } + + /** + * Create a new, empty leaf page. + * + * @param map the map + * @return the new page + */ + static Page createEmptyLeaf(MVMap map) { + return createLeaf(map, EMPTY_OBJECT_ARRAY, EMPTY_OBJECT_ARRAY, PAGE_LEAF_MEMORY); + } + + /** + * Create a new, empty internal node page. + * + * @param map the map + * @return the new page + */ + static Page createEmptyNode(MVMap map) { + return createNode(map, EMPTY_OBJECT_ARRAY, SINGLE_EMPTY, 0, PAGE_NODE_MEMORY + MEMORY_POINTER + PAGE_MEMORY_CHILD); // there is always one child + } + + /** + * Create a new non-leaf page. The arrays are not cloned. + * + * @param map the map + * @param keys the keys + * @param children the child page positions + * @param totalCount the total number of keys + * @param memory the memory used in bytes + * @return the page + */ + public static Page createNode(MVMap map, Object[] keys, PageReference[] children, long totalCount, int memory) { + assert keys != null; + Page page = new NonLeaf(map, keys, children, totalCount); + page.initMemoryAccount(memory); + return page; + } + + /** + * Create a new leaf page. The arrays are not cloned. + * + * @param map the map + * @param keys the keys + * @param values the values + * @param memory the memory used in bytes + * @return the page + */ + static Page createLeaf(MVMap map, Object[] keys, Object[] values, int memory) { + assert keys != null; + Page page = new Leaf(map, keys, values); + page.initMemoryAccount(memory); + return page; + } + + private void initMemoryAccount(int memoryCount) { + if (!map.isPersistent()) { + memory = IN_MEMORY; + } else if (memoryCount == 0) { + recalculateMemory(); + } else { + addMemory(memoryCount); + assert memoryCount == getMemory(); + } + } + + /** + * Get the value for the given key, or null if not found. + * Search is done in the tree rooted at given page. + * + * @param key the key + * @param p the root page + * @return the value, or null if not found + */ + static Object get(Page p, Object key) { + while (true) { + int index = p.binarySearch(key); + if (p.isLeaf()) { + return index >= 0 ? p.getValue(index) : null; + } else if (index++ < 0) { + index = -index; + } + p = p.getChildPage(index); + } + } + + /** + * Read a page. + * + * @param buff ByteBuffer containing serialized page info + * @param pos the position + * @param map the map + * @return the page + */ + static Page read(ByteBuffer buff, long pos, MVMap map) { + boolean leaf = (DataUtils.getPageType(pos) & 1) == PAGE_TYPE_LEAF; + Page p = leaf ? new Leaf(map) : new NonLeaf(map); + p.pos = pos; + int chunkId = DataUtils.getPageChunkId(pos); + p.read(buff, chunkId); + return p; + } + + /** + * Get the id of the page's owner map + * + * @return id + */ + public final int getMapId() { + return map.getId(); + } + + /** + * Create a copy of this page with potentially different owning map. + * This is used exclusively during bulk map copying. + * Child page references for nodes are cleared (re-pointed to an empty page) + * to be filled-in later to copying procedure. This way it can be saved + * mid-process without tree integrity violation + * + * @param map new map to own resulting page + * @return the page + */ + abstract Page copy(MVMap map); + + /** + * Get the key at the given index. + * + * @param index the index + * @return the key + */ + public Object getKey(int index) { + return keys[index]; + } + + /** + * Get the child page at the given index. + * + * @param index the index + * @return the child page + */ + public abstract Page getChildPage(int index); + + /** + * Get the position of the child. + * + * @param index the index + * @return the position + */ + public abstract long getChildPagePos(int index); + + /** + * Get the value at the given index. + * + * @param index the index + * @return the value + */ + public abstract Object getValue(int index); + + /** + * Get the number of keys in this page. + * + * @return the number of keys + */ + public final int getKeyCount() { + return keys.length; + } + + /** + * Check whether this is a leaf page. + * + * @return true if it is a leaf + */ + public final boolean isLeaf() { + return getNodeType() == PAGE_TYPE_LEAF; + } + + public abstract int getNodeType(); + + /** + * Get the position of the page + * + * @return the position + */ + public final long getPos() { + return pos; + } + + @Override + public String toString() { + StringBuilder buff = new StringBuilder(); + dump(buff); + return buff.toString(); + } + + /** + * Dump debug data for this page. + * + * @param buff append buffer + */ + protected void dump(StringBuilder buff) { + buff.append("id: ").append(System.identityHashCode(this)).append('\n'); + buff.append("pos: ").append(Long.toHexString(pos)).append('\n'); + if (isSaved()) { + int chunkId = DataUtils.getPageChunkId(pos); + buff.append("chunk: ").append(Long.toHexString(chunkId)).append('\n'); + } + } + + /** + * Create a copy of this page. + * + * @return a mutable copy of this page + */ + public final Page copy() { + Page newPage = clone(); + newPage.pos = 0; + return newPage; + } + + @Override + protected final Page clone() { + Page clone; + try { + clone = (Page) super.clone(); + } catch (CloneNotSupportedException impossible) { + throw new RuntimeException(impossible); + } + return clone; + } + + /** + * Search the key in this page using a binary search. Instead of always + * starting the search in the middle, the last found index is cached. + *

+ * If the key was found, the returned value is the index in the key array. + * If not found, the returned value is negative, where -1 means the provided + * key is smaller than any keys in this page. See also Arrays.binarySearch. + * + * @param key the key + * @return the value or null + */ + int binarySearch(Object key) { + int low = 0, high = getKeyCount() - 1; + // the cached index minus one, so that + // for the first time (when cachedCompare is 0), + // the default value is used + int x = cachedCompare - 1; + if (x < 0 || x > high) { + x = high >>> 1; + } + Object[] k = keys; + while (low <= high) { + int compare = map.compare(key, k[x]); + if (compare > 0) { + low = x + 1; + } else if (compare < 0) { + high = x - 1; + } else { + cachedCompare = x + 1; + return x; + } + x = (low + high) >>> 1; + } + cachedCompare = low; + return -(low + 1); + } + + /** + * Split the page. This modifies the current page. + * + * @param at the split index + * @return the page with the entries after the split index + */ + abstract Page split(int at); + + /** + * Split the current keys array into two arrays. + * + * @param aCount size of the first array. + * @param bCount size of the second array/ + * @return the second array. + */ + final Object[] splitKeys(int aCount, int bCount) { + assert aCount + bCount <= getKeyCount(); + Object[] aKeys = createKeyStorage(aCount); + Object[] bKeys = createKeyStorage(bCount); + System.arraycopy(keys, 0, aKeys, 0, aCount); + System.arraycopy(keys, getKeyCount() - bCount, bKeys, 0, bCount); + keys = aKeys; + return bKeys; + } + + /** + * Append additional key/value mappings to this Page. + * New mappings suppose to be in correct key order. + * + * @param extraKeyCount number of mappings to be added + * @param extraKeys to be added + * @param extraValues to be added + */ + abstract void expand(int extraKeyCount, Object[] extraKeys, Object[] extraValues); + + /** + * Expand the keys array. + * + * @param extraKeyCount number of extra key entries to create + * @param extraKeys extra key values + */ + final void expandKeys(int extraKeyCount, Object[] extraKeys) { + int keyCount = getKeyCount(); + Object[] newKeys = createKeyStorage(keyCount + extraKeyCount); + System.arraycopy(keys, 0, newKeys, 0, keyCount); + System.arraycopy(extraKeys, 0, newKeys, keyCount, extraKeyCount); + keys = newKeys; + } + + /** + * Get the total number of key-value pairs, including child pages. + * + * @return the number of key-value pairs + */ + public abstract long getTotalCount(); + + /** + * Get the number of key-value pairs for a given child. + * + * @param index the child index + * @return the descendant count + */ + abstract long getCounts(int index); + + /** + * Replace the child page. + * + * @param index the index + * @param c the new child page + */ + public abstract void setChild(int index, Page c); + + /** + * Replace the key at an index in this page. + * + * @param index the index + * @param key the new key + */ + public final void setKey(int index, Object key) { + keys = keys.clone(); + if (isPersistent()) { + Object old = keys[index]; + DataType keyType = map.getKeyType(); + int mem = keyType.getMemory(key); + if (old != null) { + mem -= keyType.getMemory(old); + } + addMemory(mem); + } + keys[index] = key; + } + + /** + * Replace the value at an index in this page. + * + * @param index the index + * @param value the new value + * @return the old value + */ + public abstract Object setValue(int index, Object value); + + /** + * Insert a key-value pair into this leaf. + * + * @param index the index + * @param key the key + * @param value the value + */ + public abstract void insertLeaf(int index, Object key, Object value); + + /** + * Insert a child page into this node. + * + * @param index the index + * @param key the key + * @param childPage the child page + */ + public abstract void insertNode(int index, Object key, Page childPage); + + /** + * Insert a key into the key array + * + * @param index index to insert at + * @param key the key value + */ + final void insertKey(int index, Object key) { + int keyCount = getKeyCount(); + assert index <= keyCount : index + " > " + keyCount; + Object[] newKeys = createKeyStorage(keyCount + 1); + DataUtils.copyWithGap(keys, newKeys, keyCount, index); + keys = newKeys; + + keys[index] = key; + + if (isPersistent()) { + addMemory(MEMORY_POINTER + map.getKeyType().getMemory(key)); + } + } + + /** + * Remove the key and value (or child) at the given index. + * + * @param index the index + */ + public void remove(int index) { + int keyCount = getKeyCount(); + org.dizitart.no2.mvstore.compat.v1.mvstore.type.DataType keyType = map.getKeyType(); + if (index == keyCount) { + --index; + } + if (isPersistent()) { + Object old = getKey(index); + addMemory(-MEMORY_POINTER - keyType.getMemory(old)); + } + Object[] newKeys = createKeyStorage(keyCount - 1); + DataUtils.copyExcept(keys, newKeys, keyCount, index); + keys = newKeys; + } + + /** + * Read the page from the buffer. + * + * @param buff the buffer + * @param chunkId the chunk id + */ + private void read(ByteBuffer buff, int chunkId) { + // size of int + short + varint, since we've read page length, check and + // mapId already + int pageLength = buff.remaining() + 10; + int len = DataUtils.readVarInt(buff); + keys = createKeyStorage(len); + int type = buff.get(); + if (isLeaf() != ((type & 1) == PAGE_TYPE_LEAF)) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_FILE_CORRUPT, "File corrupted in chunk {0}, expected node type {1}, got {2}", chunkId, isLeaf() ? "0" : "1", type); + } + if (!isLeaf()) { + readPayLoad(buff); + } + boolean compressed = (type & DataUtils.PAGE_COMPRESSED) != 0; + if (compressed) { + Compressor compressor; + if ((type & DataUtils.PAGE_COMPRESSED_HIGH) == DataUtils.PAGE_COMPRESSED_HIGH) { + compressor = map.getStore().getCompressorHigh(); + } else { + compressor = map.getStore().getCompressorFast(); + } + int lenAdd = DataUtils.readVarInt(buff); + int compLen = buff.remaining(); + byte[] comp = Utils.newBytes(compLen); + buff.get(comp); + int l = compLen + lenAdd; + buff = ByteBuffer.allocate(l); + compressor.expand(comp, 0, compLen, buff.array(), buff.arrayOffset(), l); + } + map.getKeyType().read(buff, keys, len, true); + if (isLeaf()) { + readPayLoad(buff); + } + diskSpaceUsed = pageLength; + recalculateMemory(); + } + + /** + * Read the page payload from the buffer. + * + * @param buff the buffer + */ + protected abstract void readPayLoad(ByteBuffer buff); + + public final boolean isSaved() { + return DataUtils.isPageSaved(pos); + } + + public final boolean isRemoved() { + return DataUtils.isPageRemoved(pos); + } + + /** + * Mark this page as removed "in memory". That means that only adjustment of + * "unsaved memory" amount is required. On the other hand, if page was + * persisted, it's removal should be reflected in occupancy of the + * containing chunk. + * + * @return true if it was marked by this call or has been marked already, + * false if page has been saved already. + */ + private boolean markAsRemoved() { + assert getTotalCount() > 0 : this; + long pagePos; + do { + pagePos = pos; + if (DataUtils.isPageSaved(pagePos)) { + return false; + } + assert !DataUtils.isPageRemoved(pagePos); + } while (!posUpdater.compareAndSet(this, 0L, 1L)); + return true; + } + + /** + * Store the page and update the position. + * + * @param chunk the chunk + * @param buff the target buffer + * @return the position of the buffer just after the type + */ + protected final int write(Chunk chunk, WriteBuffer buff) { + int start = buff.position(); + int len = getKeyCount(); + int type = isLeaf() ? PAGE_TYPE_LEAF : DataUtils.PAGE_TYPE_NODE; + buff.putInt(0).putShort((byte) 0).putVarInt(map.getId()).putVarInt(len); + int typePos = buff.position(); + buff.put((byte) type); + writeChildren(buff, true); + int compressStart = buff.position(); + map.getKeyType().write(buff, keys, len, true); + writeValues(buff); + MVStore store = map.getStore(); + int expLen = buff.position() - compressStart; + if (expLen > 16) { + int compressionLevel = store.getCompressionLevel(); + if (compressionLevel > 0) { + Compressor compressor; + int compressType; + if (compressionLevel == 1) { + compressor = map.getStore().getCompressorFast(); + compressType = DataUtils.PAGE_COMPRESSED; + } else { + compressor = map.getStore().getCompressorHigh(); + compressType = DataUtils.PAGE_COMPRESSED_HIGH; + } + byte[] exp = new byte[expLen]; + buff.position(compressStart).get(exp); + byte[] comp = new byte[expLen * 2]; + int compLen = compressor.compress(exp, expLen, comp, 0); + int plus = DataUtils.getVarIntLen(compLen - expLen); + if (compLen + plus < expLen) { + buff.position(typePos).put((byte) (type + compressType)); + buff.position(compressStart).putVarInt(expLen - compLen).put(comp, 0, compLen); + } + } + } + int pageLength = buff.position() - start; + int chunkId = chunk.id; + int check = DataUtils.getCheckValue(chunkId) ^ DataUtils.getCheckValue(start) ^ DataUtils.getCheckValue(pageLength); + buff.putInt(start, pageLength).putShort(start + 4, (short) check); + if (isSaved()) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_INTERNAL, "Page already stored"); + } + long pagePos = DataUtils.getPagePos(chunkId, start, pageLength, type); + boolean isDeleted = isRemoved(); + while (!posUpdater.compareAndSet(this, isDeleted ? 1L : 0L, pagePos)) { + isDeleted = isRemoved(); + } + store.cachePage(this); + if (type == DataUtils.PAGE_TYPE_NODE) { + // cache again - this will make sure nodes stays in the cache + // for a longer time + store.cachePage(this); + } + int pageLengthEncoded = DataUtils.getPageMaxLength(pos); + boolean singleWriter = map.isSingleWriter(); + chunk.accountForWrittenPage(pageLengthEncoded, singleWriter); + if (isDeleted) { + store.accountForRemovedPage(pagePos, chunk.version + 1, singleWriter); + } + diskSpaceUsed = pageLengthEncoded != DataUtils.PAGE_LARGE ? pageLengthEncoded : pageLength; + return typePos + 1; + } + + /** + * Write values that the buffer contains to the buff. + * + * @param buff the target buffer + */ + protected abstract void writeValues(WriteBuffer buff); + + /** + * Write page children to the buff. + * + * @param buff the target buffer + * @param withCounts true if the descendant counts should be written + */ + protected abstract void writeChildren(WriteBuffer buff, boolean withCounts); + + /** + * Store this page and all children that are changed, in reverse order, and + * update the position and the children. + * + * @param chunk the chunk + * @param buff the target buffer + */ + abstract void writeUnsavedRecursive(Chunk chunk, WriteBuffer buff); + + /** + * Unlink the children recursively after all data is written. + */ + abstract void writeEnd(); + + public abstract int getRawChildPageCount(); + + @Override + public final boolean equals(Object other) { + return other == this || other instanceof Page && isSaved() && ((Page) other).pos == pos; + } + + @Override + public final int hashCode() { + return isSaved() ? (int) (pos | (pos >>> 32)) : super.hashCode(); + } + + protected final boolean isPersistent() { + return memory != IN_MEMORY; + } + + public final int getMemory() { + if (isPersistent()) { +// assert memory == calculateMemory() : +// "Memory calculation error " + memory + " != " + calculateMemory(); + return memory; + } + return 0; + } + + /** + * Amount of used disk space in persistent case including child pages. + * + * @return amount of used disk space in persistent case + */ + public long getDiskSpaceUsed() { + long r = 0; + if (isPersistent()) { + r += diskSpaceUsed; + if (!isLeaf()) { + for (int i = 0; i < getRawChildPageCount(); i++) { + long pos = getChildPagePos(i); + if (pos != 0) { + r += getChildPage(i).getDiskSpaceUsed(); + } + } + } + } + return r; + } + + /** + * Increase estimated memory used in persistent case. + * + * @param mem additional memory size. + */ + final void addMemory(int mem) { + memory += mem; + } + + /** + * Recalculate estimated memory used in persistent case. + */ + final void recalculateMemory() { + assert isPersistent(); + memory = calculateMemory(); + } + + /** + * Calculate estimated memory used in persistent case. + * + * @return memory in bytes + */ + protected int calculateMemory() { + int keyCount = getKeyCount(); + int mem = keyCount * MEMORY_POINTER; + DataType keyType = map.getKeyType(); + for (int i = 0; i < keyCount; i++) { + mem += keyType.getMemory(keys[i]); + } + return mem; + } + + public boolean isComplete() { + return true; + } + + /** + * Called when done with copying page. + */ + public void setComplete() { + } + + /** + * Make accounting changes (chunk occupancy or "unsaved" RAM), related to + * this page removal. + * + * @param version at which page was removed + * @return amount (negative), by which "unsaved memory" should be adjusted, + * if page is unsaved one, and 0 for page that was already saved, or + * in case of non-persistent map + */ + public final int removePage(long version) { + if (isPersistent() && getTotalCount() > 0) { + MVStore store = map.store; + if (!markAsRemoved()) { // only if it has been saved already + long pagePos = pos; + store.accountForRemovedPage(pagePos, version, map.isSingleWriter()); + } else { + return -memory; + } + } + return 0; + } + + /** + * Extend path from a given CursorPos chain to "prepend point" in a B-tree, rooted at this Page. + * + * @param cursorPos presumably pointing to this Page (null if real root), to build upon + * @return new head of the CursorPos chain + */ + public abstract CursorPos getPrependCursorPos(CursorPos cursorPos); + + /** + * Extend path from a given CursorPos chain to "append point" in a B-tree, rooted at this Page. + * + * @param cursorPos presumably pointing to this Page (null if real root), to build upon + * @return new head of the CursorPos chain + */ + public abstract CursorPos getAppendCursorPos(CursorPos cursorPos); + + /** + * Remove all page data recursively. + * + * @param version at which page got removed + * @return adjustment for "unsaved memory" amount + */ + public abstract int removeAllRecursive(long version); + + /** + * Create array for keys storage. + * + * @param size number of entries + * @return values array + */ + private Object[] createKeyStorage(int size) { + return new Object[size]; + } + + /** + * Create array for values storage. + * + * @param size number of entries + * @return values array + */ + final Object[] createValueStorage(int size) { + return new Object[size]; + } + + /** + * A pointer to a page, either in-memory or using a page position. + */ + public static final class PageReference { + + /** + * Singleton object used when arrays of PageReference have not yet been filled. + */ + public static final PageReference EMPTY = new PageReference(null, 0, 0); + + /** + * The position, if known, or 0. + */ + private long pos; + + /** + * The page, if in memory, or null. + */ + private Page page; + + /** + * The descendant count for this child page. + */ + final long count; + + public PageReference(Page page) { + this(page, page.getPos(), page.getTotalCount()); + } + + PageReference(long pos, long count) { + this(null, pos, count); + assert DataUtils.isPageSaved(pos); + } + + private PageReference(Page page, long pos, long count) { + this.page = page; + this.pos = pos; + this.count = count; + } + + public Page getPage() { + return page; + } + + /** + * Clear if necessary, reference to the actual child Page object, + * so it can be garbage collected if not actively used elsewhere. + * Reference is cleared only if corresponding page was already saved on a disk. + */ + void clearPageReference() { + if (page != null) { + page.writeEnd(); + assert page.isSaved() || !page.isComplete(); + if (page.isSaved()) { + assert pos == page.getPos(); + assert count == page.getTotalCount() : count + " != " + page.getTotalCount(); + page = null; + } + } + } + + long getPos() { + return pos; + } + + /** + * Re-acquire position from in-memory page. + */ + void resetPos() { + Page p = page; + if (p != null && p.isSaved()) { + pos = p.getPos(); + assert count == p.getTotalCount(); + } + } + + @Override + public String toString() { + return "Cnt:" + count + ", pos:" + (pos == 0 ? "0" : DataUtils.getPageChunkId(pos) + "-" + DataUtils.getPageOffset(pos) + ":" + DataUtils.getPageMaxLength(pos)) + ((page == null ? DataUtils.getPageType(pos) == 0 : page.isLeaf()) ? " leaf" : " node") + ", page:{" + page + "}"; + } + } + + + private static class NonLeaf extends Page { + /** + * The child page references. + */ + private PageReference[] children; + + /** + * The total entry count of this page and all children. + */ + private long totalCount; + + NonLeaf(MVMap map) { + super(map); + } + + NonLeaf(MVMap map, NonLeaf source, PageReference[] children, long totalCount) { + super(map, source); + this.children = children; + this.totalCount = totalCount; + } + + NonLeaf(MVMap map, Object[] keys, PageReference[] children, long totalCount) { + super(map, keys); + this.children = children; + this.totalCount = totalCount; + } + + @Override + public int getNodeType() { + return DataUtils.PAGE_TYPE_NODE; + } + + @Override + public Page copy(MVMap map) { + return new IncompleteNonLeaf(map, this); + } + + @Override + public Page getChildPage(int index) { + PageReference ref = children[index]; + Page page = ref.getPage(); + if (page == null) { + page = map.readPage(ref.getPos()); + assert ref.getPos() == page.getPos(); + assert ref.count == page.getTotalCount(); + } + return page; + } + + @Override + public long getChildPagePos(int index) { + return children[index].getPos(); + } + + @Override + public Object getValue(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public Page split(int at) { + assert !isSaved(); + int b = getKeyCount() - at; + Object[] bKeys = splitKeys(at, b - 1); + PageReference[] aChildren = new PageReference[at + 1]; + PageReference[] bChildren = new PageReference[b]; + System.arraycopy(children, 0, aChildren, 0, at + 1); + System.arraycopy(children, at + 1, bChildren, 0, b); + children = aChildren; + + long t = 0; + for (PageReference x : aChildren) { + t += x.count; + } + totalCount = t; + t = 0; + for (PageReference x : bChildren) { + t += x.count; + } + Page newPage = createNode(map, bKeys, bChildren, t, 0); + if (isPersistent()) { + recalculateMemory(); + } + return newPage; + } + + @Override + public void expand(int keyCount, Object[] extraKeys, Object[] extraValues) { + throw new UnsupportedOperationException(); + } + + @Override + public long getTotalCount() { + assert !isComplete() || totalCount == calculateTotalCount() : "Total count: " + totalCount + " != " + calculateTotalCount(); + return totalCount; + } + + private long calculateTotalCount() { + long check = 0; + int keyCount = getKeyCount(); + for (int i = 0; i <= keyCount; i++) { + check += children[i].count; + } + return check; + } + + void recalculateTotalCount() { + totalCount = calculateTotalCount(); + } + + @Override + long getCounts(int index) { + return children[index].count; + } + + @Override + public void setChild(int index, Page c) { + assert c != null; + PageReference child = children[index]; + if (c != child.getPage() || c.getPos() != child.getPos()) { + totalCount += c.getTotalCount() - child.count; + children = children.clone(); + children[index] = new PageReference(c); + } + } + + @Override + public Object setValue(int index, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertLeaf(int index, Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public void insertNode(int index, Object key, Page childPage) { + int childCount = getRawChildPageCount(); + insertKey(index, key); + + PageReference[] newChildren = new PageReference[childCount + 1]; + DataUtils.copyWithGap(children, newChildren, childCount, index); + children = newChildren; + children[index] = new PageReference(childPage); + + totalCount += childPage.getTotalCount(); + if (isPersistent()) { + addMemory(MEMORY_POINTER + PAGE_MEMORY_CHILD); + } + } + + @Override + public void remove(int index) { + int childCount = getRawChildPageCount(); + super.remove(index); + if (isPersistent()) { + addMemory(-MEMORY_POINTER - PAGE_MEMORY_CHILD); + } + totalCount -= children[index].count; + PageReference[] newChildren = new PageReference[childCount - 1]; + DataUtils.copyExcept(children, newChildren, childCount, index); + children = newChildren; + } + + @Override + public int removeAllRecursive(long version) { + int unsavedMemory = removePage(version); + if (isPersistent()) { + for (int i = 0, size = map.getChildPageCount(this); i < size; i++) { + PageReference ref = children[i]; + Page page = ref.getPage(); + if (page != null) { + unsavedMemory += page.removeAllRecursive(version); + } else { + long pagePos = ref.getPos(); + assert DataUtils.isPageSaved(pagePos); + if (DataUtils.isLeafPosition(pagePos)) { + map.store.accountForRemovedPage(pagePos, version, map.isSingleWriter()); + } else { + unsavedMemory += map.readPage(pagePos).removeAllRecursive(version); + } + } + } + } + return unsavedMemory; + } + + @Override + public CursorPos getPrependCursorPos(CursorPos cursorPos) { + Page childPage = getChildPage(0); + return childPage.getPrependCursorPos(new CursorPos(this, 0, cursorPos)); + } + + @Override + public CursorPos getAppendCursorPos(CursorPos cursorPos) { + int keyCount = getKeyCount(); + Page childPage = getChildPage(keyCount); + return childPage.getAppendCursorPos(new CursorPos(this, keyCount, cursorPos)); + } + + @Override + protected void readPayLoad(ByteBuffer buff) { + int keyCount = getKeyCount(); + children = new PageReference[keyCount + 1]; + long[] p = new long[keyCount + 1]; + for (int i = 0; i <= keyCount; i++) { + p[i] = buff.getLong(); + } + long total = 0; + for (int i = 0; i <= keyCount; i++) { + long s = DataUtils.readVarLong(buff); + long position = p[i]; + assert position == 0 ? s == 0 : s >= 0; + total += s; + children[i] = position == 0 ? PageReference.EMPTY : new PageReference(position, s); + } + totalCount = total; + } + + @Override + protected void writeValues(WriteBuffer buff) { + } + + @Override + protected void writeChildren(WriteBuffer buff, boolean withCounts) { + int keyCount = getKeyCount(); + for (int i = 0; i <= keyCount; i++) { + buff.putLong(children[i].getPos()); + } + if (withCounts) { + for (int i = 0; i <= keyCount; i++) { + buff.putVarLong(children[i].count); + } + } + } + + @Override + void writeUnsavedRecursive(Chunk chunk, WriteBuffer buff) { + if (!isSaved()) { + int patch = write(chunk, buff); + writeChildrenRecursive(chunk, buff); + int old = buff.position(); + buff.position(patch); + writeChildren(buff, false); + buff.position(old); + } + } + + void writeChildrenRecursive(Chunk chunk, WriteBuffer buff) { + int len = getRawChildPageCount(); + for (int i = 0; i < len; i++) { + PageReference ref = children[i]; + Page p = ref.getPage(); + if (p != null) { + p.writeUnsavedRecursive(chunk, buff); + ref.resetPos(); + } + } + } + + @Override + void writeEnd() { + int len = getRawChildPageCount(); + for (int i = 0; i < len; i++) { + children[i].clearPageReference(); + } + } + + @Override + public int getRawChildPageCount() { + return getKeyCount() + 1; + } + + @Override + protected int calculateMemory() { + return super.calculateMemory() + PAGE_NODE_MEMORY + getRawChildPageCount() * (MEMORY_POINTER + PAGE_MEMORY_CHILD); + } + + @Override + public void dump(StringBuilder buff) { + super.dump(buff); + int keyCount = getKeyCount(); + for (int i = 0; i <= keyCount; i++) { + if (i > 0) { + buff.append(" "); + } + buff.append("[").append(Long.toHexString(children[i].getPos())).append("]"); + if (i < keyCount) { + buff.append(" ").append(getKey(i)); + } + } + } + } + + + private static class IncompleteNonLeaf extends NonLeaf { + + private boolean complete; + + IncompleteNonLeaf(MVMap map, NonLeaf source) { + super(map, source, constructEmptyPageRefs(source.getRawChildPageCount()), source.getTotalCount()); + } + + private static PageReference[] constructEmptyPageRefs(int size) { + // replace child pages with empty pages + PageReference[] children = new PageReference[size]; + Arrays.fill(children, PageReference.EMPTY); + return children; + } + + @Override + void writeUnsavedRecursive(Chunk chunk, WriteBuffer buff) { + if (complete) { + super.writeUnsavedRecursive(chunk, buff); + } else if (!isSaved()) { + writeChildrenRecursive(chunk, buff); + } + } + + @Override + public boolean isComplete() { + return complete; + } + + @Override + public void setComplete() { + recalculateTotalCount(); + complete = true; + } + + @Override + public void dump(StringBuilder buff) { + super.dump(buff); + buff.append(", complete:").append(complete); + } + + } + + + private static class Leaf extends Page { + /** + * The storage for values. + */ + private Object[] values; + + Leaf(MVMap map) { + super(map); + } + + private Leaf(MVMap map, Leaf source) { + super(map, source); + this.values = source.values; + } + + Leaf(MVMap map, Object[] keys, Object[] values) { + super(map, keys); + this.values = values; + } + + @Override + public int getNodeType() { + return PAGE_TYPE_LEAF; + } + + @Override + public Page copy(MVMap map) { + return new Leaf(map, this); + } + + @Override + public Page getChildPage(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public long getChildPagePos(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue(int index) { + return values[index]; + } + + @Override + public Page split(int at) { + assert !isSaved(); + int b = getKeyCount() - at; + Object[] bKeys = splitKeys(at, b); + Object[] bValues = createValueStorage(b); + if (values != null) { + Object[] aValues = createValueStorage(at); + System.arraycopy(values, 0, aValues, 0, at); + System.arraycopy(values, at, bValues, 0, b); + values = aValues; + } + Page newPage = createLeaf(map, bKeys, bValues, 0); + if (isPersistent()) { + recalculateMemory(); + } + return newPage; + } + + @Override + public void expand(int extraKeyCount, Object[] extraKeys, Object[] extraValues) { + int keyCount = getKeyCount(); + expandKeys(extraKeyCount, extraKeys); + if (values != null) { + Object[] newValues = createValueStorage(keyCount + extraKeyCount); + System.arraycopy(values, 0, newValues, 0, keyCount); + System.arraycopy(extraValues, 0, newValues, keyCount, extraKeyCount); + values = newValues; + } + if (isPersistent()) { + recalculateMemory(); + } + } + + @Override + public long getTotalCount() { + return getKeyCount(); + } + + @Override + long getCounts(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public void setChild(int index, Page c) { + throw new UnsupportedOperationException(); + } + + @Override + public Object setValue(int index, Object value) { + DataType valueType = map.getValueType(); + values = values.clone(); + Object old = setValueInternal(index, value); + if (isPersistent()) { + addMemory(valueType.getMemory(value) - valueType.getMemory(old)); + } + return old; + } + + private Object setValueInternal(int index, Object value) { + Object old = values[index]; + values[index] = value; + return old; + } + + @Override + public void insertLeaf(int index, Object key, Object value) { + int keyCount = getKeyCount(); + insertKey(index, key); + + if (values != null) { + Object[] newValues = createValueStorage(keyCount + 1); + DataUtils.copyWithGap(values, newValues, keyCount, index); + values = newValues; + setValueInternal(index, value); + if (isPersistent()) { + addMemory(MEMORY_POINTER + map.getValueType().getMemory(value)); + } + } + } + + @Override + public void insertNode(int index, Object key, Page childPage) { + throw new UnsupportedOperationException(); + } + + @Override + public void remove(int index) { + int keyCount = getKeyCount(); + super.remove(index); + if (values != null) { + if (isPersistent()) { + Object old = getValue(index); + addMemory(-MEMORY_POINTER - map.getValueType().getMemory(old)); + } + Object[] newValues = createValueStorage(keyCount - 1); + DataUtils.copyExcept(values, newValues, keyCount, index); + values = newValues; + } + } + + @Override + public int removeAllRecursive(long version) { + return removePage(version); + } + + @Override + public CursorPos getPrependCursorPos(CursorPos cursorPos) { + return new CursorPos(this, -1, cursorPos); + } + + @Override + public CursorPos getAppendCursorPos(CursorPos cursorPos) { + int keyCount = getKeyCount(); + return new CursorPos(this, -keyCount - 1, cursorPos); + } + + @Override + protected void readPayLoad(ByteBuffer buff) { + int keyCount = getKeyCount(); + values = createValueStorage(keyCount); + map.getValueType().read(buff, values, getKeyCount(), false); + } + + @Override + protected void writeValues(WriteBuffer buff) { + map.getValueType().write(buff, values, getKeyCount(), false); + } + + @Override + protected void writeChildren(WriteBuffer buff, boolean withCounts) { + } + + @Override + void writeUnsavedRecursive(Chunk chunk, WriteBuffer buff) { + if (!isSaved()) { + write(chunk, buff); + } + } + + @Override + void writeEnd() { + } + + @Override + public int getRawChildPageCount() { + return 0; + } + + @Override + protected int calculateMemory() { + int keyCount = getKeyCount(); + int mem = super.calculateMemory() + PAGE_LEAF_MEMORY + keyCount * MEMORY_POINTER; + DataType valueType = map.getValueType(); + for (int i = 0; i < keyCount; i++) { + mem += valueType.getMemory(values[i]); + } + return mem; + } + + @Override + public void dump(StringBuilder buff) { + super.dump(buff); + int keyCount = getKeyCount(); + for (int i = 0; i < keyCount; i++) { + if (i > 0) { + buff.append(" "); + } + buff.append(getKey(i)); + if (values != null) { + buff.append(':'); + buff.append(getValue(i)); + } + } + } + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/RootReference.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/RootReference.java new file mode 100644 index 000000000..e7200bd6f --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/RootReference.java @@ -0,0 +1,263 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +/** + * Class RootReference is an immutable structure to represent state of the MVMap as a whole + * (not related to a particular B-Tree node). + * Single structure would allow for non-blocking atomic state change. + * The most important part of it is a reference to the root node. + * + * @author Andrei Tokar + */ +public final class RootReference +{ + /** + * The root page. + */ + public final Page root; + /** + * The version used for writing. + */ + public final long version; + /** + * Counter of reenterant locks. + */ + private final byte holdCount; + /** + * Lock owner thread id. + */ + private final long ownerId; + /** + * Reference to the previous root in the chain. + * That is the last root of the previous version, which had any data changes. + * Versions without any data changes are dropped from the chain, as it built. + */ + volatile RootReference previous; + /** + * Counter for successful root updates. + */ + final long updateCounter; + /** + * Counter for attempted root updates. + */ + final long updateAttemptCounter; + /** + * Size of the occupied part of the append buffer. + */ + private final byte appendCounter; + + + // This one is used to set root initially and for r/o snapshots + RootReference(Page root, long version) { + this.root = root; + this.version = version; + this.previous = null; + this.updateCounter = 1; + this.updateAttemptCounter = 1; + this.holdCount = 0; + this.ownerId = 0; + this.appendCounter = 0; + } + + private RootReference(RootReference r, Page root, long updateAttemptCounter) { + this.root = root; + this.version = r.version; + this.previous = r.previous; + this.updateCounter = r.updateCounter + 1; + this.updateAttemptCounter = r.updateAttemptCounter + updateAttemptCounter; + this.holdCount = 0; + this.ownerId = 0; + this.appendCounter = r.appendCounter; + } + + // This one is used for locking + private RootReference(RootReference r, int attempt) { + this.root = r.root; + this.version = r.version; + this.previous = r.previous; + this.updateCounter = r.updateCounter + 1; + this.updateAttemptCounter = r.updateAttemptCounter + attempt; + assert r.holdCount == 0 || r.ownerId == Thread.currentThread().getId() // + : Thread.currentThread().getId() + " " + r; + this.holdCount = (byte)(r.holdCount + 1); + this.ownerId = Thread.currentThread().getId(); + this.appendCounter = r.appendCounter; + } + + // This one is used for unlocking + private RootReference(RootReference r, Page root, boolean keepLocked, int appendCounter) { + this.root = root; + this.version = r.version; + this.previous = r.previous; + this.updateCounter = r.updateCounter; + this.updateAttemptCounter = r.updateAttemptCounter; + assert r.holdCount > 0 && r.ownerId == Thread.currentThread().getId() // + : Thread.currentThread().getId() + " " + r; + this.holdCount = (byte)(r.holdCount - (keepLocked ? 0 : 1)); + this.ownerId = this.holdCount == 0 ? 0 : Thread.currentThread().getId(); + this.appendCounter = (byte) appendCounter; + } + + // This one is used for version change + private RootReference(RootReference r, long version, int attempt) { + RootReference previous = r; + RootReference tmp; + while ((tmp = previous.previous) != null && tmp.root == r.root) { + previous = tmp; + } + this.root = r.root; + this.version = version; + this.previous = previous; + this.updateCounter = r.updateCounter + 1; + this.updateAttemptCounter = r.updateAttemptCounter + attempt; + this.holdCount = r.holdCount == 0 ? 0 : (byte)(r.holdCount - 1); + this.ownerId = this.holdCount == 0 ? 0 : r.ownerId; + assert r.appendCounter == 0; + this.appendCounter = 0; + } + + /** + * Try to unlock. + * + * @param newRootPage the new root page + * @param attemptCounter the number of attempts so far + * @return the new, unlocked, root reference, or null if not successful + */ + RootReference updateRootPage(Page newRootPage, long attemptCounter) { + if (holdCount == 0) { + RootReference updatedRootReference = new RootReference(this, newRootPage, attemptCounter); + if (root.map.compareAndSetRoot(this, updatedRootReference)) { + return updatedRootReference; + } + } + return null; + } + + /** + * Try to lock. + * + * @param attemptCounter the number of attempts so far + * @return the new, locked, root reference, or null if not successful + */ + RootReference tryLock(int attemptCounter) { + if (holdCount == 0 || ownerId == Thread.currentThread().getId()) { + RootReference lockedRootReference = new RootReference(this, attemptCounter); + if (root.map.compareAndSetRoot(this, lockedRootReference)) { + return lockedRootReference; + } + } + return null; + } + + /** + * Try to unlock, and if successful update the version + * + * @param version the version + * @param attempt the number of attempts so far + * @return the new, unlocked and updated, root reference, or null if not successful + */ + RootReference tryUnlockAndUpdateVersion(long version, int attempt) { + if (holdCount == 0 || ownerId == Thread.currentThread().getId()) { + RootReference updatedRootReference = new RootReference(this, version, attempt); + if (root.map.compareAndSetRoot(this, updatedRootReference)) { + return updatedRootReference; + } + } + return null; + } + + /** + * Update the page, possibly keeping it locked. + * + * @param page the page + * @param keepLocked whether to keep it locked + * @param appendCounter the number of attempts so far + * @return the new root reference, or null if not successful + */ + RootReference updatePageAndLockedStatus(Page page, boolean keepLocked, int appendCounter) { + assert isLockedByCurrentThread() : this; + RootReference updatedRootReference = new RootReference(this, page, keepLocked, appendCounter); + if (root.map.compareAndSetRoot(this, updatedRootReference)) { + return updatedRootReference; + } + return null; + } + + /** + * Removed old versions that are not longer used. + * + * @param oldestVersionToKeep the oldest version that needs to be retained + */ + void removeUnusedOldVersions(long oldestVersionToKeep) { + // We need to keep at least one previous version (if any) here, + // because in order to retain whole history of some version + // we really need last root of the previous version. + // Root labeled with version "X" is the LAST known root for that version + // and therefore the FIRST known root for the version "X+1" + for(RootReference rootRef = this; rootRef != null; rootRef = rootRef.previous) { + if (rootRef.version < oldestVersionToKeep) { + RootReference previous; + assert (previous = rootRef.previous) == null || previous.getAppendCounter() == 0 // + : oldestVersionToKeep + " " + rootRef.previous; + rootRef.previous = null; + } + } + } + + boolean isLocked() { + return holdCount != 0; + } + + public boolean isLockedByCurrentThread() { + return holdCount != 0 && ownerId == Thread.currentThread().getId(); + } + + long getVersion() { + RootReference prev = previous; + return prev == null || prev.root != root || + prev.appendCounter != appendCounter ? + version : prev.version; + } + + /** + * Does the root have changes since the specified version? + * + * @param version to check against + * @return true if this root has unsaved changes + */ + boolean hasChangesSince(long version) { + return (root.isSaved() ? getAppendCounter() > 0 : getTotalCount() > 0) || getVersion() > version; + } + + int getAppendCounter() { + return appendCounter & 0xff; + } + + /** + * Whether flushing is needed. + * + * @return true if yes + */ + public boolean needFlush() { + return appendCounter != 0; + } + + public long getTotalCount() { + return root.getTotalCount() + getAppendCounter(); + } + + @Override + public String toString() { + return "RootReference(" + System.identityHashCode(root) + + ", v=" + version + + ", owner=" + ownerId + (ownerId == Thread.currentThread().getId() ? "(current)" : "") + + ", holdCnt=" + holdCount + + ", keys=" + root.getTotalCount() + + ", append=" + getAppendCounter() + + ")"; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/SysProperties.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/SysProperties.java new file mode 100644 index 000000000..20aefda17 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/SysProperties.java @@ -0,0 +1,170 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import org.h2.util.MathUtils; +import org.h2.util.Utils; + +import java.io.File; + +public class SysProperties { + public static final String H2_SCRIPT_DIRECTORY = "h2.scriptDirectory"; + public static final String H2_BROWSER = "h2.browser"; + public static final String FILE_SEPARATOR; + public static final String LINE_SEPARATOR; + public static final String USER_HOME; + public static final boolean PREVIEW; + public static final String ALLOWED_CLASSES; + public static final boolean ENABLE_ANONYMOUS_TLS; + public static final String BIND_ADDRESS; + public static final boolean CHECK; + public static final String CLIENT_TRACE_DIRECTORY; + public static final int COLLATOR_CACHE_SIZE; + public static final int CONSOLE_MAX_TABLES_LIST_INDEXES; + public static final int CONSOLE_MAX_TABLES_LIST_COLUMNS; + public static final int CONSOLE_MAX_PROCEDURES_LIST_COLUMNS; + public static final boolean CONSOLE_STREAM; + public static final int CONSOLE_TIMEOUT; + public static final int DATASOURCE_TRACE_LEVEL; + public static final int DELAY_WRONG_PASSWORD_MIN; + public static final int DELAY_WRONG_PASSWORD_MAX; + public static final boolean JAVA_SYSTEM_COMPILER; + public static boolean lobCloseBetweenReads; + public static final int LOB_FILES_PER_DIRECTORY; + public static final int LOB_CLIENT_MAX_SIZE_MEMORY; + public static final int MAX_FILE_RETRY; + public static final int MAX_RECONNECT; + public static final int MAX_MEMORY_ROWS; + public static final long MAX_TRACE_DATA_LENGTH; + public static final boolean MODIFY_ON_WRITE; + public static final boolean NIO_LOAD_MAPPED; + public static final boolean NIO_CLEANER_HACK; + public static final boolean OBJECT_CACHE; + public static final int OBJECT_CACHE_MAX_PER_ELEMENT_SIZE; + public static final int OBJECT_CACHE_SIZE; + public static final boolean OLD_RESULT_SET_GET_OBJECT; + public static final boolean BIG_DECIMAL_IS_DECIMAL; + public static final boolean RETURN_OFFSET_DATE_TIME; + public static final String PG_DEFAULT_CLIENT_ENCODING; + public static final String PREFIX_TEMP_FILE; + public static boolean FORCE_AUTOCOMMIT_OFF_ON_COMMIT; + public static final int SERVER_CACHED_OBJECTS; + public static final int SERVER_RESULT_SET_FETCH_SIZE; + public static final int SOCKET_CONNECT_RETRY; + public static final int SOCKET_CONNECT_TIMEOUT; + public static final boolean SORT_BINARY_UNSIGNED; + public static final boolean SORT_UUID_UNSIGNED; + public static final boolean SORT_NULLS_HIGH; + public static final long SPLIT_FILE_SIZE_SHIFT; + public static final String SYNC_METHOD; + public static final boolean TRACE_IO; + public static final boolean THREAD_DEADLOCK_DETECTOR; + public static final boolean IMPLICIT_RELATIVE_PATH; + public static final String URL_MAP; + public static final boolean USE_THREAD_CONTEXT_CLASS_LOADER; + public static boolean serializeJavaObject; + public static final String JAVA_OBJECT_SERIALIZER; + public static final String CUSTOM_DATA_TYPES_HANDLER; + public static final String AUTH_CONFIG_FILE; + private static final String H2_BASE_DIR = "h2.baseDir"; + + private SysProperties() { + } + + public static void setBaseDir(String var0) { + if (!var0.endsWith("/")) { + var0 = var0 + "/"; + } + + System.setProperty("h2.baseDir", var0); + } + + public static String getBaseDir() { + return Utils.getProperty("h2.baseDir", (String)null); + } + + public static String getScriptDirectory() { + return Utils.getProperty("h2.scriptDirectory", ""); + } + + private static int getAutoScaledForMemoryProperty(String var0, int var1) { + String var2 = Utils.getProperty(var0, (String)null); + if (var2 != null) { + try { + return Integer.decode(var2); + } catch (NumberFormatException var4) { + } + } + + return Utils.scaleForAvailableMemory(var1); + } + + static { + FILE_SEPARATOR = File.separator; + LINE_SEPARATOR = System.lineSeparator(); + USER_HOME = Utils.getProperty("user.home", ""); + PREVIEW = Utils.getProperty("h2.preview", false); + ALLOWED_CLASSES = Utils.getProperty("h2.allowedClasses", "*"); + ENABLE_ANONYMOUS_TLS = Utils.getProperty("h2.enableAnonymousTLS", true); + BIND_ADDRESS = Utils.getProperty("h2.bindAddress", (String)null); + CHECK = Utils.getProperty("h2.check", !"0.9".equals(Utils.getProperty("java.specification.version", (String)null))); + CLIENT_TRACE_DIRECTORY = Utils.getProperty("h2.clientTraceDirectory", "trace.db/"); + COLLATOR_CACHE_SIZE = Utils.getProperty("h2.collatorCacheSize", 32000); + CONSOLE_MAX_TABLES_LIST_INDEXES = Utils.getProperty("h2.consoleTableIndexes", 100); + CONSOLE_MAX_TABLES_LIST_COLUMNS = Utils.getProperty("h2.consoleTableColumns", 500); + CONSOLE_MAX_PROCEDURES_LIST_COLUMNS = Utils.getProperty("h2.consoleProcedureColumns", 300); + CONSOLE_STREAM = Utils.getProperty("h2.consoleStream", true); + CONSOLE_TIMEOUT = Utils.getProperty("h2.consoleTimeout", 1800000); + DATASOURCE_TRACE_LEVEL = Utils.getProperty("h2.dataSourceTraceLevel", 1); + DELAY_WRONG_PASSWORD_MIN = Utils.getProperty("h2.delayWrongPasswordMin", 250); + DELAY_WRONG_PASSWORD_MAX = Utils.getProperty("h2.delayWrongPasswordMax", 4000); + JAVA_SYSTEM_COMPILER = Utils.getProperty("h2.javaSystemCompiler", true); + lobCloseBetweenReads = Utils.getProperty("h2.lobCloseBetweenReads", false); + LOB_FILES_PER_DIRECTORY = Utils.getProperty("h2.lobFilesPerDirectory", 256); + LOB_CLIENT_MAX_SIZE_MEMORY = Utils.getProperty("h2.lobClientMaxSizeMemory", 1048576); + MAX_FILE_RETRY = Math.max(1, Utils.getProperty("h2.maxFileRetry", 16)); + MAX_RECONNECT = Utils.getProperty("h2.maxReconnect", 3); + MAX_MEMORY_ROWS = getAutoScaledForMemoryProperty("h2.maxMemoryRows", 40000); + MAX_TRACE_DATA_LENGTH = (long)Utils.getProperty("h2.maxTraceDataLength", 65535); + MODIFY_ON_WRITE = Utils.getProperty("h2.modifyOnWrite", false); + NIO_LOAD_MAPPED = Utils.getProperty("h2.nioLoadMapped", false); + NIO_CLEANER_HACK = Utils.getProperty("h2.nioCleanerHack", false); + OBJECT_CACHE = Utils.getProperty("h2.objectCache", true); + OBJECT_CACHE_MAX_PER_ELEMENT_SIZE = Utils.getProperty("h2.objectCacheMaxPerElementSize", 4096); + + try { + OBJECT_CACHE_SIZE = MathUtils.nextPowerOf2(Utils.getProperty("h2.objectCacheSize", 1024)); + } catch (IllegalArgumentException var1) { + throw new IllegalStateException("Invalid h2.objectCacheSize", var1); + } + + OLD_RESULT_SET_GET_OBJECT = Utils.getProperty("h2.oldResultSetGetObject", !PREVIEW); + BIG_DECIMAL_IS_DECIMAL = Utils.getProperty("h2.bigDecimalIsDecimal", !PREVIEW); + RETURN_OFFSET_DATE_TIME = Utils.getProperty("h2.returnOffsetDateTime", PREVIEW); + PG_DEFAULT_CLIENT_ENCODING = Utils.getProperty("h2.pgClientEncoding", "UTF-8"); + PREFIX_TEMP_FILE = Utils.getProperty("h2.prefixTempFile", "h2.temp"); + FORCE_AUTOCOMMIT_OFF_ON_COMMIT = Utils.getProperty("h2.forceAutoCommitOffOnCommit", false); + SERVER_CACHED_OBJECTS = Utils.getProperty("h2.serverCachedObjects", 64); + SERVER_RESULT_SET_FETCH_SIZE = Utils.getProperty("h2.serverResultSetFetchSize", 100); + SOCKET_CONNECT_RETRY = Utils.getProperty("h2.socketConnectRetry", 16); + SOCKET_CONNECT_TIMEOUT = Utils.getProperty("h2.socketConnectTimeout", 2000); + SORT_BINARY_UNSIGNED = Utils.getProperty("h2.sortBinaryUnsigned", true); + SORT_UUID_UNSIGNED = Utils.getProperty("h2.sortUuidUnsigned", PREVIEW); + SORT_NULLS_HIGH = Utils.getProperty("h2.sortNullsHigh", false); + SPLIT_FILE_SIZE_SHIFT = (long)Utils.getProperty("h2.splitFileSizeShift", 30); + SYNC_METHOD = Utils.getProperty("h2.syncMethod", "sync"); + TRACE_IO = Utils.getProperty("h2.traceIO", false); + THREAD_DEADLOCK_DETECTOR = Utils.getProperty("h2.threadDeadlockDetector", false); + IMPLICIT_RELATIVE_PATH = Utils.getProperty("h2.implicitRelativePath", false); + URL_MAP = Utils.getProperty("h2.urlMap", (String)null); + USE_THREAD_CONTEXT_CLASS_LOADER = Utils.getProperty("h2.useThreadContextClassLoader", false); + serializeJavaObject = Utils.getProperty("h2.serializeJavaObject", true); + JAVA_OBJECT_SERIALIZER = Utils.getProperty("h2.javaObjectSerializer", (String)null); + CUSTOM_DATA_TYPES_HANDLER = Utils.getProperty("h2.customDataTypesHandler", (String)null); + AUTH_CONFIG_FILE = Utils.getProperty("h2.authConfigFile", (String)null); + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/WriteBuffer.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/WriteBuffer.java new file mode 100644 index 000000000..5471b5b72 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/WriteBuffer.java @@ -0,0 +1,333 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore; + +import org.h2.mvstore.DataUtils; + +import java.nio.ByteBuffer; + +/** + * An auto-resize buffer to write data into a ByteBuffer. + */ +public class WriteBuffer { + + /** + * The maximum size of the buffer in order to be re-used after a clear + * operation. + */ + private static final int MAX_REUSE_CAPACITY = 4 * 1024 * 1024; + + /** + * The minimum number of bytes to grow a buffer at a time. + */ + private static final int MIN_GROW = 1024 * 1024; + + /** + * The buffer that is used after a clear operation. + */ + private ByteBuffer reuse; + + /** + * The current buffer (may be replaced if it is too small). + */ + private ByteBuffer buff; + + public WriteBuffer(int initialSize) { + reuse = ByteBuffer.allocate(initialSize); + buff = reuse; + } + + public WriteBuffer() { + this(MIN_GROW); + } + + /** + * Write a variable size integer. + * + * @param x the value + * @return this + */ + public WriteBuffer putVarInt(int x) { + DataUtils.writeVarInt(ensureCapacity(5), x); + return this; + } + + /** + * Write a variable size long. + * + * @param x the value + * @return this + */ + public WriteBuffer putVarLong(long x) { + DataUtils.writeVarLong(ensureCapacity(10), x); + return this; + } + + /** + * Write the characters of a string in a format similar to UTF-8. + * + * @param s the string + * @param len the number of characters to write + * @return this + */ + public WriteBuffer putStringData(String s, int len) { + ByteBuffer b = ensureCapacity(3 * len); + DataUtils.writeStringData(b, s, len); + return this; + } + + /** + * Put a byte. + * + * @param x the value + * @return this + */ + public WriteBuffer put(byte x) { + ensureCapacity(1).put(x); + return this; + } + + /** + * Put a character. + * + * @param x the value + * @return this + */ + public WriteBuffer putChar(char x) { + ensureCapacity(2).putChar(x); + return this; + } + + /** + * Put a short. + * + * @param x the value + * @return this + */ + public WriteBuffer putShort(short x) { + ensureCapacity(2).putShort(x); + return this; + } + + /** + * Put an integer. + * + * @param x the value + * @return this + */ + public WriteBuffer putInt(int x) { + ensureCapacity(4).putInt(x); + return this; + } + + /** + * Put a long. + * + * @param x the value + * @return this + */ + public WriteBuffer putLong(long x) { + ensureCapacity(8).putLong(x); + return this; + } + + /** + * Put a float. + * + * @param x the value + * @return this + */ + public WriteBuffer putFloat(float x) { + ensureCapacity(4).putFloat(x); + return this; + } + + /** + * Put a double. + * + * @param x the value + * @return this + */ + public WriteBuffer putDouble(double x) { + ensureCapacity(8).putDouble(x); + return this; + } + + /** + * Put a byte array. + * + * @param bytes the value + * @return this + */ + public WriteBuffer put(byte[] bytes) { + ensureCapacity(bytes.length).put(bytes); + return this; + } + + /** + * Put a byte array. + * + * @param bytes the value + * @param offset the source offset + * @param length the number of bytes + * @return this + */ + public WriteBuffer put(byte[] bytes, int offset, int length) { + ensureCapacity(length).put(bytes, offset, length); + return this; + } + + /** + * Put the contents of a byte buffer. + * + * @param src the source buffer + * @return this + */ + public WriteBuffer put(ByteBuffer src) { + ensureCapacity(src.remaining()).put(src); + return this; + } + + /** + * Set the limit, possibly growing the buffer. + * + * @param newLimit the new limit + * @return this + */ + public WriteBuffer limit(int newLimit) { + ensureCapacity(newLimit - buff.position()).limit(newLimit); + return this; + } + + /** + * Get the capacity. + * + * @return the capacity + */ + public int capacity() { + return buff.capacity(); + } + + /** + * Set the position. + * + * @param newPosition the new position + * @return the new position + */ + public WriteBuffer position(int newPosition) { + buff.position(newPosition); + return this; + } + + /** + * Get the limit. + * + * @return the limit + */ + public int limit() { + return buff.limit(); + } + + /** + * Get the current position. + * + * @return the position + */ + public int position() { + return buff.position(); + } + + /** + * Copy the data into the destination array. + * + * @param dst the destination array + * @return this + */ + public WriteBuffer get(byte[] dst) { + buff.get(dst); + return this; + } + + /** + * Update an integer at the given index. + * + * @param index the index + * @param value the value + * @return this + */ + public WriteBuffer putInt(int index, int value) { + buff.putInt(index, value); + return this; + } + + /** + * Update a short at the given index. + * + * @param index the index + * @param value the value + * @return this + */ + public WriteBuffer putShort(int index, short value) { + buff.putShort(index, value); + return this; + } + + /** + * Clear the buffer after use. + * + * @return this + */ + public WriteBuffer clear() { + if (buff.limit() > MAX_REUSE_CAPACITY) { + buff = reuse; + } else if (buff != reuse) { + reuse = buff; + } + buff.clear(); + return this; + } + + /** + * Get the byte buffer. + * + * @return the byte buffer + */ + public ByteBuffer getBuffer() { + return buff; + } + + private ByteBuffer ensureCapacity(int len) { + if (buff.remaining() < len) { + grow(len); + } + return buff; + } + + private void grow(int additional) { + ByteBuffer temp = buff; + int needed = additional - temp.remaining(); + // grow at least MIN_GROW + long grow = Math.max(needed, MIN_GROW); + // grow at least 50% of the current size + grow = Math.max(temp.capacity() / 2, grow); + // the new capacity is at most Integer.MAX_VALUE + int newCapacity = (int) Math.min(Integer.MAX_VALUE, temp.capacity() + grow); + if (newCapacity < needed) { + throw new OutOfMemoryError("Capacity: " + newCapacity + " needed: " + needed); + } + try { + buff = ByteBuffer.allocate(newCapacity); + } catch (OutOfMemoryError e) { + throw new OutOfMemoryError("Capacity: " + newCapacity); + } + temp.flip(); + buff.put(temp); + if (newCapacity <= MAX_REUSE_CAPACITY) { + reuse = buff; + } + } + +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/cache/CacheLongKeyLIRS.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/cache/CacheLongKeyLIRS.java new file mode 100644 index 000000000..c7d2c6435 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/cache/CacheLongKeyLIRS.java @@ -0,0 +1,1215 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.cache; + + + +import org.dizitart.no2.mvstore.compat.v1.mvstore.DataUtils; + +import java.lang.ref.WeakReference; +import java.util.*; + +/** + * A scan resistant cache that uses keys of type long. It is meant to cache + * objects that are relatively costly to acquire, for example file content. + *

+ * This implementation is multi-threading safe and supports concurrent access. + * Null keys or null values are not allowed. The map fill factor is at most 75%. + *

+ * Each entry is assigned a distinct memory size, and the cache will try to use + * at most the specified amount of memory. The memory unit is not relevant, + * however it is suggested to use bytes as the unit. + *

+ * This class implements an approximation of the LIRS replacement algorithm + * invented by Xiaodong Zhang and Song Jiang as described in + * http://www.cse.ohio-state.edu/~zhang/lirs-sigmetrics-02.html with a few + * smaller changes: An additional queue for non-resident entries is used, to + * prevent unbound memory usage. The maximum size of this queue is at most the + * size of the rest of the stack. About 6.25% of the mapped entries are cold. + *

+ * Internally, the cache is split into a number of segments, and each segment is + * an individual LIRS cache. + *

+ * Accessed entries are only moved to the top of the stack if at least a number + * of other entries have been moved to the front (8 per segment by default). + * Write access and moving entries to the top of the stack is synchronized per + * segment. + * + * @author Thomas Mueller + * @param the value type + */ +public class CacheLongKeyLIRS { + + /** + * The maximum memory this cache should use. + */ + private long maxMemory; + + private final Segment[] segments; + + private final int segmentCount; + private final int segmentShift; + private final int segmentMask; + private final int stackMoveDistance; + private final int nonResidentQueueSize; + private final int nonResidentQueueSizeHigh; + + /** + * Create a new cache with the given memory size. + * + * @param config the configuration + */ + @SuppressWarnings("unchecked") + public CacheLongKeyLIRS(Config config) { + setMaxMemory(config.maxMemory); + this.nonResidentQueueSize = config.nonResidentQueueSize; + this.nonResidentQueueSizeHigh = config.nonResidentQueueSizeHigh; + DataUtils.checkArgument( + Integer.bitCount(config.segmentCount) == 1, + "The segment count must be a power of 2, is {0}", config.segmentCount); + this.segmentCount = config.segmentCount; + this.segmentMask = segmentCount - 1; + this.stackMoveDistance = config.stackMoveDistance; + segments = new Segment[segmentCount]; + clear(); + // use the high bits for the segment + this.segmentShift = 32 - Integer.bitCount(segmentMask); + } + + /** + * Remove all entries. + */ + public void clear() { + long max = getMaxItemSize(); + for (int i = 0; i < segmentCount; i++) { + segments[i] = new Segment<>(max, stackMoveDistance, 8, nonResidentQueueSize, + nonResidentQueueSizeHigh); + } + } + + /** + * Determines max size of the data item size to fit into cache + * @return data items size limit + */ + public long getMaxItemSize() { + return Math.max(1, maxMemory / segmentCount); + } + + private Entry find(long key) { + int hash = getHash(key); + return getSegment(hash).find(key, hash); + } + + /** + * Check whether there is a resident entry for the given key. This + * method does not adjust the internal state of the cache. + * + * @param key the key (may not be null) + * @return true if there is a resident entry + */ + public boolean containsKey(long key) { + Entry e = find(key); + return e != null && e.value != null; + } + + /** + * Get the value for the given key if the entry is cached. This method does + * not modify the internal state. + * + * @param key the key (may not be null) + * @return the value, or null if there is no resident entry + */ + public V peek(long key) { + Entry e = find(key); + return e == null ? null : e.getValue(); + } + + /** + * Add an entry to the cache using the average memory size. + * + * @param key the key (may not be null) + * @param value the value (may not be null) + * @return the old value, or null if there was no resident entry + */ + public V put(long key, V value) { + return put(key, value, sizeOf(value)); + } + + /** + * Add an entry to the cache. The entry may or may not exist in the + * cache yet. This method will usually mark unknown entries as cold and + * known entries as hot. + * + * @param key the key (may not be null) + * @param value the value (may not be null) + * @param memory the memory used for the given entry + * @return the old value, or null if there was no resident entry + */ + public V put(long key, V value, int memory) { + if (value == null) { + throw DataUtils.newIllegalArgumentException( + "The value may not be null"); + } + int hash = getHash(key); + int segmentIndex = getSegmentIndex(hash); + Segment s = segments[segmentIndex]; + // check whether resize is required: synchronize on s, to avoid + // concurrent resizes (concurrent reads read + // from the old segment) + synchronized (s) { + s = resizeIfNeeded(s, segmentIndex); + return s.put(key, hash, value, memory); + } + } + + private Segment resizeIfNeeded(Segment s, int segmentIndex) { + int newLen = s.getNewMapLen(); + if (newLen == 0) { + return s; + } + // another thread might have resized + // (as we retrieved the segment before synchronizing on it) + Segment s2 = segments[segmentIndex]; + if (s == s2) { + // no other thread resized, so we do + s = new Segment<>(s, newLen); + segments[segmentIndex] = s; + } + return s; + } + + /** + * Get the size of the given value. The default implementation returns 1. + * + * @param value the value + * @return the size + */ + @SuppressWarnings("unused") + protected int sizeOf(V value) { + return 1; + } + + /** + * Remove an entry. Both resident and non-resident entries can be + * removed. + * + * @param key the key (may not be null) + * @return the old value, or null if there was no resident entry + */ + public V remove(long key) { + int hash = getHash(key); + int segmentIndex = getSegmentIndex(hash); + Segment s = segments[segmentIndex]; + // check whether resize is required: synchronize on s, to avoid + // concurrent resizes (concurrent reads read + // from the old segment) + synchronized (s) { + s = resizeIfNeeded(s, segmentIndex); + return s.remove(key, hash); + } + } + + /** + * Get the memory used for the given key. + * + * @param key the key (may not be null) + * @return the memory, or 0 if there is no resident entry + */ + public int getMemory(long key) { + Entry e = find(key); + return e == null ? 0 : e.getMemory(); + } + + /** + * Get the value for the given key if the entry is cached. This method + * adjusts the internal state of the cache sometimes, to ensure commonly + * used entries stay in the cache. + * + * @param key the key (may not be null) + * @return the value, or null if there is no resident entry + */ + public V get(long key) { + int hash = getHash(key); + Segment s = getSegment(hash); + Entry e = s.find(key, hash); + return s.get(e); + } + + private Segment getSegment(int hash) { + return segments[getSegmentIndex(hash)]; + } + + private int getSegmentIndex(int hash) { + return (hash >>> segmentShift) & segmentMask; + } + + /** + * Get the hash code for the given key. The hash code is + * further enhanced to spread the values more evenly. + * + * @param key the key + * @return the hash code + */ + static int getHash(long key) { + int hash = (int) ((key >>> 32) ^ key); + // a supplemental secondary hash function + // to protect against hash codes that don't differ much + hash = ((hash >>> 16) ^ hash) * 0x45d9f3b; + hash = ((hash >>> 16) ^ hash) * 0x45d9f3b; + hash = (hash >>> 16) ^ hash; + return hash; + } + + /** + * Get the currently used memory. + * + * @return the used memory + */ + public long getUsedMemory() { + long x = 0; + for (Segment s : segments) { + x += s.usedMemory; + } + return x; + } + + /** + * Set the maximum memory this cache should use. This will not + * immediately cause entries to get removed however; it will only change + * the limit. To resize the internal array, call the clear method. + * + * @param maxMemory the maximum size (1 or larger) in bytes + */ + public void setMaxMemory(long maxMemory) { + DataUtils.checkArgument( + maxMemory > 0, + "Max memory must be larger than 0, is {0}", maxMemory); + this.maxMemory = maxMemory; + if (segments != null) { + long max = 1 + maxMemory / segments.length; + for (Segment s : segments) { + s.setMaxMemory(max); + } + } + } + + /** + * Get the maximum memory to use. + * + * @return the maximum memory + */ + public long getMaxMemory() { + return maxMemory; + } + + /** + * Get the entry set for all resident entries. + * + * @return the entry set + */ + public synchronized Set> entrySet() { + HashMap map = new HashMap<>(); + for (long k : keySet()) { + V value = peek(k); + if (value != null) { + map.put(k, value); + } + } + return map.entrySet(); + } + + /** + * Get the set of keys for resident entries. + * + * @return the set of keys + */ + public Set keySet() { + HashSet set = new HashSet<>(); + for (Segment s : segments) { + set.addAll(s.keySet()); + } + return set; + } + + /** + * Get the number of non-resident entries in the cache. + * + * @return the number of non-resident entries + */ + public int sizeNonResident() { + int x = 0; + for (Segment s : segments) { + x += s.queue2Size; + } + return x; + } + + /** + * Get the length of the internal map array. + * + * @return the size of the array + */ + public int sizeMapArray() { + int x = 0; + for (Segment s : segments) { + x += s.entries.length; + } + return x; + } + + /** + * Get the number of hot entries in the cache. + * + * @return the number of hot entries + */ + public int sizeHot() { + int x = 0; + for (Segment s : segments) { + x += s.mapSize - s.queueSize - s.queue2Size; + } + return x; + } + + /** + * Get the number of cache hits. + * + * @return the cache hits + */ + public long getHits() { + long x = 0; + for (Segment s : segments) { + x += s.hits; + } + return x; + } + + /** + * Get the number of cache misses. + * + * @return the cache misses + */ + public long getMisses() { + int x = 0; + for (Segment s : segments) { + x += s.misses; + } + return x; + } + + /** + * Get the number of resident entries. + * + * @return the number of entries + */ + public int size() { + int x = 0; + for (Segment s : segments) { + x += s.mapSize - s.queue2Size; + } + return x; + } + + /** + * Get the list of keys. This method allows to read the internal state of + * the cache. + * + * @param cold if true, only keys for the cold entries are returned + * @param nonResident true for non-resident entries + * @return the key list + */ + public List keys(boolean cold, boolean nonResident) { + ArrayList keys = new ArrayList<>(); + for (Segment s : segments) { + keys.addAll(s.keys(cold, nonResident)); + } + return keys; + } + + /** + * Get the values for all resident entries. + * + * @return the entry set + */ + public List values() { + ArrayList list = new ArrayList<>(); + for (long k : keySet()) { + V value = peek(k); + if (value != null) { + list.add(value); + } + } + return list; + } + + /** + * Check whether the cache is empty. + * + * @return true if it is empty + */ + public boolean isEmpty() { + return size() == 0; + } + + /** + * Check whether the given value is stored. + * + * @param value the value + * @return true if it is stored + */ + public boolean containsValue(V value) { + return getMap().containsValue(value); + } + + /** + * Convert this cache to a map. + * + * @return the map + */ + public Map getMap() { + HashMap map = new HashMap<>(); + for (long k : keySet()) { + V x = peek(k); + if (x != null) { + map.put(k, x); + } + } + return map; + } + + /** + * Add all elements of the map to this cache. + * + * @param m the map + */ + public void putAll(Map m) { + for (Map.Entry e : m.entrySet()) { + // copy only non-null entries + put(e.getKey(), e.getValue()); + } + } + + /** + * Loop through segments, trimming the non resident queue. + */ + public void trimNonResidentQueue() { + for (Segment s : segments) { + synchronized (s) { + s.trimNonResidentQueue(); + } + } + } + + /** + * A cache segment + * + * @param the value type + */ + private static class Segment { + + /** + * The number of (hot, cold, and non-resident) entries in the map. + */ + int mapSize; + + /** + * The size of the LIRS queue for resident cold entries. + */ + int queueSize; + + /** + * The size of the LIRS queue for non-resident cold entries. + */ + int queue2Size; + + /** + * The number of cache hits. + */ + long hits; + + /** + * The number of cache misses. + */ + long misses; + + /** + * The map array. The size is always a power of 2. + */ + final Entry[] entries; + + /** + * The currently used memory. + */ + long usedMemory; + + /** + * How many other item are to be moved to the top of the stack before + * the current item is moved. + */ + private final int stackMoveDistance; + + /** + * The maximum memory this cache should use in bytes. + */ + private long maxMemory; + + /** + * The bit mask that is applied to the key hash code to get the index in + * the map array. The mask is the length of the array minus one. + */ + private final int mask; + + /** + * Low watermark for the number of entries in the non-resident queue, + * as a factor of the number of entries in the map. + */ + private final int nonResidentQueueSize; + + /** + * High watermark for the number of entries in the non-resident queue, + * as a factor of the number of entries in the map. + */ + private final int nonResidentQueueSizeHigh; + + /** + * The stack of recently referenced elements. This includes all hot + * entries, and the recently referenced cold entries. Resident cold + * entries that were not recently referenced, as well as non-resident + * cold entries, are not in the stack. + *

+ * There is always at least one entry: the head entry. + */ + private final Entry stack; + + /** + * The number of entries in the stack. + */ + private int stackSize; + + /** + * The queue of resident cold entries. + *

+ * There is always at least one entry: the head entry. + */ + private final Entry queue; + + /** + * The queue of non-resident cold entries. + *

+ * There is always at least one entry: the head entry. + */ + private final Entry queue2; + + /** + * The number of times any item was moved to the top of the stack. + */ + private int stackMoveCounter; + + /** + * Create a new cache segment. + * @param maxMemory the maximum memory to use + * @param stackMoveDistance the number of other entries to be moved to + * the top of the stack before moving an entry to the top + * @param len the number of hash table buckets (must be a power of 2) + * @param nonResidentQueueSize the non-resident queue size low watermark factor + * @param nonResidentQueueSizeHigh the non-resident queue size high watermark factor + */ + Segment(long maxMemory, int stackMoveDistance, int len, + int nonResidentQueueSize, int nonResidentQueueSizeHigh) { + setMaxMemory(maxMemory); + this.stackMoveDistance = stackMoveDistance; + this.nonResidentQueueSize = nonResidentQueueSize; + this.nonResidentQueueSizeHigh = nonResidentQueueSizeHigh; + + // the bit mask has all bits set + mask = len - 1; + + // initialize the stack and queue heads + stack = new Entry<>(); + stack.stackPrev = stack.stackNext = stack; + queue = new Entry<>(); + queue.queuePrev = queue.queueNext = queue; + queue2 = new Entry<>(); + queue2.queuePrev = queue2.queueNext = queue2; + + @SuppressWarnings("unchecked") + Entry[] e = new Entry[len]; + entries = e; + } + + /** + * Create a new cache segment from an existing one. + * The caller must synchronize on the old segment, to avoid + * concurrent modifications. + * + * @param old the old segment + * @param len the number of hash table buckets (must be a power of 2) + */ + Segment(Segment old, int len) { + this(old.maxMemory, old.stackMoveDistance, len, + old.nonResidentQueueSize, old.nonResidentQueueSizeHigh); + hits = old.hits; + misses = old.misses; + Entry s = old.stack.stackPrev; + while (s != old.stack) { + Entry e = new Entry<>(s); + addToMap(e); + addToStack(e); + s = s.stackPrev; + } + s = old.queue.queuePrev; + while (s != old.queue) { + Entry e = find(s.key, getHash(s.key)); + if (e == null) { + e = new Entry<>(s); + addToMap(e); + } + addToQueue(queue, e); + s = s.queuePrev; + } + s = old.queue2.queuePrev; + while (s != old.queue2) { + Entry e = find(s.key, getHash(s.key)); + if (e == null) { + e = new Entry<>(s); + addToMap(e); + } + addToQueue(queue2, e); + s = s.queuePrev; + } + } + + /** + * Calculate the new number of hash table buckets if the internal map + * should be re-sized. + * + * @return 0 if no resizing is needed, or the new length + */ + int getNewMapLen() { + int len = mask + 1; + if (len * 3 < mapSize * 4 && len < (1 << 28)) { + // more than 75% usage + return len * 2; + } else if (len > 32 && len / 8 > mapSize) { + // less than 12% usage + return len / 2; + } + return 0; + } + + private void addToMap(Entry e) { + int index = getHash(e.key) & mask; + e.mapNext = entries[index]; + entries[index] = e; + usedMemory += e.getMemory(); + mapSize++; + } + + /** + * Get the value from the given entry. + * This method adjusts the internal state of the cache sometimes, + * to ensure commonly used entries stay in the cache. + * + * @param e the entry + * @return the value, or null if there is no resident entry + */ + synchronized V get(Entry e) { + V value = e == null ? null : e.getValue(); + if (value == null) { + // the entry was not found + // or it was a non-resident entry + misses++; + } else { + access(e); + hits++; + } + return value; + } + + /** + * Access an item, moving the entry to the top of the stack or front of + * the queue if found. + * + * @param e entry to record access for + */ + private void access(Entry e) { + if (e.isHot()) { + if (e != stack.stackNext && e.stackNext != null) { + if (stackMoveCounter - e.topMove > stackMoveDistance) { + // move a hot entry to the top of the stack + // unless it is already there + boolean wasEnd = e == stack.stackPrev; + removeFromStack(e); + if (wasEnd) { + // if moving the last entry, the last entry + // could now be cold, which is not allowed + pruneStack(); + } + addToStack(e); + } + } + } else { + V v = e.getValue(); + if (v != null) { + removeFromQueue(e); + if (e.reference != null) { + e.value = v; + e.reference = null; + usedMemory += e.memory; + } + if (e.stackNext != null) { + // resident, or even non-resident (weak value reference), + // cold entries become hot if they are on the stack + removeFromStack(e); + // which means a hot entry needs to become cold + // (this entry is cold, that means there is at least one + // more entry in the stack, which must be hot) + convertOldestHotToCold(); + } else { + // cold entries that are not on the stack + // move to the front of the queue + addToQueue(queue, e); + } + // in any case, the cold entry is moved to the top of the stack + addToStack(e); + // but if newly promoted cold/non-resident is the only entry on a stack now + // that means last one is cold, need to prune + pruneStack(); + } + } + } + + /** + * Add an entry to the cache. The entry may or may not exist in the + * cache yet. This method will usually mark unknown entries as cold and + * known entries as hot. + * + * @param key the key (may not be null) + * @param hash the hash + * @param value the value (may not be null) + * @param memory the memory used for the given entry + * @return the old value, or null if there was no resident entry + */ + synchronized V put(long key, int hash, V value, int memory) { + Entry e = find(key, hash); + boolean existed = e != null; + V old = null; + if (existed) { + old = e.getValue(); + remove(key, hash); + } + if (memory > maxMemory) { + // the new entry is too big to fit + return old; + } + e = new Entry<>(key, value, memory); + int index = hash & mask; + e.mapNext = entries[index]; + entries[index] = e; + usedMemory += memory; + if (usedMemory > maxMemory) { + // old entries needs to be removed + evict(); + // if the cache is full, the new entry is + // cold if possible + if (stackSize > 0) { + // the new cold entry is at the top of the queue + addToQueue(queue, e); + } + } + mapSize++; + // added entries are always added to the stack + addToStack(e); + if (existed) { + // if it was there before (even non-resident), it becomes hot + access(e); + } + return old; + } + + /** + * Remove an entry. Both resident and non-resident entries can be + * removed. + * + * @param key the key (may not be null) + * @param hash the hash + * @return the old value, or null if there was no resident entry + */ + synchronized V remove(long key, int hash) { + int index = hash & mask; + Entry e = entries[index]; + if (e == null) { + return null; + } + if (e.key == key) { + entries[index] = e.mapNext; + } else { + Entry last; + do { + last = e; + e = e.mapNext; + if (e == null) { + return null; + } + } while (e.key != key); + last.mapNext = e.mapNext; + } + V old = e.getValue(); + mapSize--; + usedMemory -= e.getMemory(); + if (e.stackNext != null) { + removeFromStack(e); + } + if (e.isHot()) { + // when removing a hot entry, the newest cold entry gets hot, + // so the number of hot entries does not change + e = queue.queueNext; + if (e != queue) { + removeFromQueue(e); + if (e.stackNext == null) { + addToStackBottom(e); + } + } + pruneStack(); + } else { + removeFromQueue(e); + } + return old; + } + + /** + * Evict cold entries (resident and non-resident) until the memory limit + * is reached. The new entry is added as a cold entry, except if it is + * the only entry. + */ + private void evict() { + do { + evictBlock(); + } while (usedMemory > maxMemory); + } + + private void evictBlock() { + // ensure there are not too many hot entries: right shift of 5 is + // division by 32, that means if there are only 1/32 (3.125%) or + // less cold entries, a hot entry needs to become cold + while (queueSize <= ((mapSize - queue2Size) >>> 5) && stackSize > 0) { + convertOldestHotToCold(); + } + // the oldest resident cold entries become non-resident + while (usedMemory > maxMemory && queueSize > 0) { + Entry e = queue.queuePrev; + usedMemory -= e.memory; + removeFromQueue(e); + e.reference = new WeakReference<>(e.value); + e.value = null; + addToQueue(queue2, e); + // the size of the non-resident-cold entries needs to be limited + trimNonResidentQueue(); + } + } + + void trimNonResidentQueue() { + int residentCount = mapSize - queue2Size; + int maxQueue2SizeHigh = nonResidentQueueSizeHigh * residentCount; + int maxQueue2Size = nonResidentQueueSize * residentCount; + while (queue2Size > maxQueue2Size) { + Entry e = queue2.queuePrev; + if (queue2Size <= maxQueue2SizeHigh) { + WeakReference reference = e.reference; + if (reference != null && reference.get() != null) { + break; // stop trimming if entry holds a value + } + } + int hash = getHash(e.key); + remove(e.key, hash); + } + } + + private void convertOldestHotToCold() { + // the last entry of the stack is known to be hot + Entry last = stack.stackPrev; + if (last == stack) { + // never remove the stack head itself (this would mean the + // internal structure of the cache is corrupt) + throw new IllegalStateException(); + } + // remove from stack - which is done anyway in the stack pruning, + // but we can do it here as well + removeFromStack(last); + // adding an entry to the queue will make it cold + addToQueue(queue, last); + pruneStack(); + } + + /** + * Ensure the last entry of the stack is cold. + */ + private void pruneStack() { + while (true) { + Entry last = stack.stackPrev; + // must stop at a hot entry or the stack head, + // but the stack head itself is also hot, so we + // don't have to test it + if (last.isHot()) { + break; + } + // the cold entry is still in the queue + removeFromStack(last); + } + } + + /** + * Try to find an entry in the map. + * + * @param key the key + * @param hash the hash + * @return the entry (might be a non-resident) + */ + Entry find(long key, int hash) { + int index = hash & mask; + Entry e = entries[index]; + while (e != null && e.key != key) { + e = e.mapNext; + } + return e; + } + + private void addToStack(Entry e) { + e.stackPrev = stack; + e.stackNext = stack.stackNext; + e.stackNext.stackPrev = e; + stack.stackNext = e; + stackSize++; + e.topMove = stackMoveCounter++; + } + + private void addToStackBottom(Entry e) { + e.stackNext = stack; + e.stackPrev = stack.stackPrev; + e.stackPrev.stackNext = e; + stack.stackPrev = e; + stackSize++; + } + + /** + * Remove the entry from the stack. The head itself must not be removed. + * + * @param e the entry + */ + private void removeFromStack(Entry e) { + e.stackPrev.stackNext = e.stackNext; + e.stackNext.stackPrev = e.stackPrev; + e.stackPrev = e.stackNext = null; + stackSize--; + } + + private void addToQueue(Entry q, Entry e) { + e.queuePrev = q; + e.queueNext = q.queueNext; + e.queueNext.queuePrev = e; + q.queueNext = e; + if (e.value != null) { + queueSize++; + } else { + queue2Size++; + } + } + + private void removeFromQueue(Entry e) { + e.queuePrev.queueNext = e.queueNext; + e.queueNext.queuePrev = e.queuePrev; + e.queuePrev = e.queueNext = null; + if (e.value != null) { + queueSize--; + } else { + queue2Size--; + } + } + + /** + * Get the list of keys. This method allows to read the internal state + * of the cache. + * + * @param cold if true, only keys for the cold entries are returned + * @param nonResident true for non-resident entries + * @return the key list + */ + synchronized List keys(boolean cold, boolean nonResident) { + ArrayList keys = new ArrayList<>(); + if (cold) { + Entry start = nonResident ? queue2 : queue; + for (Entry e = start.queueNext; e != start; + e = e.queueNext) { + keys.add(e.key); + } + } else { + for (Entry e = stack.stackNext; e != stack; + e = e.stackNext) { + keys.add(e.key); + } + } + return keys; + } + + /** + * Get the set of keys for resident entries. + * + * @return the set of keys + */ + synchronized Set keySet() { + HashSet set = new HashSet<>(); + for (Entry e = stack.stackNext; e != stack; e = e.stackNext) { + set.add(e.key); + } + for (Entry e = queue.queueNext; e != queue; e = e.queueNext) { + set.add(e.key); + } + return set; + } + + /** + * Set the maximum memory this cache should use. This will not + * immediately cause entries to get removed however; it will only change + * the limit. To resize the internal array, call the clear method. + * + * @param maxMemory the maximum size (1 or larger) in bytes + */ + void setMaxMemory(long maxMemory) { + this.maxMemory = maxMemory; + } + + } + + /** + * A cache entry. Each entry is either hot (low inter-reference recency; + * LIR), cold (high inter-reference recency; HIR), or non-resident-cold. Hot + * entries are in the stack only. Cold entries are in the queue, and may be + * in the stack. Non-resident-cold entries have their value set to null and + * are in the stack and in the non-resident queue. + * + * @param the value type + */ + static class Entry { + + /** + * The key. + */ + final long key; + + /** + * The value. Set to null for non-resident-cold entries. + */ + V value; + + /** + * Weak reference to the value. Set to null for resident entries. + */ + WeakReference reference; + + /** + * The estimated memory used. + */ + final int memory; + + /** + * When the item was last moved to the top of the stack. + */ + int topMove; + + /** + * The next entry in the stack. + */ + Entry stackNext; + + /** + * The previous entry in the stack. + */ + Entry stackPrev; + + /** + * The next entry in the queue (either the resident queue or the + * non-resident queue). + */ + Entry queueNext; + + /** + * The previous entry in the queue. + */ + Entry queuePrev; + + /** + * The next entry in the map (the chained entry). + */ + Entry mapNext; + + + Entry() { + this(0L, null, 0); + } + + Entry(long key, V value, int memory) { + this.key = key; + this.memory = memory; + this.value = value; + } + + Entry(Entry old) { + this(old.key, old.value, old.memory); + this.reference = old.reference; + this.topMove = old.topMove; + } + + /** + * Whether this entry is hot. Cold entries are in one of the two queues. + * + * @return whether the entry is hot + */ + boolean isHot() { + return queueNext == null; + } + + V getValue() { + return value == null ? reference.get() : value; + } + + int getMemory() { + return value == null ? 0 : memory; + } + } + + /** + * The cache configuration. + */ + public static class Config { + + /** + * The maximum memory to use (1 or larger). + */ + public long maxMemory = 1; + + /** + * The number of cache segments (must be a power of 2). + */ + public int segmentCount = 16; + + /** + * How many other item are to be moved to the top of the stack before + * the current item is moved. + */ + public int stackMoveDistance = 32; + + /** + * Low water mark for the number of entries in the non-resident queue, + * as a factor of the number of all other entries in the map. + */ + public final int nonResidentQueueSize = 3; + + /** + * High watermark for the number of entries in the non-resident queue, + * as a factor of the number of all other entries in the map + */ + public final int nonResidentQueueSizeHigh = 12; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/cache/FilePathCache.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/cache/FilePathCache.java new file mode 100644 index 000000000..61a5c86d1 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/cache/FilePathCache.java @@ -0,0 +1,182 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.cache; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FileBase; +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePath; +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePathWrapper; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; + +/** + * A file with a read cache. + */ +public class FilePathCache extends FilePathWrapper { + + /** + * The instance. + */ + public static final FilePathCache INSTANCE = new FilePathCache(); + + /** + * Register the file system. + */ + static { + FilePath.register(INSTANCE); + } + + public static FileChannel wrap(FileChannel f) { + return new FileCache(f); + } + + @Override + public FileChannel open(String mode) throws IOException { + return new FileCache(getBase().open(mode)); + } + + @Override + public String getScheme() { + return "cache"; + } + + /** + * A file with a read cache. + */ + public static class FileCache extends FileBase { + + private static final int CACHE_BLOCK_SIZE = 4 * 1024; + private final FileChannel base; + + private final CacheLongKeyLIRS cache; + + { + CacheLongKeyLIRS.Config cc = new CacheLongKeyLIRS.Config(); + // 1 MB cache size + cc.maxMemory = 1024 * 1024; + cache = new CacheLongKeyLIRS<>(cc); + } + + FileCache(FileChannel base) { + this.base = base; + } + + @Override + protected void implCloseChannel() throws IOException { + base.close(); + } + + @Override + public FileChannel position(long newPosition) throws IOException { + base.position(newPosition); + return this; + } + + @Override + public long position() throws IOException { + return base.position(); + } + + @Override + public int read(ByteBuffer dst) throws IOException { + return base.read(dst); + } + + @Override + public synchronized int read(ByteBuffer dst, long position) throws IOException { + long cachePos = getCachePos(position); + int off = (int) (position - cachePos); + int len = CACHE_BLOCK_SIZE - off; + len = Math.min(len, dst.remaining()); + ByteBuffer buff = cache.get(cachePos); + if (buff == null) { + buff = ByteBuffer.allocate(CACHE_BLOCK_SIZE); + long pos = cachePos; + while (true) { + int read = base.read(buff, pos); + if (read <= 0) { + break; + } + if (buff.remaining() == 0) { + break; + } + pos += read; + } + int read = buff.position(); + if (read == CACHE_BLOCK_SIZE) { + cache.put(cachePos, buff, CACHE_BLOCK_SIZE); + } else { + if (read <= 0) { + return -1; + } + len = Math.min(len, read - off); + } + } + dst.put(buff.array(), off, len); + return len == 0 ? -1 : len; + } + + private static long getCachePos(long pos) { + return (pos / CACHE_BLOCK_SIZE) * CACHE_BLOCK_SIZE; + } + + @Override + public long size() throws IOException { + return base.size(); + } + + @Override + public synchronized FileChannel truncate(long newSize) throws IOException { + cache.clear(); + base.truncate(newSize); + return this; + } + + @Override + public synchronized int write(ByteBuffer src, long position) throws IOException { + clearCache(src, position); + return base.write(src, position); + } + + @Override + public synchronized int write(ByteBuffer src) throws IOException { + clearCache(src, position()); + return base.write(src); + } + + private void clearCache(ByteBuffer src, long position) { + if (cache.size() > 0) { + int len = src.remaining(); + long p = getCachePos(position); + while (len > 0) { + cache.remove(p); + p += CACHE_BLOCK_SIZE; + len -= CACHE_BLOCK_SIZE; + } + } + } + + @Override + public void force(boolean metaData) throws IOException { + base.force(metaData); + } + + @Override + public FileLock tryLock(long position, long size, boolean shared) + throws IOException { + return base.tryLock(position, size, shared); + } + + @Override + public String toString() { + return "cache:" + base.toString(); + } + + } + +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/CompressDeflate.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/CompressDeflate.java new file mode 100644 index 000000000..250f33aa8 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/CompressDeflate.java @@ -0,0 +1,84 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.compress; + +import org.h2.message.DbException; + +import java.util.StringTokenizer; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +public class CompressDeflate implements Compressor { + private int level = -1; + private int strategy = 0; + + public CompressDeflate() { + } + + public void setOptions(String var1) { + if (var1 != null) { + try { + StringTokenizer var2 = new StringTokenizer(var1); + + while(var2.hasMoreElements()) { + String var3 = var2.nextToken(); + if (!"level".equals(var3) && !"l".equals(var3)) { + if ("strategy".equals(var3) || "s".equals(var3)) { + this.strategy = Integer.parseInt(var2.nextToken()); + } + } else { + this.level = Integer.parseInt(var2.nextToken()); + } + + Deflater var4 = new Deflater(this.level); + var4.setStrategy(this.strategy); + } + + } catch (Exception var5) { + throw DbException.get(90102, var1); + } + } + } + + public int compress(byte[] var1, int var2, byte[] var3, int var4) { + Deflater var5 = new Deflater(this.level); + var5.setStrategy(this.strategy); + var5.setInput(var1, 0, var2); + var5.finish(); + int var6 = var5.deflate(var3, var4, var3.length - var4); + if (var6 == 0) { + this.strategy = 0; + this.level = -1; + return this.compress(var1, var2, var3, var4); + } else { + var5.end(); + return var4 + var6; + } + } + + public int getAlgorithm() { + return 2; + } + + public void expand(byte[] var1, int var2, int var3, byte[] var4, int var5, int var6) { + Inflater var7 = new Inflater(); + var7.setInput(var1, var2, var3); + var7.finished(); + + try { + int var8 = var7.inflate(var4, var5, var6); + if (var8 != var6) { + throw new DataFormatException(var8 + " " + var6); + } + } catch (DataFormatException var9) { + throw DbException.get(90104, var9, new String[0]); + } + + var7.end(); + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/CompressLZF.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/CompressLZF.java new file mode 100644 index 000000000..62d0adae7 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/CompressLZF.java @@ -0,0 +1,272 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.compress; + +import java.nio.ByteBuffer; + +public final class CompressLZF implements Compressor { + private static final int HASH_SIZE = 16384; + private static final int MAX_LITERAL = 32; + private static final int MAX_OFF = 8192; + private static final int MAX_REF = 264; + private int[] cachedHashTable; + + public CompressLZF() { + } + + public void setOptions(String var1) { + } + + private static int first(byte[] var0, int var1) { + return var0[var1] << 8 | var0[var1 + 1] & 255; + } + + private static int first(ByteBuffer var0, int var1) { + return var0.get(var1) << 8 | var0.get(var1 + 1) & 255; + } + + private static int next(int var0, byte[] var1, int var2) { + return var0 << 8 | var1[var2 + 2] & 255; + } + + private static int next(int var0, ByteBuffer var1, int var2) { + return var0 << 8 | var1.get(var2 + 2) & 255; + } + + private static int hash(int var0) { + return var0 * 2777 >> 9 & 16383; + } + + public int compress(byte[] var1, int var2, byte[] var3, int var4) { + int var5 = 0; + if (this.cachedHashTable == null) { + this.cachedHashTable = new int[16384]; + } + + int[] var6 = this.cachedHashTable; + int var7 = 0; + ++var4; + int var8 = first((byte[])var1, 0); + + while(true) { + while(var5 < var2 - 4) { + byte var9 = var1[var5 + 2]; + var8 = (var8 << 8) + (var9 & 255); + int var10 = hash(var8); + int var11 = var6[var10]; + var6[var10] = var5; + if (var11 < var5 && var11 > 0 && (var10 = var5 - var11 - 1) < 8192 && var1[var11 + 2] == var9 && var1[var11 + 1] == (byte)(var8 >> 8) && var1[var11] == (byte)(var8 >> 16)) { + int var12 = var2 - var5 - 2; + if (var12 > 264) { + var12 = 264; + } + + if (var7 == 0) { + --var4; + } else { + var3[var4 - var7 - 1] = (byte)(var7 - 1); + var7 = 0; + } + + int var13; + for(var13 = 3; var13 < var12 && var1[var11 + var13] == var1[var5 + var13]; ++var13) { + } + + var13 -= 2; + if (var13 < 7) { + var3[var4++] = (byte)((var10 >> 8) + (var13 << 5)); + } else { + var3[var4++] = (byte)((var10 >> 8) + 224); + var3[var4++] = (byte)(var13 - 7); + } + + var3[var4++] = (byte)var10; + ++var4; + var5 += var13; + var8 = first(var1, var5); + var8 = next(var8, var1, var5); + var6[hash(var8)] = var5++; + var8 = next(var8, var1, var5); + var6[hash(var8)] = var5++; + } else { + var3[var4++] = var1[var5++]; + ++var7; + if (var7 == 32) { + var3[var4 - var7 - 1] = (byte)(var7 - 1); + var7 = 0; + ++var4; + } + } + } + + while(var5 < var2) { + var3[var4++] = var1[var5++]; + ++var7; + if (var7 == 32) { + var3[var4 - var7 - 1] = (byte)(var7 - 1); + var7 = 0; + ++var4; + } + } + + var3[var4 - var7 - 1] = (byte)(var7 - 1); + if (var7 == 0) { + --var4; + } + + return var4; + } + } + + public int compress(ByteBuffer var1, int var2, byte[] var3, int var4) { + int var5 = var1.capacity() - var2; + if (this.cachedHashTable == null) { + this.cachedHashTable = new int[16384]; + } + + int[] var6 = this.cachedHashTable; + int var7 = 0; + ++var4; + int var8 = first((ByteBuffer)var1, 0); + + while(true) { + while(var2 < var5 - 4) { + byte var9 = var1.get(var2 + 2); + var8 = (var8 << 8) + (var9 & 255); + int var10 = hash(var8); + int var11 = var6[var10]; + var6[var10] = var2; + if (var11 < var2 && var11 > 0 && (var10 = var2 - var11 - 1) < 8192 && var1.get(var11 + 2) == var9 && var1.get(var11 + 1) == (byte)(var8 >> 8) && var1.get(var11) == (byte)(var8 >> 16)) { + int var12 = var5 - var2 - 2; + if (var12 > 264) { + var12 = 264; + } + + if (var7 == 0) { + --var4; + } else { + var3[var4 - var7 - 1] = (byte)(var7 - 1); + var7 = 0; + } + + int var13; + for(var13 = 3; var13 < var12 && var1.get(var11 + var13) == var1.get(var2 + var13); ++var13) { + } + + var13 -= 2; + if (var13 < 7) { + var3[var4++] = (byte)((var10 >> 8) + (var13 << 5)); + } else { + var3[var4++] = (byte)((var10 >> 8) + 224); + var3[var4++] = (byte)(var13 - 7); + } + + var3[var4++] = (byte)var10; + ++var4; + var2 += var13; + var8 = first(var1, var2); + var8 = next(var8, var1, var2); + var6[hash(var8)] = var2++; + var8 = next(var8, var1, var2); + var6[hash(var8)] = var2++; + } else { + var3[var4++] = var1.get(var2++); + ++var7; + if (var7 == 32) { + var3[var4 - var7 - 1] = (byte)(var7 - 1); + var7 = 0; + ++var4; + } + } + } + + while(var2 < var5) { + var3[var4++] = var1.get(var2++); + ++var7; + if (var7 == 32) { + var3[var4 - var7 - 1] = (byte)(var7 - 1); + var7 = 0; + ++var4; + } + } + + var3[var4 - var7 - 1] = (byte)(var7 - 1); + if (var7 == 0) { + --var4; + } + + return var4; + } + } + + public void expand(byte[] var1, int var2, int var3, byte[] var4, int var5, int var6) { + if (var2 >= 0 && var5 >= 0 && var6 >= 0) { + do { + int var7 = var1[var2++] & 255; + if (var7 < 32) { + ++var7; + System.arraycopy(var1, var2, var4, var5, var7); + var5 += var7; + var2 += var7; + } else { + int var8 = var7 >> 5; + if (var8 == 7) { + var8 += var1[var2++] & 255; + } + + var8 += 2; + var7 = -((var7 & 31) << 8) - 1; + var7 -= var1[var2++] & 255; + var7 += var5; + if (var5 + var8 >= var4.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + for(int var9 = 0; var9 < var8; ++var9) { + var4[var5++] = var4[var7++]; + } + } + } while(var5 < var6); + + } else { + throw new IllegalArgumentException(); + } + } + + public static void expand(ByteBuffer var0, ByteBuffer var1) { + do { + int var2 = var0.get() & 255; + int var3; + if (var2 < 32) { + ++var2; + + for(var3 = 0; var3 < var2; ++var3) { + var1.put(var0.get()); + } + } else { + var3 = var2 >> 5; + if (var3 == 7) { + var3 += var0.get() & 255; + } + + var3 += 2; + var2 = -((var2 & 31) << 8) - 1; + var2 -= var0.get() & 255; + var2 += var1.position(); + + for(int var4 = 0; var4 < var3; ++var4) { + var1.put(var1.get(var2++)); + } + } + } while(var1.position() < var1.capacity()); + + } + + public int getAlgorithm() { + return 1; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/Compressor.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/Compressor.java new file mode 100644 index 000000000..1a94089ff --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/compress/Compressor.java @@ -0,0 +1,21 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.compress; + +public interface Compressor { + int NO = 0; + int LZF = 1; + int DEFLATE = 2; + + int getAlgorithm(); + + int compress(byte[] var1, int var2, byte[] var3, int var4); + + void expand(byte[] var1, int var2, int var3, byte[] var4, int var5, int var6); + + void setOptions(String var1); +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileBase.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileBase.java new file mode 100644 index 000000000..73baeea53 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileBase.java @@ -0,0 +1,82 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; + +public abstract class FileBase extends FileChannel { + public FileBase() { + } + + public abstract long size() throws IOException; + + public abstract long position() throws IOException; + + public abstract FileChannel position(long var1) throws IOException; + + public abstract int read(ByteBuffer var1) throws IOException; + + public abstract int write(ByteBuffer var1) throws IOException; + + public synchronized int read(ByteBuffer var1, long var2) throws IOException { + long var4 = this.position(); + this.position(var2); + int var6 = this.read(var1); + this.position(var4); + return var6; + } + + public synchronized int write(ByteBuffer var1, long var2) throws IOException { + long var4 = this.position(); + this.position(var2); + int var6 = this.write(var1); + this.position(var4); + return var6; + } + + public abstract FileChannel truncate(long var1) throws IOException; + + public void force(boolean var1) throws IOException { + } + + protected void implCloseChannel() throws IOException { + } + + public FileLock lock(long var1, long var3, boolean var5) throws IOException { + throw new UnsupportedOperationException(); + } + + public MappedByteBuffer map(FileChannel.MapMode var1, long var2, long var4) throws IOException { + throw new UnsupportedOperationException(); + } + + public long read(ByteBuffer[] var1, int var2, int var3) throws IOException { + throw new UnsupportedOperationException(); + } + + public long transferFrom(ReadableByteChannel var1, long var2, long var4) throws IOException { + throw new UnsupportedOperationException(); + } + + public long transferTo(long var1, long var3, WritableByteChannel var5) throws IOException { + throw new UnsupportedOperationException(); + } + + public FileLock tryLock(long var1, long var3, boolean var5) throws IOException { + throw new UnsupportedOperationException(); + } + + public long write(ByteBuffer[] var1, int var2, int var3) throws IOException { + throw new UnsupportedOperationException(); + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileChannelInputStream.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileChannelInputStream.java new file mode 100644 index 000000000..c023a8b82 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileChannelInputStream.java @@ -0,0 +1,56 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +public class FileChannelInputStream extends InputStream { + private final FileChannel channel; + private final boolean closeChannel; + private ByteBuffer buffer; + private long pos; + + public FileChannelInputStream(FileChannel var1, boolean var2) { + this.channel = var1; + this.closeChannel = var2; + } + + public int read() throws IOException { + if (this.buffer == null) { + this.buffer = ByteBuffer.allocate(1); + } + + this.buffer.rewind(); + int var1 = this.channel.read(this.buffer, (long)(this.pos++)); + return var1 < 0 ? -1 : this.buffer.get(0) & 255; + } + + public int read(byte[] var1) throws IOException { + return this.read(var1, 0, var1.length); + } + + public int read(byte[] var1, int var2, int var3) throws IOException { + ByteBuffer var4 = ByteBuffer.wrap(var1, var2, var3); + int var5 = this.channel.read(var4, this.pos); + if (var5 == -1) { + return -1; + } else { + this.pos += (long)var5; + return var5; + } + } + + public void close() throws IOException { + if (this.closeChannel) { + this.channel.close(); + } + + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileChannelOutputStream.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileChannelOutputStream.java new file mode 100644 index 000000000..d481ea99d --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileChannelOutputStream.java @@ -0,0 +1,44 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +public class FileChannelOutputStream extends OutputStream { + private final FileChannel channel; + private final byte[] buffer = new byte[]{0}; + + public FileChannelOutputStream(FileChannel var1, boolean var2) throws IOException { + this.channel = var1; + if (var2) { + var1.position(var1.size()); + } else { + var1.position(0L); + var1.truncate(0L); + } + + } + + public void write(int var1) throws IOException { + this.buffer[0] = (byte)var1; + FileUtils.writeFully(this.channel, ByteBuffer.wrap(this.buffer)); + } + + public void write(byte[] var1) throws IOException { + FileUtils.writeFully(this.channel, ByteBuffer.wrap(var1)); + } + + public void write(byte[] var1, int var2, int var3) throws IOException { + FileUtils.writeFully(this.channel, ByteBuffer.wrap(var1, var2, var3)); + } + + public void close() throws IOException { + this.channel.close(); + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileDisk.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileDisk.java new file mode 100644 index 000000000..38f96c9db --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileDisk.java @@ -0,0 +1,96 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.SysProperties; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.NonWritableChannelException; + + +class FileDisk extends FileBase { + private final RandomAccessFile file; + private final String name; + private final boolean readOnly; + + FileDisk(String var1, String var2) throws FileNotFoundException { + this.file = new RandomAccessFile(var1, var2); + this.name = var1; + this.readOnly = var2.equals("r"); + } + + public void force(boolean var1) throws IOException { + String var2 = SysProperties.SYNC_METHOD; + if (!"".equals(var2)) { + if ("sync".equals(var2)) { + this.file.getFD().sync(); + } else if ("force".equals(var2)) { + this.file.getChannel().force(true); + } else if ("forceFalse".equals(var2)) { + this.file.getChannel().force(false); + } else { + this.file.getFD().sync(); + } + } + + } + + public FileChannel truncate(long var1) throws IOException { + if (this.readOnly) { + throw new NonWritableChannelException(); + } else { + this.file.getChannel().truncate(var1); + return this; + } + } + + public synchronized FileLock tryLock(long var1, long var3, boolean var5) throws IOException { + return this.file.getChannel().tryLock(var1, var3, var5); + } + + public void implCloseChannel() throws IOException { + this.file.close(); + } + + public long position() throws IOException { + return this.file.getFilePointer(); + } + + public long size() throws IOException { + return this.file.length(); + } + + public int read(ByteBuffer var1) throws IOException { + int var2 = this.file.read(var1.array(), var1.arrayOffset() + var1.position(), var1.remaining()); + if (var2 > 0) { + var1.position(var1.position() + var2); + } + + return var2; + } + + public FileChannel position(long var1) throws IOException { + this.file.seek(var1); + return this; + } + + public int write(ByteBuffer var1) throws IOException { + int var2 = var1.remaining(); + this.file.write(var1.array(), var1.arrayOffset() + var1.position(), var2); + var1.position(var1.position() + var2); + return var2; + } + + public String toString() { + return this.name; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileNio.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileNio.java new file mode 100644 index 000000000..5e7fd4424 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileNio.java @@ -0,0 +1,90 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.NonWritableChannelException; + +class FileNio extends FileBase { + private final String name; + private final FileChannel channel; + + FileNio(String var1, String var2) throws IOException { + this.name = var1; + this.channel = (new RandomAccessFile(var1, var2)).getChannel(); + } + + public void implCloseChannel() throws IOException { + this.channel.close(); + } + + public long position() throws IOException { + return this.channel.position(); + } + + public long size() throws IOException { + return this.channel.size(); + } + + public int read(ByteBuffer var1) throws IOException { + return this.channel.read(var1); + } + + public FileChannel position(long var1) throws IOException { + this.channel.position(var1); + return this; + } + + public int read(ByteBuffer var1, long var2) throws IOException { + return this.channel.read(var1, var2); + } + + public int write(ByteBuffer var1, long var2) throws IOException { + return this.channel.write(var1, var2); + } + + public FileChannel truncate(long var1) throws IOException { + long var3 = this.channel.size(); + if (var1 < var3) { + long var5 = this.channel.position(); + this.channel.truncate(var1); + long var7 = this.channel.position(); + if (var5 < var1) { + if (var7 != var5) { + this.channel.position(var5); + } + } else if (var7 > var1) { + this.channel.position(var1); + } + } + + return this; + } + + public void force(boolean var1) throws IOException { + this.channel.force(var1); + } + + public int write(ByteBuffer var1) throws IOException { + try { + return this.channel.write(var1); + } catch (NonWritableChannelException var3) { + throw new IOException("read only"); + } + } + + public synchronized FileLock tryLock(long var1, long var3, boolean var5) throws IOException { + return this.channel.tryLock(var1, var3, var5); + } + + public String toString() { + return "nio:" + this.name; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePath.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePath.java new file mode 100644 index 000000000..a813157b9 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePath.java @@ -0,0 +1,161 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import org.h2.util.MathUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileChannel; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class FilePath { + private static FilePath defaultProvider; + private static ConcurrentHashMap providers; + private static String tempRandom; + private static long tempSequence; + protected String name; + + public FilePath() { + } + + public static FilePath get(String var0) { + var0 = var0.replace('\\', '/'); + int var1 = var0.indexOf(58); + registerDefaultProviders(); + if (var1 < 2) { + return defaultProvider.getPath(var0); + } else { + String var2 = var0.substring(0, var1); + FilePath var3 = providers.get(var2); + if (var3 == null) { + var3 = defaultProvider; + } + + return var3.getPath(var0); + } + } + + private static void registerDefaultProviders() { + if (providers == null || defaultProvider == null) { + ConcurrentHashMap var0 = new ConcurrentHashMap(); + String[] var1 = new String[] { + "org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePathDisk", + "org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePathNio", + "org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FilePathEncrypt", + "org.h2.store.fs.FilePathMem", + "org.h2.store.fs.FilePathMemLZF", + "org.h2.store.fs.FilePathNioMem", + "org.h2.store.fs.FilePathNioMemLZF", + "org.h2.store.fs.FilePathSplit", + "org.h2.store.fs.FilePathNioMapped", + "org.h2.store.fs.FilePathAsync", + "org.h2.store.fs.FilePathZip", + "org.h2.store.fs.FilePathRetryOnInterrupt" + }; + int var2 = var1.length; + + for(int var3 = 0; var3 < var2; ++var3) { + String var4 = var1[var3]; + + try { + FilePath var5 = (FilePath)Class.forName(var4).getDeclaredConstructor().newInstance(); + var0.put(var5.getScheme(), var5); + if (defaultProvider == null) { + defaultProvider = var5; + } + } catch (Exception var6) { + } + } + + providers = var0; + } + + } + + public static void register(FilePath var0) { + registerDefaultProviders(); + providers.put(var0.getScheme(), var0); + } + + public static void unregister(FilePath var0) { + registerDefaultProviders(); + providers.remove(var0.getScheme()); + } + + public abstract long size(); + + public abstract void moveTo(FilePath var1, boolean var2); + + public abstract boolean createFile(); + + public abstract boolean exists(); + + public abstract void delete(); + + public abstract List newDirectoryStream(); + + public abstract FilePath toRealPath(); + + public abstract FilePath getParent(); + + public abstract boolean isDirectory(); + + public abstract boolean isAbsolute(); + + public abstract long lastModified(); + + public abstract boolean canWrite(); + + public abstract void createDirectory(); + + public String getName() { + int var1 = Math.max(this.name.indexOf(58), this.name.lastIndexOf(47)); + return var1 < 0 ? this.name : this.name.substring(var1 + 1); + } + + public abstract OutputStream newOutputStream(boolean var1) throws IOException; + + public abstract FileChannel open(String var1) throws IOException; + + public abstract InputStream newInputStream() throws IOException; + + public abstract boolean setReadOnly(); + + public FilePath createTempFile(String var1, boolean var2) throws IOException { + while(true) { + FilePath var3 = this.getPath(this.name + getNextTempFileNamePart(false) + var1); + if (!var3.exists() && var3.createFile()) { + var3.open("rw").close(); + return var3; + } + + getNextTempFileNamePart(true); + } + } + + protected static synchronized String getNextTempFileNamePart(boolean var0) { + if (var0 || tempRandom == null) { + tempRandom = MathUtils.randomInt(Integer.MAX_VALUE) + "."; + } + + return tempRandom + tempSequence++; + } + + public String toString() { + return this.name; + } + + public abstract String getScheme(); + + public abstract FilePath getPath(String var1); + + public FilePath unwrap() { + return this; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathDisk.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathDisk.java new file mode 100644 index 000000000..3e4341827 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathDisk.java @@ -0,0 +1,368 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.SysProperties; +import org.dizitart.no2.mvstore.compat.v1.mvstore.util.IOUtils; +import org.h2.message.DbException; + +import java.io.*; +import java.net.URL; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.List; + +public class FilePathDisk extends FilePath { + private static final String CLASSPATH_PREFIX = "classpath:"; + + public FilePathDisk() { + } + + public FilePathDisk getPath(String var1) { + FilePathDisk var2 = new FilePathDisk(); + var2.name = translateFileName(var1); + return var2; + } + + public long size() { + if (this.name.startsWith("classpath:")) { + try { + String var1 = this.name.substring("classpath:".length()); + if (!var1.startsWith("/")) { + var1 = "/" + var1; + } + + URL var2 = this.getClass().getResource(var1); + return var2 != null ? new File(var2.getPath()).length() : 0L; + } catch (Exception var3) { + return 0L; + } + } else { + return (new File(this.name)).length(); + } + } + + protected static String translateFileName(String var0) { + var0 = var0.replace('\\', '/'); + if (var0.startsWith("file:")) { + var0 = var0.substring("file:".length()); + } + + return expandUserHomeDirectory(var0); + } + + public static String expandUserHomeDirectory(String var0) { + if (var0.startsWith("~") && (var0.length() == 1 || var0.startsWith("~/"))) { + String var1 = SysProperties.USER_HOME; + var0 = var1 + var0.substring(1); + } + + return var0; + } + + public void moveTo(FilePath var1, boolean var2) { + File var3 = new File(this.name); + File var4 = new File(var1.name); + if (!var3.getAbsolutePath().equals(var4.getAbsolutePath())) { + if (!var3.exists()) { + throw DbException.get(90024, this.name + " (not found)", var1.name); + } else if (var2) { + boolean var7 = var3.renameTo(var4); + if (!var7) { + throw DbException.get(90024, this.name, var1.name); + } + } else if (var4.exists()) { + throw DbException.get(90024, this.name, var1 + " (exists)"); + } else { + for(int var5 = 0; var5 < SysProperties.MAX_FILE_RETRY; ++var5) { + IOUtils.trace("rename", this.name + " >" + var1, (Object)null); + boolean var6 = var3.renameTo(var4); + if (var6) { + return; + } + + wait(var5); + } + + throw DbException.get(90024, this.name, var1.name); + } + } + } + + private static void wait(int var0) { + if (var0 == 8) { + System.gc(); + } + + try { + long var1 = (long)Math.min(256, var0 * var0); + Thread.sleep(var1); + } catch (InterruptedException var3) { + } + + } + + public boolean createFile() { + File var1 = new File(this.name); + int var2 = 0; + + while(var2 < SysProperties.MAX_FILE_RETRY) { + try { + return var1.createNewFile(); + } catch (IOException var4) { + wait(var2); + ++var2; + } + } + + return false; + } + + public boolean exists() { + return (new File(this.name)).exists(); + } + + public void delete() { + File var1 = new File(this.name); + + for(int var2 = 0; var2 < SysProperties.MAX_FILE_RETRY; ++var2) { + IOUtils.trace("delete", this.name, (Object)null); + boolean var3 = var1.delete(); + if (var3 || !var1.exists()) { + return; + } + + wait(var2); + } + + throw DbException.get(90025, this.name); + } + + public List newDirectoryStream() { + ArrayList var1 = new ArrayList(); + File var2 = new File(this.name); + + try { + String[] var3 = var2.list(); + if (var3 != null) { + String var4 = var2.getCanonicalPath(); + if (!var4.endsWith(SysProperties.FILE_SEPARATOR)) { + var4 = var4 + SysProperties.FILE_SEPARATOR; + } + + var1.ensureCapacity(var3.length); + String[] var5 = var3; + int var6 = var3.length; + + for(int var7 = 0; var7 < var6; ++var7) { + String var8 = var5[var7]; + var1.add(this.getPath(var4 + var8)); + } + } + + return var1; + } catch (IOException var9) { + throw DbException.convertIOException(var9, this.name); + } + } + + public boolean canWrite() { + return canWriteInternal(new File(this.name)); + } + + public boolean setReadOnly() { + File var1 = new File(this.name); + return var1.setReadOnly(); + } + + public FilePathDisk toRealPath() { + try { + String var1 = (new File(this.name)).getCanonicalPath(); + return this.getPath(var1); + } catch (IOException var2) { + throw DbException.convertIOException(var2, this.name); + } + } + + public FilePath getParent() { + String var1 = (new File(this.name)).getParent(); + return var1 == null ? null : this.getPath(var1); + } + + public boolean isDirectory() { + return (new File(this.name)).isDirectory(); + } + + public boolean isAbsolute() { + return (new File(this.name)).isAbsolute(); + } + + public long lastModified() { + return (new File(this.name)).lastModified(); + } + + private static boolean canWriteInternal(File var0) { + try { + if (!var0.canWrite()) { + return false; + } + } catch (Exception var16) { + return false; + } + + RandomAccessFile var1 = null; + + boolean var3; + try { + var1 = new RandomAccessFile(var0, "rw"); + boolean var2 = true; + return var2; + } catch (FileNotFoundException var14) { + var3 = false; + } finally { + if (var1 != null) { + try { + var1.close(); + } catch (IOException var13) { + } + } + + } + + return var3; + } + + public void createDirectory() { + File var1 = new File(this.name); + + for(int var2 = 0; var2 < SysProperties.MAX_FILE_RETRY; ++var2) { + if (var1.exists()) { + if (var1.isDirectory()) { + return; + } + + throw DbException.get(90062, this.name + " (a file with this name already exists)"); + } + + if (var1.mkdir()) { + return; + } + + wait(var2); + } + + throw DbException.get(90062, this.name); + } + + public OutputStream newOutputStream(boolean var1) throws IOException { + try { + File var2 = new File(this.name); + File var3 = var2.getParentFile(); + if (var3 != null) { + FileUtils.createDirectories(var3.getAbsolutePath()); + } + + FileOutputStream var4 = new FileOutputStream(this.name, var1); + IOUtils.trace("openFileOutputStream", this.name, var4); + return var4; + } catch (IOException var5) { + freeMemoryAndFinalize(); + return new FileOutputStream(this.name); + } + } + + public InputStream newInputStream() throws IOException { + if (this.name.matches("[a-zA-Z]{2,19}:.*")) { + if (this.name.startsWith("classpath:")) { + String var4 = this.name.substring("classpath:".length()); + if (!var4.startsWith("/")) { + var4 = "/" + var4; + } + + InputStream var2 = this.getClass().getResourceAsStream(var4); + if (var2 == null) { + var2 = Thread.currentThread().getContextClassLoader().getResourceAsStream(var4.substring(1)); + } + + if (var2 == null) { + throw new FileNotFoundException("resource " + var4); + } else { + return var2; + } + } else { + URL var3 = new URL(this.name); + return var3.openStream(); + } + } else { + FileInputStream var1 = new FileInputStream(this.name); + IOUtils.trace("openFileInputStream", this.name, var1); + return var1; + } + } + + static void freeMemoryAndFinalize() { + IOUtils.trace("freeMemoryAndFinalize", (String)null, (Object)null); + Runtime var0 = Runtime.getRuntime(); + long var1 = var0.freeMemory(); + + for(int var3 = 0; var3 < 16; ++var3) { + var0.gc(); + long var4 = var0.freeMemory(); + var0.runFinalization(); + if (var4 == var1) { + break; + } + + var1 = var4; + } + + } + + public FileChannel open(String var1) throws IOException { + FileDisk var2; + try { + var2 = new FileDisk(this.name, var1); + IOUtils.trace("open", this.name, var2); + } catch (IOException var6) { + freeMemoryAndFinalize(); + + try { + var2 = new FileDisk(this.name, var1); + } catch (IOException var5) { + throw var6; + } + } + + return var2; + } + + public String getScheme() { + return "file"; + } + + public FilePath createTempFile(String var1, boolean var2) throws IOException { + String var3 = this.name + "."; + String var4 = (new File(var3)).getName(); + File var5; + if (var2) { + var5 = new File(System.getProperty("java.io.tmpdir", ".")); + } else { + var5 = (new File(var3)).getAbsoluteFile().getParentFile(); + } + + FileUtils.createDirectories(var5.getAbsolutePath()); + + while(true) { + File var6 = new File(var5, var4 + getNextTempFileNamePart(false) + var1); + if (!var6.exists() && var6.createNewFile()) { + return get(var6.getCanonicalPath()); + } + + getNextTempFileNamePart(true); + } + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathEncrypt.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathEncrypt.java new file mode 100644 index 000000000..718c6d700 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathEncrypt.java @@ -0,0 +1,441 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import org.h2.security.AES; +import org.h2.security.BlockCipher; +import org.h2.security.SHA256; +import org.h2.util.MathUtils; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +public class FilePathEncrypt extends FilePathWrapper { + private static final String SCHEME = "encrypt"; + + public FilePathEncrypt() { + } + + public static void register() { + FilePath.register(new FilePathEncrypt()); + } + + public FileChannel open(String var1) throws IOException { + String[] var2 = this.parse(this.name); + FileChannel var3 = FileUtils.open(var2[1], var1); + byte[] var4 = var2[0].getBytes(StandardCharsets.UTF_8); + return new FileEncrypt(this.name, var4, var3); + } + + public String getScheme() { + return "encrypt"; + } + + protected String getPrefix() { + String[] var1 = this.parse(this.name); + return this.getScheme() + ":" + var1[0] + ":"; + } + + public FilePath unwrap(String var1) { + return FilePath.get(this.parse(var1)[1]); + } + + public long size() { + long var1 = this.getBase().size() - 4096L; + var1 = Math.max(0L, var1); + if ((var1 & 4095L) != 0L) { + var1 -= 4096L; + } + + return var1; + } + + public OutputStream newOutputStream(boolean var1) throws IOException { + return new FileChannelOutputStream(this.open("rw"), var1); + } + + public InputStream newInputStream() throws IOException { + return new FileChannelInputStream(this.open("r"), true); + } + + private String[] parse(String var1) { + if (!var1.startsWith(this.getScheme())) { + throw new IllegalArgumentException(var1 + " doesn't start with " + this.getScheme()); + } else { + var1 = var1.substring(this.getScheme().length() + 1); + int var2 = var1.indexOf(58); + if (var2 < 0) { + throw new IllegalArgumentException(var1 + " doesn't contain encryption algorithm and password"); + } else { + String var3 = var1.substring(0, var2); + var1 = var1.substring(var2 + 1); + return new String[]{var3, var1}; + } + } + } + + public static byte[] getPasswordBytes(char[] var0) { + int var1 = var0.length; + byte[] var2 = new byte[var1 * 2]; + + for(int var3 = 0; var3 < var1; ++var3) { + char var4 = var0[var3]; + var2[var3 + var3] = (byte)(var4 >>> 8); + var2[var3 + var3 + 1] = (byte)var4; + } + + return var2; + } + + static class XTS { + private static final int GF_128_FEEDBACK = 135; + private static final int CIPHER_BLOCK_SIZE = 16; + private final BlockCipher cipher; + + XTS(BlockCipher var1) { + this.cipher = var1; + } + + void encrypt(long var1, int var3, byte[] var4, int var5) { + byte[] var6 = this.initTweak(var1); + + int var7; + for(var7 = 0; var7 + 16 <= var3; var7 += 16) { + if (var7 > 0) { + updateTweak(var6); + } + + xorTweak(var4, var7 + var5, var6); + this.cipher.encrypt(var4, var7 + var5, 16); + xorTweak(var4, var7 + var5, var6); + } + + if (var7 < var3) { + updateTweak(var6); + swap(var4, var7 + var5, var7 - 16 + var5, var3 - var7); + xorTweak(var4, var7 - 16 + var5, var6); + this.cipher.encrypt(var4, var7 - 16 + var5, 16); + xorTweak(var4, var7 - 16 + var5, var6); + } + + } + + void decrypt(long var1, int var3, byte[] var4, int var5) { + byte[] var6 = this.initTweak(var1); + byte[] var7 = var6; + + int var8; + for(var8 = 0; var8 + 16 <= var3; var8 += 16) { + if (var8 > 0) { + updateTweak(var6); + if (var8 + 16 + 16 > var3 && var8 + 16 < var3) { + var7 = (byte[])var6.clone(); + updateTweak(var6); + } + } + + xorTweak(var4, var8 + var5, var6); + this.cipher.decrypt(var4, var8 + var5, 16); + xorTweak(var4, var8 + var5, var6); + } + + if (var8 < var3) { + swap(var4, var8, var8 - 16 + var5, var3 - var8 + var5); + xorTweak(var4, var8 - 16 + var5, var7); + this.cipher.decrypt(var4, var8 - 16 + var5, 16); + xorTweak(var4, var8 - 16 + var5, var7); + } + + } + + private byte[] initTweak(long var1) { + byte[] var3 = new byte[16]; + + for(int var4 = 0; var4 < 16; var1 >>>= 8) { + var3[var4] = (byte)((int)(var1 & 255L)); + ++var4; + } + + this.cipher.encrypt(var3, 0, 16); + return var3; + } + + private static void xorTweak(byte[] var0, int var1, byte[] var2) { + for(int var3 = 0; var3 < 16; ++var3) { + var0[var1 + var3] ^= var2[var3]; + } + + } + + private static void updateTweak(byte[] var0) { + byte var1 = 0; + byte var2 = 0; + + for(int var3 = 0; var3 < 16; ++var3) { + var2 = (byte)(var0[var3] >> 7 & 1); + var0[var3] = (byte)((var0[var3] << 1) + var1 & 255); + var1 = var2; + } + + if (var2 != 0) { + var0[0] = (byte)(var0[0] ^ 135); + } + + } + + private static void swap(byte[] var0, int var1, int var2, int var3) { + for(int var4 = 0; var4 < var3; ++var4) { + byte var5 = var0[var1 + var4]; + var0[var1 + var4] = var0[var2 + var4]; + var0[var2 + var4] = var5; + } + + } + } + + public static class FileEncrypt extends FileBase { + static final int BLOCK_SIZE = 4096; + static final int BLOCK_SIZE_MASK = 4095; + static final int HEADER_LENGTH = 4096; + private static final byte[] HEADER = "H2encrypt\n".getBytes(); + private static final int SALT_POS; + private static final int SALT_LENGTH = 8; + private static final int HASH_ITERATIONS = 10; + private final FileChannel base; + private long pos; + private long size; + private final String name; + private XTS xts; + private byte[] encryptionKey; + + public FileEncrypt(String var1, byte[] var2, FileChannel var3) { + this.name = var1; + this.base = var3; + this.encryptionKey = var2; + } + + private void init() throws IOException { + if (this.xts == null) { + this.size = this.base.size() - 4096L; + boolean var1 = this.size < 0L; + byte[] var2; + if (var1) { + byte[] var3 = Arrays.copyOf(HEADER, 4096); + var2 = MathUtils.secureRandomBytes(8); + System.arraycopy(var2, 0, var3, SALT_POS, var2.length); + writeFully(this.base, 0L, ByteBuffer.wrap(var3)); + this.size = 0L; + } else { + var2 = new byte[8]; + readFully(this.base, (long)SALT_POS, ByteBuffer.wrap(var2)); + if ((this.size & 4095L) != 0L) { + this.size -= 4096L; + } + } + + AES var4 = new AES(); + var4.setKey(SHA256.getPBKDF2(this.encryptionKey, var2, 10, 16)); + this.encryptionKey = null; + this.xts = new XTS(var4); + } + } + + protected void implCloseChannel() throws IOException { + this.base.close(); + } + + public FileChannel position(long var1) throws IOException { + this.pos = var1; + return this; + } + + public long position() throws IOException { + return this.pos; + } + + public int read(ByteBuffer var1) throws IOException { + int var2 = this.read(var1, this.pos); + if (var2 > 0) { + this.pos += (long)var2; + } + + return var2; + } + + public int read(ByteBuffer var1, long var2) throws IOException { + int var4 = var1.remaining(); + if (var4 == 0) { + return 0; + } else { + this.init(); + var4 = (int)Math.min((long)var4, this.size - var2); + if (var2 >= this.size) { + return -1; + } else if (var2 < 0L) { + throw new IllegalArgumentException("pos: " + var2); + } else if ((var2 & 4095L) == 0L && (var4 & 4095) == 0) { + this.readInternal(var1, var2, var4); + return var4; + } else { + long var5 = var2 / 4096L * 4096L; + int var7 = (int)(var2 - var5); + int var8 = (var4 + var7 + 4096 - 1) / 4096 * 4096; + ByteBuffer var9 = ByteBuffer.allocate(var8); + this.readInternal(var9, var5, var8); + var9.flip(); + var9.limit(var7 + var4); + var9.position(var7); + var1.put(var9); + return var4; + } + } + } + + private void readInternal(ByteBuffer var1, long var2, int var4) throws IOException { + int var5 = var1.position(); + readFully(this.base, var2 + 4096L, var1); + + for(long var6 = var2 / 4096L; var4 > 0; var4 -= 4096) { + this.xts.decrypt(var6++, 4096, var1.array(), var1.arrayOffset() + var5); + var5 += 4096; + } + + } + + private static void readFully(FileChannel var0, long var1, ByteBuffer var3) throws IOException { + do { + int var4 = var0.read(var3, var1); + if (var4 < 0) { + throw new EOFException(); + } + + var1 += (long)var4; + } while(var3.remaining() > 0); + + } + + public int write(ByteBuffer var1, long var2) throws IOException { + this.init(); + int var4 = var1.remaining(); + long var5; + if ((var2 & 4095L) == 0L && (var4 & 4095) == 0) { + this.writeInternal(var1, var2, var4); + var5 = var2 + (long)var4; + this.size = Math.max(this.size, var5); + return var4; + } else { + var5 = var2 / 4096L * 4096L; + int var7 = (int)(var2 - var5); + int var8 = (var4 + var7 + 4096 - 1) / 4096 * 4096; + ByteBuffer var9 = ByteBuffer.allocate(var8); + int var10 = (int)(this.size - var5 + 4096L - 1L) / 4096 * 4096; + int var11 = Math.min(var8, var10); + if (var11 > 0) { + this.readInternal(var9, var5, var11); + var9.rewind(); + } + + var9.limit(var7 + var4); + var9.position(var7); + var9.put(var1); + var9.limit(var8); + var9.rewind(); + this.writeInternal(var9, var5, var8); + long var12 = var2 + (long)var4; + this.size = Math.max(this.size, var12); + int var14 = (int)(this.size & 4095L); + if (var14 > 0) { + var9 = ByteBuffer.allocate(var14); + writeFully(this.base, var5 + 4096L + (long)var8, var9); + } + + return var4; + } + } + + private void writeInternal(ByteBuffer var1, long var2, int var4) throws IOException { + ByteBuffer var5 = ByteBuffer.allocate(var4); + var5.put(var1); + var5.flip(); + long var6 = var2 / 4096L; + int var8 = 0; + + for(int var9 = var4; var9 > 0; var9 -= 4096) { + this.xts.encrypt(var6++, 4096, var5.array(), var5.arrayOffset() + var8); + var8 += 4096; + } + + writeFully(this.base, var2 + 4096L, var5); + } + + private static void writeFully(FileChannel var0, long var1, ByteBuffer var3) throws IOException { + int var4 = 0; + + do { + int var5 = var0.write(var3, var1 + (long)var4); + var4 += var5; + } while(var3.remaining() > 0); + + } + + public int write(ByteBuffer var1) throws IOException { + int var2 = this.write(var1, this.pos); + if (var2 > 0) { + this.pos += (long)var2; + } + + return var2; + } + + public long size() throws IOException { + this.init(); + return this.size; + } + + public FileChannel truncate(long var1) throws IOException { + this.init(); + if (var1 > this.size) { + return this; + } else if (var1 < 0L) { + throw new IllegalArgumentException("newSize: " + var1); + } else { + int var3 = (int)(var1 & 4095L); + if (var3 > 0) { + this.base.truncate(var1 + 4096L + 4096L); + } else { + this.base.truncate(var1 + 4096L); + } + + this.size = var1; + this.pos = Math.min(this.pos, this.size); + return this; + } + } + + public void force(boolean var1) throws IOException { + this.base.force(var1); + } + + public FileLock tryLock(long var1, long var3, boolean var5) throws IOException { + return this.base.tryLock(var1, var3, var5); + } + + public String toString() { + return this.name; + } + + static { + SALT_POS = HEADER.length; + } + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathNio.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathNio.java new file mode 100644 index 000000000..c5a512ab3 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathNio.java @@ -0,0 +1,22 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import java.io.IOException; +import java.nio.channels.FileChannel; + +public class FilePathNio extends FilePathWrapper { + public FilePathNio() { + } + + public FileChannel open(String var1) throws IOException { + return new FileNio(this.name.substring(this.getScheme().length() + 1), var1); + } + + public String getScheme() { + return "nio"; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathWrapper.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathWrapper.java new file mode 100644 index 000000000..a64627752 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FilePathWrapper.java @@ -0,0 +1,133 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.channels.FileChannel; +import java.util.List; + +public abstract class FilePathWrapper extends FilePath { + private FilePath base; + + public FilePathWrapper() { + } + + public FilePathWrapper getPath(String var1) { + return this.create(var1, this.unwrap(var1)); + } + + public FilePathWrapper wrap(FilePath var1) { + return var1 == null ? null : this.create(this.getPrefix() + var1.name, var1); + } + + public FilePath unwrap() { + return this.unwrap(this.name); + } + + private FilePathWrapper create(String var1, FilePath var2) { + try { + FilePathWrapper var3 = (FilePathWrapper)this.getClass().getDeclaredConstructor().newInstance(); + var3.name = var1; + var3.base = var2; + return var3; + } catch (Exception var4) { + throw new IllegalArgumentException("Path: " + var1, var4); + } + } + + protected String getPrefix() { + return this.getScheme() + ":"; + } + + protected FilePath unwrap(String var1) { + return FilePath.get(var1.substring(this.getScheme().length() + 1)); + } + + protected FilePath getBase() { + return this.base; + } + + public boolean canWrite() { + return this.base.canWrite(); + } + + public void createDirectory() { + this.base.createDirectory(); + } + + public boolean createFile() { + return this.base.createFile(); + } + + public void delete() { + this.base.delete(); + } + + public boolean exists() { + return this.base.exists(); + } + + public FilePath getParent() { + return this.wrap(this.base.getParent()); + } + + public boolean isAbsolute() { + return this.base.isAbsolute(); + } + + public boolean isDirectory() { + return this.base.isDirectory(); + } + + public long lastModified() { + return this.base.lastModified(); + } + + public FilePath toRealPath() { + return this.wrap(this.base.toRealPath()); + } + + public List newDirectoryStream() { + List var1 = this.base.newDirectoryStream(); + int var2 = 0; + + for(int var3 = var1.size(); var2 < var3; ++var2) { + var1.set(var2, this.wrap((FilePath)var1.get(var2))); + } + + return var1; + } + + public void moveTo(FilePath var1, boolean var2) { + this.base.moveTo(((FilePathWrapper)var1).base, var2); + } + + public InputStream newInputStream() throws IOException { + return this.base.newInputStream(); + } + + public OutputStream newOutputStream(boolean var1) throws IOException { + return this.base.newOutputStream(var1); + } + + public FileChannel open(String var1) throws IOException { + return this.base.open(var1); + } + + public boolean setReadOnly() { + return this.base.setReadOnly(); + } + + public long size() { + return this.base.size(); + } + + public FilePath createTempFile(String var1, boolean var2) throws IOException { + return this.wrap(this.base.createTempFile(var1, var2)); + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileUtils.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileUtils.java new file mode 100644 index 000000000..9d9a98df3 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/fs/FileUtils.java @@ -0,0 +1,178 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.fs; + +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class FileUtils { + public FileUtils() { + } + + public static boolean exists(String var0) { + return FilePath.get(var0).exists(); + } + + public static void createDirectory(String var0) { + FilePath.get(var0).createDirectory(); + } + + public static boolean createFile(String var0) { + return FilePath.get(var0).createFile(); + } + + public static void delete(String var0) { + FilePath.get(var0).delete(); + } + + public static String toRealPath(String var0) { + return FilePath.get(var0).toRealPath().toString(); + } + + public static String getParent(String var0) { + FilePath var1 = FilePath.get(var0).getParent(); + return var1 == null ? null : var1.toString(); + } + + public static boolean isAbsolute(String var0) { + return FilePath.get(var0).isAbsolute() || var0.startsWith(File.pathSeparator) || var0.startsWith("/"); + } + + public static void move(String var0, String var1) { + FilePath.get(var0).moveTo(FilePath.get(var1), false); + } + + public static void moveAtomicReplace(String var0, String var1) { + FilePath.get(var0).moveTo(FilePath.get(var1), true); + } + + public static String getName(String var0) { + return FilePath.get(var0).getName(); + } + + public static List newDirectoryStream(String var0) { + List var1 = FilePath.get(var0).newDirectoryStream(); + int var2 = var1.size(); + ArrayList var3 = new ArrayList(var2); + Iterator var4 = var1.iterator(); + + while(var4.hasNext()) { + FilePath var5 = (FilePath)var4.next(); + var3.add(var5.toString()); + } + + return var3; + } + + public static long lastModified(String var0) { + return FilePath.get(var0).lastModified(); + } + + public static long size(String var0) { + return FilePath.get(var0).size(); + } + + public static boolean isDirectory(String var0) { + return FilePath.get(var0).isDirectory(); + } + + public static FileChannel open(String var0, String var1) throws IOException { + return FilePath.get(var0).open(var1); + } + + public static InputStream newInputStream(String var0) throws IOException { + return FilePath.get(var0).newInputStream(); + } + + public static OutputStream newOutputStream(String var0, boolean var1) throws IOException { + return FilePath.get(var0).newOutputStream(var1); + } + + public static boolean canWrite(String var0) { + return FilePath.get(var0).canWrite(); + } + + public static boolean setReadOnly(String var0) { + return FilePath.get(var0).setReadOnly(); + } + + public static String unwrap(String var0) { + return FilePath.get(var0).unwrap().toString(); + } + + public static void deleteRecursive(String var0, boolean var1) { + if (exists(var0)) { + if (isDirectory(var0)) { + Iterator var2 = newDirectoryStream(var0).iterator(); + + while(var2.hasNext()) { + String var3 = (String)var2.next(); + deleteRecursive(var3, var1); + } + } + + if (var1) { + tryDelete(var0); + } else { + delete(var0); + } + } + + } + + public static void createDirectories(String var0) { + if (var0 != null) { + if (exists(var0)) { + if (!isDirectory(var0)) { + createDirectory(var0); + } + } else { + String var1 = getParent(var0); + createDirectories(var1); + createDirectory(var0); + } + } + + } + + public static boolean tryDelete(String var0) { + try { + FilePath.get(var0).delete(); + return true; + } catch (Exception var2) { + return false; + } + } + + public static String createTempFile(String var0, String var1, boolean var2) throws IOException { + return FilePath.get(var0).createTempFile(var1, var2).toString(); + } + + public static void readFully(FileChannel var0, ByteBuffer var1) throws IOException { + do { + int var2 = var0.read(var1); + if (var2 < 0) { + throw new EOFException(); + } + } while(var1.remaining() > 0); + + } + + public static void writeFully(FileChannel var0, ByteBuffer var1) throws IOException { + do { + var0.write(var1); + } while(var1.remaining() > 0); + + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/DataType.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/DataType.java new file mode 100644 index 000000000..35eca916e --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/DataType.java @@ -0,0 +1,72 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.type; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.WriteBuffer; + +import java.nio.ByteBuffer; + +/** + * A data type. + */ +public interface DataType { + + /** + * Compare two keys. + * + * @param a the first key + * @param b the second key + * @return -1 if the first key is smaller, 1 if larger, and 0 if equal + * @throws UnsupportedOperationException if the type is not orderable + */ + int compare(Object a, Object b); + + /** + * Estimate the used memory in bytes. + * + * @param obj the object + * @return the used memory + */ + int getMemory(Object obj); + + /** + * Write an object. + * + * @param buff the target buffer + * @param obj the value + */ + void write(WriteBuffer buff, Object obj); + + /** + * Write a list of objects. + * + * @param buff the target buffer + * @param obj the objects + * @param len the number of objects to write + * @param key whether the objects are keys + */ + void write(WriteBuffer buff, Object[] obj, int len, boolean key); + + /** + * Read an object. + * + * @param buff the source buffer + * @return the object + */ + Object read(ByteBuffer buff); + + /** + * Read a list of objects. + * + * @param buff the target buffer + * @param obj the objects + * @param len the number of objects to read + * @param key whether the objects are keys + */ + void read(ByteBuffer buff, Object[] obj, int len, boolean key); + +} + diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/ObjectDataType.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/ObjectDataType.java new file mode 100644 index 000000000..ae8ee4401 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/ObjectDataType.java @@ -0,0 +1,1552 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.type; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.DataUtils; +import org.dizitart.no2.mvstore.compat.v1.mvstore.WriteBuffer; +import org.h2.util.Utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.UUID; + +/** + * A data type implementation for the most common data types, including + * serializable objects. + */ +public class ObjectDataType implements DataType { + + /** + * The type constants are also used as tag values. + */ + static final int TYPE_NULL = 0; + static final int TYPE_BOOLEAN = 1; + static final int TYPE_BYTE = 2; + static final int TYPE_SHORT = 3; + static final int TYPE_INT = 4; + static final int TYPE_LONG = 5; + static final int TYPE_BIG_INTEGER = 6; + static final int TYPE_FLOAT = 7; + static final int TYPE_DOUBLE = 8; + static final int TYPE_BIG_DECIMAL = 9; + static final int TYPE_CHAR = 10; + static final int TYPE_STRING = 11; + static final int TYPE_UUID = 12; + static final int TYPE_DATE = 13; + static final int TYPE_ARRAY = 14; + static final int TYPE_SERIALIZED_OBJECT = 19; + + /** + * For very common values (e.g. 0 and 1) we save space by encoding the value + * in the tag. e.g. TAG_BOOLEAN_TRUE and TAG_FLOAT_0. + */ + static final int TAG_BOOLEAN_TRUE = 32; + static final int TAG_INTEGER_NEGATIVE = 33; + static final int TAG_INTEGER_FIXED = 34; + static final int TAG_LONG_NEGATIVE = 35; + static final int TAG_LONG_FIXED = 36; + static final int TAG_BIG_INTEGER_0 = 37; + static final int TAG_BIG_INTEGER_1 = 38; + static final int TAG_BIG_INTEGER_SMALL = 39; + static final int TAG_FLOAT_0 = 40; + static final int TAG_FLOAT_1 = 41; + static final int TAG_FLOAT_FIXED = 42; + static final int TAG_DOUBLE_0 = 43; + static final int TAG_DOUBLE_1 = 44; + static final int TAG_DOUBLE_FIXED = 45; + static final int TAG_BIG_DECIMAL_0 = 46; + static final int TAG_BIG_DECIMAL_1 = 47; + static final int TAG_BIG_DECIMAL_SMALL = 48; + static final int TAG_BIG_DECIMAL_SMALL_SCALED = 49; + + /** + * For small-values/small-arrays, we encode the value/array-length in the + * tag. + */ + static final int TAG_INTEGER_0_15 = 64; + static final int TAG_LONG_0_7 = 80; + static final int TAG_STRING_0_15 = 88; + static final int TAG_BYTE_ARRAY_0_15 = 104; + + /** + * Constants for floating point synchronization. + */ + static final int FLOAT_ZERO_BITS = Float.floatToIntBits(0.0f); + static final int FLOAT_ONE_BITS = Float.floatToIntBits(1.0f); + static final long DOUBLE_ZERO_BITS = Double.doubleToLongBits(0.0d); + static final long DOUBLE_ONE_BITS = Double.doubleToLongBits(1.0d); + + static final Class[] COMMON_CLASSES = { boolean.class, byte.class, + short.class, char.class, int.class, long.class, float.class, + double.class, Object.class, Boolean.class, Byte.class, Short.class, + Character.class, Integer.class, Long.class, BigInteger.class, + Float.class, Double.class, BigDecimal.class, String.class, + UUID.class, Date.class }; + + private static final HashMap, Integer> COMMON_CLASSES_MAP = new HashMap<>(32); + + private AutoDetectDataType last = new StringType(this); + + @Override + public int compare(Object a, Object b) { + return last.compare(a, b); + } + + @Override + public int getMemory(Object obj) { + return last.getMemory(obj); + } + + @Override + public void read(ByteBuffer buff, Object[] obj, int len, boolean key) { + for (int i = 0; i < len; i++) { + obj[i] = read(buff); + } + } + + @Override + public void write(WriteBuffer buff, Object[] obj, int len, boolean key) { + for (int i = 0; i < len; i++) { + write(buff, obj[i]); + } + } + + @Override + public void write(WriteBuffer buff, Object obj) { + last.write(buff, obj); + } + + private AutoDetectDataType newType(int typeId) { + switch (typeId) { + case TYPE_NULL: + return new NullType(this); + case TYPE_BOOLEAN: + return new BooleanType(this); + case TYPE_BYTE: + return new ByteType(this); + case TYPE_SHORT: + return new ShortType(this); + case TYPE_CHAR: + return new CharacterType(this); + case TYPE_INT: + return new IntegerType(this); + case TYPE_LONG: + return new LongType(this); + case TYPE_FLOAT: + return new FloatType(this); + case TYPE_DOUBLE: + return new DoubleType(this); + case TYPE_BIG_INTEGER: + return new BigIntegerType(this); + case TYPE_BIG_DECIMAL: + return new BigDecimalType(this); + case TYPE_STRING: + return new StringType(this); + case TYPE_UUID: + return new UUIDType(this); + case TYPE_DATE: + return new DateType(this); + case TYPE_ARRAY: + return new ObjectArrayType(this); + case TYPE_SERIALIZED_OBJECT: + return new SerializedObjectType(this); + } + throw DataUtils.newIllegalStateException(DataUtils.ERROR_INTERNAL, + "Unsupported type {0}", typeId); + } + + @Override + public Object read(ByteBuffer buff) { + int tag = buff.get(); + int typeId; + if (tag <= TYPE_SERIALIZED_OBJECT) { + typeId = tag; + } else { + switch (tag) { + case TAG_BOOLEAN_TRUE: + typeId = TYPE_BOOLEAN; + break; + case TAG_INTEGER_NEGATIVE: + case TAG_INTEGER_FIXED: + typeId = TYPE_INT; + break; + case TAG_LONG_NEGATIVE: + case TAG_LONG_FIXED: + typeId = TYPE_LONG; + break; + case TAG_BIG_INTEGER_0: + case TAG_BIG_INTEGER_1: + case TAG_BIG_INTEGER_SMALL: + typeId = TYPE_BIG_INTEGER; + break; + case TAG_FLOAT_0: + case TAG_FLOAT_1: + case TAG_FLOAT_FIXED: + typeId = TYPE_FLOAT; + break; + case TAG_DOUBLE_0: + case TAG_DOUBLE_1: + case TAG_DOUBLE_FIXED: + typeId = TYPE_DOUBLE; + break; + case TAG_BIG_DECIMAL_0: + case TAG_BIG_DECIMAL_1: + case TAG_BIG_DECIMAL_SMALL: + case TAG_BIG_DECIMAL_SMALL_SCALED: + typeId = TYPE_BIG_DECIMAL; + break; + default: + if (tag >= TAG_INTEGER_0_15 && tag <= TAG_INTEGER_0_15 + 15) { + typeId = TYPE_INT; + } else if (tag >= TAG_STRING_0_15 + && tag <= TAG_STRING_0_15 + 15) { + typeId = TYPE_STRING; + } else if (tag >= TAG_LONG_0_7 && tag <= TAG_LONG_0_7 + 7) { + typeId = TYPE_LONG; + } else if (tag >= TAG_BYTE_ARRAY_0_15 + && tag <= TAG_BYTE_ARRAY_0_15 + 15) { + typeId = TYPE_ARRAY; + } else { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_FILE_CORRUPT, "Unknown tag {0}", + tag); + } + } + } + AutoDetectDataType t = last; + if (typeId != t.typeId) { + last = t = newType(typeId); + } + return t.read(buff, tag); + } + + private static int getTypeId(Object obj) { + if (obj instanceof Integer) { + return TYPE_INT; + } else if (obj instanceof String) { + return TYPE_STRING; + } else if (obj instanceof Long) { + return TYPE_LONG; + } else if (obj instanceof Double) { + return TYPE_DOUBLE; + } else if (obj instanceof Float) { + return TYPE_FLOAT; + } else if (obj instanceof Boolean) { + return TYPE_BOOLEAN; + } else if (obj instanceof UUID) { + return TYPE_UUID; + } else if (obj instanceof Byte) { + return TYPE_BYTE; + } else if (obj instanceof Short) { + return TYPE_SHORT; + } else if (obj instanceof Character) { + return TYPE_CHAR; + } else if (obj == null) { + return TYPE_NULL; + } else if (isDate(obj)) { + return TYPE_DATE; + } else if (isBigInteger(obj)) { + return TYPE_BIG_INTEGER; + } else if (isBigDecimal(obj)) { + return TYPE_BIG_DECIMAL; + } else if (obj.getClass().isArray()) { + return TYPE_ARRAY; + } + return TYPE_SERIALIZED_OBJECT; + } + + /** + * Switch the last remembered type to match the type of the given object. + * + * @param obj the object + * @return the auto-detected type used + */ + AutoDetectDataType switchType(Object obj) { + int typeId = getTypeId(obj); + AutoDetectDataType l = last; + if (typeId != l.typeId) { + last = l = newType(typeId); + } + return l; + } + + /** + * Check whether this object is a BigInteger. + * + * @param obj the object + * @return true if yes + */ + static boolean isBigInteger(Object obj) { + return obj != null && obj.getClass() == BigInteger.class; + } + + /** + * Check whether this object is a BigDecimal. + * + * @param obj the object + * @return true if yes + */ + static boolean isBigDecimal(Object obj) { + return obj != null && obj.getClass() == BigDecimal.class; + } + + /** + * Check whether this object is a date. + * + * @param obj the object + * @return true if yes + */ + static boolean isDate(Object obj) { + return obj != null && obj.getClass() == Date.class; + } + + /** + * Check whether this object is an array. + * + * @param obj the object + * @return true if yes + */ + static boolean isArray(Object obj) { + return obj != null && obj.getClass().isArray(); + } + + /** + * Get the class id, or null if not found. + * + * @param clazz the class + * @return the class id or null + */ + static Integer getCommonClassId(Class clazz) { + HashMap, Integer> map = COMMON_CLASSES_MAP; + if (map.size() == 0) { + // lazy initialization + // synchronized, because the COMMON_CLASSES_MAP is not + synchronized (map) { + if (map.size() == 0) { + for (int i = 0, size = COMMON_CLASSES.length; i < size; i++) { + map.put(COMMON_CLASSES[i], i); + } + } + } + } + return map.get(clazz); + } + + /** + * Serialize the object to a byte array. + * + * @param obj the object to serialize + * @return the byte array + */ + public static byte[] serialize(Object obj) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream os = new ObjectOutputStream(out); + os.writeObject(obj); + return out.toByteArray(); + } catch (Throwable e) { + throw DataUtils.newIllegalArgumentException( + "Could not serialize {0}", obj, e); + } + } + + /** + * De-serialize the byte array to an object. + * + * @param data the byte array + * @return the object + */ + public static Object deserialize(byte[] data) { + try { + ByteArrayInputStream in = new ByteArrayInputStream(data); + ObjectInputStream is = new ObjectInputStream(in); + return is.readObject(); + } catch (Throwable e) { + throw DataUtils.newIllegalArgumentException( + "Could not deserialize {0}", Arrays.toString(data), e); + } + } + + /** + * Compare the contents of two byte arrays. If the content or length of the + * first array is smaller than the second array, -1 is returned. If the + * content or length of the second array is smaller than the first array, 1 + * is returned. If the contents and lengths are the same, 0 is returned. + *

+ * This method interprets bytes as unsigned. + * + * @param data1 the first byte array (must not be null) + * @param data2 the second byte array (must not be null) + * @return the result of the comparison (-1, 1 or 0) + */ + public static int compareNotNull(byte[] data1, byte[] data2) { + if (data1 == data2) { + return 0; + } + int len = Math.min(data1.length, data2.length); + for (int i = 0; i < len; i++) { + int b = data1[i] & 255; + int b2 = data2[i] & 255; + if (b != b2) { + return b > b2 ? 1 : -1; + } + } + return Integer.signum(data1.length - data2.length); + } + + /** + * The base class for auto-detect data types. + */ + abstract static class AutoDetectDataType implements DataType { + + protected final ObjectDataType base; + protected final int typeId; + + AutoDetectDataType(ObjectDataType base, int typeId) { + this.base = base; + this.typeId = typeId; + } + + @Override + public int getMemory(Object o) { + return getType(o).getMemory(o); + } + + @Override + public int compare(Object aObj, Object bObj) { + AutoDetectDataType aType = getType(aObj); + AutoDetectDataType bType = getType(bObj); + int typeDiff = aType.typeId - bType.typeId; + if (typeDiff == 0) { + return aType.compare(aObj, bObj); + } + return Integer.signum(typeDiff); + } + + @Override + public void write(WriteBuffer buff, Object[] obj, + int len, boolean key) { + for (int i = 0; i < len; i++) { + write(buff, obj[i]); + } + } + + @Override + public void write(WriteBuffer buff, Object o) { + getType(o).write(buff, o); + } + + @Override + public void read(ByteBuffer buff, Object[] obj, + int len, boolean key) { + for (int i = 0; i < len; i++) { + obj[i] = read(buff); + } + } + + @Override + public final Object read(ByteBuffer buff) { + throw DataUtils.newIllegalStateException(DataUtils.ERROR_INTERNAL, + "Internal error"); + } + + /** + * Get the type for the given object. + * + * @param o the object + * @return the type + */ + AutoDetectDataType getType(Object o) { + return base.switchType(o); + } + + /** + * Read an object from the buffer. + * + * @param buff the buffer + * @param tag the first byte of the object (usually the type) + * @return the read object + */ + abstract Object read(ByteBuffer buff, int tag); + + } + + /** + * The type for the null value + */ + static class NullType extends AutoDetectDataType { + + NullType(ObjectDataType base) { + super(base, TYPE_NULL); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj == null && bObj == null) { + return 0; + } else if (aObj == null) { + return -1; + } else if (bObj == null) { + return 1; + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj == null ? 0 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (obj != null) { + super.write(buff, obj); + return; + } + buff.put((byte) TYPE_NULL); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + return null; + } + + } + + /** + * The type for boolean true and false. + */ + static class BooleanType extends AutoDetectDataType { + + BooleanType(ObjectDataType base) { + super(base, TYPE_BOOLEAN); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof Boolean && bObj instanceof Boolean) { + Boolean a = (Boolean) aObj; + Boolean b = (Boolean) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof Boolean ? 0 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof Boolean)) { + super.write(buff, obj); + return; + } + int tag = ((Boolean) obj) ? TAG_BOOLEAN_TRUE : TYPE_BOOLEAN; + buff.put((byte) tag); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + return tag == TYPE_BOOLEAN ? Boolean.FALSE : Boolean.TRUE; + } + + } + + /** + * The type for byte objects. + */ + static class ByteType extends AutoDetectDataType { + + ByteType(ObjectDataType base) { + super(base, TYPE_BYTE); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof Byte && bObj instanceof Byte) { + Byte a = (Byte) aObj; + Byte b = (Byte) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof Byte ? 0 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof Byte)) { + super.write(buff, obj); + return; + } + buff.put((byte) TYPE_BYTE); + buff.put((Byte) obj); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + return buff.get(); + } + + } + + /** + * The type for character objects. + */ + static class CharacterType extends AutoDetectDataType { + + CharacterType(ObjectDataType base) { + super(base, TYPE_CHAR); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof Character && bObj instanceof Character) { + Character a = (Character) aObj; + Character b = (Character) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof Character ? 24 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof Character)) { + super.write(buff, obj); + return; + } + buff.put((byte) TYPE_CHAR); + buff.putChar((Character) obj); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + return buff.getChar(); + } + + } + + /** + * The type for short objects. + */ + static class ShortType extends AutoDetectDataType { + + ShortType(ObjectDataType base) { + super(base, TYPE_SHORT); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof Short && bObj instanceof Short) { + Short a = (Short) aObj; + Short b = (Short) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof Short ? 24 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof Short)) { + super.write(buff, obj); + return; + } + buff.put((byte) TYPE_SHORT); + buff.putShort((Short) obj); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + return buff.getShort(); + } + + } + + /** + * The type for integer objects. + */ + static class IntegerType extends AutoDetectDataType { + + IntegerType(ObjectDataType base) { + super(base, TYPE_INT); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof Integer && bObj instanceof Integer) { + Integer a = (Integer) aObj; + Integer b = (Integer) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof Integer ? 24 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof Integer)) { + super.write(buff, obj); + return; + } + int x = (Integer) obj; + if (x < 0) { + // -Integer.MIN_VALUE is smaller than 0 + if (-x < 0 || -x > DataUtils.COMPRESSED_VAR_INT_MAX) { + buff.put((byte) TAG_INTEGER_FIXED).putInt(x); + } else { + buff.put((byte) TAG_INTEGER_NEGATIVE).putVarInt(-x); + } + } else if (x <= 15) { + buff.put((byte) (TAG_INTEGER_0_15 + x)); + } else if (x <= DataUtils.COMPRESSED_VAR_INT_MAX) { + buff.put((byte) TYPE_INT).putVarInt(x); + } else { + buff.put((byte) TAG_INTEGER_FIXED).putInt(x); + } + } + + @Override + public Object read(ByteBuffer buff, int tag) { + switch (tag) { + case TYPE_INT: + return DataUtils.readVarInt(buff); + case TAG_INTEGER_NEGATIVE: + return -DataUtils.readVarInt(buff); + case TAG_INTEGER_FIXED: + return buff.getInt(); + } + return tag - TAG_INTEGER_0_15; + } + + } + + /** + * The type for long objects. + */ + static class LongType extends AutoDetectDataType { + + LongType(ObjectDataType base) { + super(base, TYPE_LONG); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof Long && bObj instanceof Long) { + Long a = (Long) aObj; + Long b = (Long) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof Long ? 30 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof Long)) { + super.write(buff, obj); + return; + } + long x = (Long) obj; + if (x < 0) { + // -Long.MIN_VALUE is smaller than 0 + if (-x < 0 || -x > DataUtils.COMPRESSED_VAR_LONG_MAX) { + buff.put((byte) TAG_LONG_FIXED); + buff.putLong(x); + } else { + buff.put((byte) TAG_LONG_NEGATIVE); + buff.putVarLong(-x); + } + } else if (x <= 7) { + buff.put((byte) (TAG_LONG_0_7 + x)); + } else if (x <= DataUtils.COMPRESSED_VAR_LONG_MAX) { + buff.put((byte) TYPE_LONG); + buff.putVarLong(x); + } else { + buff.put((byte) TAG_LONG_FIXED); + buff.putLong(x); + } + } + + @Override + public Object read(ByteBuffer buff, int tag) { + switch (tag) { + case TYPE_LONG: + return DataUtils.readVarLong(buff); + case TAG_LONG_NEGATIVE: + return -DataUtils.readVarLong(buff); + case TAG_LONG_FIXED: + return buff.getLong(); + } + return (long) (tag - TAG_LONG_0_7); + } + + } + + /** + * The type for float objects. + */ + static class FloatType extends AutoDetectDataType { + + FloatType(ObjectDataType base) { + super(base, TYPE_FLOAT); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof Float && bObj instanceof Float) { + Float a = (Float) aObj; + Float b = (Float) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof Float ? 24 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof Float)) { + super.write(buff, obj); + return; + } + float x = (Float) obj; + int f = Float.floatToIntBits(x); + if (f == ObjectDataType.FLOAT_ZERO_BITS) { + buff.put((byte) TAG_FLOAT_0); + } else if (f == ObjectDataType.FLOAT_ONE_BITS) { + buff.put((byte) TAG_FLOAT_1); + } else { + int value = Integer.reverse(f); + if (value >= 0 && value <= DataUtils.COMPRESSED_VAR_INT_MAX) { + buff.put((byte) TYPE_FLOAT).putVarInt(value); + } else { + buff.put((byte) TAG_FLOAT_FIXED).putFloat(x); + } + } + } + + @Override + public Object read(ByteBuffer buff, int tag) { + switch (tag) { + case TAG_FLOAT_0: + return 0f; + case TAG_FLOAT_1: + return 1f; + case TAG_FLOAT_FIXED: + return buff.getFloat(); + } + return Float.intBitsToFloat(Integer.reverse(DataUtils + .readVarInt(buff))); + } + + } + + /** + * The type for double objects. + */ + static class DoubleType extends AutoDetectDataType { + + DoubleType(ObjectDataType base) { + super(base, TYPE_DOUBLE); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof Double && bObj instanceof Double) { + Double a = (Double) aObj; + Double b = (Double) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof Double ? 30 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof Double)) { + super.write(buff, obj); + return; + } + double x = (Double) obj; + long d = Double.doubleToLongBits(x); + if (d == ObjectDataType.DOUBLE_ZERO_BITS) { + buff.put((byte) TAG_DOUBLE_0); + } else if (d == ObjectDataType.DOUBLE_ONE_BITS) { + buff.put((byte) TAG_DOUBLE_1); + } else { + long value = Long.reverse(d); + if (value >= 0 && value <= DataUtils.COMPRESSED_VAR_LONG_MAX) { + buff.put((byte) TYPE_DOUBLE); + buff.putVarLong(value); + } else { + buff.put((byte) TAG_DOUBLE_FIXED); + buff.putDouble(x); + } + } + } + + @Override + public Object read(ByteBuffer buff, int tag) { + switch (tag) { + case TAG_DOUBLE_0: + return 0d; + case TAG_DOUBLE_1: + return 1d; + case TAG_DOUBLE_FIXED: + return buff.getDouble(); + } + return Double.longBitsToDouble(Long.reverse(DataUtils + .readVarLong(buff))); + } + + } + + /** + * The type for BigInteger objects. + */ + static class BigIntegerType extends AutoDetectDataType { + + BigIntegerType(ObjectDataType base) { + super(base, TYPE_BIG_INTEGER); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (isBigInteger(aObj) && isBigInteger(bObj)) { + BigInteger a = (BigInteger) aObj; + BigInteger b = (BigInteger) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return isBigInteger(obj) ? 100 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!isBigInteger(obj)) { + super.write(buff, obj); + return; + } + BigInteger x = (BigInteger) obj; + if (BigInteger.ZERO.equals(x)) { + buff.put((byte) TAG_BIG_INTEGER_0); + } else if (BigInteger.ONE.equals(x)) { + buff.put((byte) TAG_BIG_INTEGER_1); + } else { + int bits = x.bitLength(); + if (bits <= 63) { + buff.put((byte) TAG_BIG_INTEGER_SMALL).putVarLong( + x.longValue()); + } else { + byte[] bytes = x.toByteArray(); + buff.put((byte) TYPE_BIG_INTEGER).putVarInt(bytes.length) + .put(bytes); + } + } + } + + @Override + public Object read(ByteBuffer buff, int tag) { + switch (tag) { + case TAG_BIG_INTEGER_0: + return BigInteger.ZERO; + case TAG_BIG_INTEGER_1: + return BigInteger.ONE; + case TAG_BIG_INTEGER_SMALL: + return BigInteger.valueOf(DataUtils.readVarLong(buff)); + } + int len = DataUtils.readVarInt(buff); + byte[] bytes = Utils.newBytes(len); + buff.get(bytes); + return new BigInteger(bytes); + } + + } + + /** + * The type for BigDecimal objects. + */ + static class BigDecimalType extends AutoDetectDataType { + + BigDecimalType(ObjectDataType base) { + super(base, TYPE_BIG_DECIMAL); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (isBigDecimal(aObj) && isBigDecimal(bObj)) { + BigDecimal a = (BigDecimal) aObj; + BigDecimal b = (BigDecimal) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public int getMemory(Object obj) { + return isBigDecimal(obj) ? 150 : super.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!isBigDecimal(obj)) { + super.write(buff, obj); + return; + } + BigDecimal x = (BigDecimal) obj; + if (BigDecimal.ZERO.equals(x)) { + buff.put((byte) TAG_BIG_DECIMAL_0); + } else if (BigDecimal.ONE.equals(x)) { + buff.put((byte) TAG_BIG_DECIMAL_1); + } else { + int scale = x.scale(); + BigInteger b = x.unscaledValue(); + int bits = b.bitLength(); + if (bits < 64) { + if (scale == 0) { + buff.put((byte) TAG_BIG_DECIMAL_SMALL); + } else { + buff.put((byte) TAG_BIG_DECIMAL_SMALL_SCALED) + .putVarInt(scale); + } + buff.putVarLong(b.longValue()); + } else { + byte[] bytes = b.toByteArray(); + buff.put((byte) TYPE_BIG_DECIMAL).putVarInt(scale) + .putVarInt(bytes.length).put(bytes); + } + } + } + + @Override + public Object read(ByteBuffer buff, int tag) { + switch (tag) { + case TAG_BIG_DECIMAL_0: + return BigDecimal.ZERO; + case TAG_BIG_DECIMAL_1: + return BigDecimal.ONE; + case TAG_BIG_DECIMAL_SMALL: + return BigDecimal.valueOf(DataUtils.readVarLong(buff)); + case TAG_BIG_DECIMAL_SMALL_SCALED: + int scale = DataUtils.readVarInt(buff); + return BigDecimal.valueOf(DataUtils.readVarLong(buff), scale); + } + int scale = DataUtils.readVarInt(buff); + int len = DataUtils.readVarInt(buff); + byte[] bytes = Utils.newBytes(len); + buff.get(bytes); + BigInteger b = new BigInteger(bytes); + return new BigDecimal(b, scale); + } + + } + + /** + * The type for string objects. + */ + static class StringType extends AutoDetectDataType { + + StringType(ObjectDataType base) { + super(base, TYPE_STRING); + } + + @Override + public int getMemory(Object obj) { + if (!(obj instanceof String)) { + return super.getMemory(obj); + } + return 24 + 2 * obj.toString().length(); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof String && bObj instanceof String) { + return aObj.toString().compareTo(bObj.toString()); + } + return super.compare(aObj, bObj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof String)) { + super.write(buff, obj); + return; + } + String s = (String) obj; + int len = s.length(); + if (len <= 15) { + buff.put((byte) (TAG_STRING_0_15 + len)); + } else { + buff.put((byte) TYPE_STRING).putVarInt(len); + } + buff.putStringData(s, len); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + int len; + if (tag == TYPE_STRING) { + len = DataUtils.readVarInt(buff); + } else { + len = tag - TAG_STRING_0_15; + } + return DataUtils.readString(buff, len); + } + + } + + /** + * The type for UUID objects. + */ + static class UUIDType extends AutoDetectDataType { + + UUIDType(ObjectDataType base) { + super(base, TYPE_UUID); + } + + @Override + public int getMemory(Object obj) { + return obj instanceof UUID ? 40 : super.getMemory(obj); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (aObj instanceof UUID && bObj instanceof UUID) { + UUID a = (UUID) aObj; + UUID b = (UUID) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!(obj instanceof UUID)) { + super.write(buff, obj); + return; + } + buff.put((byte) TYPE_UUID); + UUID a = (UUID) obj; + buff.putLong(a.getMostSignificantBits()); + buff.putLong(a.getLeastSignificantBits()); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + long a = buff.getLong(), b = buff.getLong(); + return new UUID(a, b); + } + + } + + /** + * The type for java.util.Date objects. + */ + static class DateType extends AutoDetectDataType { + + DateType(ObjectDataType base) { + super(base, TYPE_DATE); + } + + @Override + public int getMemory(Object obj) { + return isDate(obj) ? 40 : super.getMemory(obj); + } + + @Override + public int compare(Object aObj, Object bObj) { + if (isDate(aObj) && isDate(bObj)) { + Date a = (Date) aObj; + Date b = (Date) bObj; + return a.compareTo(b); + } + return super.compare(aObj, bObj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!isDate(obj)) { + super.write(buff, obj); + return; + } + buff.put((byte) TYPE_DATE); + Date a = (Date) obj; + buff.putLong(a.getTime()); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + long a = buff.getLong(); + return new Date(a); + } + + } + + /** + * The type for object arrays. + */ + static class ObjectArrayType extends AutoDetectDataType { + + private final ObjectDataType elementType = new ObjectDataType(); + + ObjectArrayType(ObjectDataType base) { + super(base, TYPE_ARRAY); + } + + @Override + public int getMemory(Object obj) { + if (!isArray(obj)) { + return super.getMemory(obj); + } + int size = 64; + Class type = obj.getClass().getComponentType(); + if (type.isPrimitive()) { + int len = Array.getLength(obj); + if (type == boolean.class || type == byte.class) { + size += len; + } else if (type == char.class || type == short.class) { + size += len * 2; + } else if (type == int.class || type == float.class) { + size += len * 4; + } else if (type == double.class || type == long.class) { + size += len * 8; + } + } else { + for (Object x : (Object[]) obj) { + if (x != null) { + size += elementType.getMemory(x); + } + } + } + // we say they are larger, because these objects + // use quite a lot of disk space + return size * 2; + } + + @Override + public int compare(Object aObj, Object bObj) { + if (!isArray(aObj) || !isArray(bObj)) { + return super.compare(aObj, bObj); + } + if (aObj == bObj) { + return 0; + } + Class type = aObj.getClass().getComponentType(); + Class bType = bObj.getClass().getComponentType(); + if (type != bType) { + Integer classA = getCommonClassId(type); + Integer classB = getCommonClassId(bType); + if (classA != null) { + if (classB != null) { + return classA.compareTo(classB); + } + return -1; + } else if (classB != null) { + return 1; + } + return type.getName().compareTo(bType.getName()); + } + int aLen = Array.getLength(aObj); + int bLen = Array.getLength(bObj); + int len = Math.min(aLen, bLen); + if (type.isPrimitive()) { + if (type == byte.class) { + byte[] a = (byte[]) aObj; + byte[] b = (byte[]) bObj; + return compareNotNull(a, b); + } + for (int i = 0; i < len; i++) { + int x; + if (type == boolean.class) { + x = Integer.signum((((boolean[]) aObj)[i] ? 1 : 0) + - (((boolean[]) bObj)[i] ? 1 : 0)); + } else if (type == char.class) { + x = Integer.signum((((char[]) aObj)[i]) + - (((char[]) bObj)[i])); + } else if (type == short.class) { + x = Integer.signum((((short[]) aObj)[i]) + - (((short[]) bObj)[i])); + } else if (type == int.class) { + int a = ((int[]) aObj)[i]; + int b = ((int[]) bObj)[i]; + x = Integer.compare(a, b); + } else if (type == float.class) { + x = Float.compare(((float[]) aObj)[i], + ((float[]) bObj)[i]); + } else if (type == double.class) { + x = Double.compare(((double[]) aObj)[i], + ((double[]) bObj)[i]); + } else { + long a = ((long[]) aObj)[i]; + long b = ((long[]) bObj)[i]; + x = Long.compare(a, b); + } + if (x != 0) { + return x; + } + } + } else { + Object[] a = (Object[]) aObj; + Object[] b = (Object[]) bObj; + for (int i = 0; i < len; i++) { + int comp = elementType.compare(a[i], b[i]); + if (comp != 0) { + return comp; + } + } + } + return Integer.compare(aLen, bLen); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + if (!isArray(obj)) { + super.write(buff, obj); + return; + } + Class type = obj.getClass().getComponentType(); + Integer classId = getCommonClassId(type); + if (classId != null) { + if (type.isPrimitive()) { + if (type == byte.class) { + byte[] data = (byte[]) obj; + int len = data.length; + if (len <= 15) { + buff.put((byte) (TAG_BYTE_ARRAY_0_15 + len)); + } else { + buff.put((byte) TYPE_ARRAY) + .put((byte) classId.intValue()) + .putVarInt(len); + } + buff.put(data); + return; + } + int len = Array.getLength(obj); + buff.put((byte) TYPE_ARRAY).put((byte) classId.intValue()) + .putVarInt(len); + for (int i = 0; i < len; i++) { + if (type == boolean.class) { + buff.put((byte) (((boolean[]) obj)[i] ? 1 : 0)); + } else if (type == char.class) { + buff.putChar(((char[]) obj)[i]); + } else if (type == short.class) { + buff.putShort(((short[]) obj)[i]); + } else if (type == int.class) { + buff.putInt(((int[]) obj)[i]); + } else if (type == float.class) { + buff.putFloat(((float[]) obj)[i]); + } else if (type == double.class) { + buff.putDouble(((double[]) obj)[i]); + } else { + buff.putLong(((long[]) obj)[i]); + } + } + return; + } + buff.put((byte) TYPE_ARRAY).put((byte) classId.intValue()); + } else { + buff.put((byte) TYPE_ARRAY).put((byte) -1); + String c = type.getName(); + StringDataType.INSTANCE.write(buff, c); + } + Object[] array = (Object[]) obj; + int len = array.length; + buff.putVarInt(len); + for (Object x : array) { + elementType.write(buff, x); + } + } + + @Override + public Object read(ByteBuffer buff, int tag) { + if (tag != TYPE_ARRAY) { + byte[] data; + int len = tag - TAG_BYTE_ARRAY_0_15; + data = Utils.newBytes(len); + buff.get(data); + return data; + } + int ct = buff.get(); + Class clazz; + Object obj; + if (ct == -1) { + String componentType = StringDataType.INSTANCE.read(buff); + try { + clazz = Class.forName(componentType); + } catch (Exception e) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_SERIALIZATION, + "Could not get class {0}", componentType, e); + } + } else { + clazz = COMMON_CLASSES[ct]; + } + int len = DataUtils.readVarInt(buff); + try { + obj = Array.newInstance(clazz, len); + } catch (Exception e) { + throw DataUtils.newIllegalStateException( + DataUtils.ERROR_SERIALIZATION, + "Could not create array of type {0} length {1}", clazz, + len, e); + } + if (clazz.isPrimitive()) { + for (int i = 0; i < len; i++) { + if (clazz == boolean.class) { + ((boolean[]) obj)[i] = buff.get() == 1; + } else if (clazz == byte.class) { + ((byte[]) obj)[i] = buff.get(); + } else if (clazz == char.class) { + ((char[]) obj)[i] = buff.getChar(); + } else if (clazz == short.class) { + ((short[]) obj)[i] = buff.getShort(); + } else if (clazz == int.class) { + ((int[]) obj)[i] = buff.getInt(); + } else if (clazz == float.class) { + ((float[]) obj)[i] = buff.getFloat(); + } else if (clazz == double.class) { + ((double[]) obj)[i] = buff.getDouble(); + } else { + ((long[]) obj)[i] = buff.getLong(); + } + } + } else { + Object[] array = (Object[]) obj; + for (int i = 0; i < len; i++) { + array[i] = elementType.read(buff); + } + } + return obj; + } + + } + + /** + * The type for serialized objects. + */ + static class SerializedObjectType extends AutoDetectDataType { + + private int averageSize = 10_000; + + SerializedObjectType(ObjectDataType base) { + super(base, TYPE_SERIALIZED_OBJECT); + } + + @SuppressWarnings("unchecked") + @Override + public int compare(Object aObj, Object bObj) { + if (aObj == bObj) { + return 0; + } + DataType ta = getType(aObj); + DataType tb = getType(bObj); + if (ta != this || tb != this) { + if (ta == tb) { + return ta.compare(aObj, bObj); + } + return super.compare(aObj, bObj); + } + // TODO ensure comparable type (both may be comparable but not + // with each other) + if (aObj instanceof Comparable) { + if (aObj.getClass().isAssignableFrom(bObj.getClass())) { + return ((Comparable) aObj).compareTo(bObj); + } + } + if (bObj instanceof Comparable) { + if (bObj.getClass().isAssignableFrom(aObj.getClass())) { + return -((Comparable) bObj).compareTo(aObj); + } + } + byte[] a = serialize(aObj); + byte[] b = serialize(bObj); + return compareNotNull(a, b); + } + + @Override + public int getMemory(Object obj) { + DataType t = getType(obj); + if (t == this) { + return averageSize; + } + return t.getMemory(obj); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + DataType t = getType(obj); + if (t != this) { + t.write(buff, obj); + return; + } + byte[] data = serialize(obj); + // we say they are larger, because these objects + // use quite a lot of disk space + int size = data.length * 2; + // adjust the average size + // using an exponential moving average + averageSize = (size + 15 * averageSize) / 16; + buff.put((byte) TYPE_SERIALIZED_OBJECT).putVarInt(data.length) + .put(data); + } + + @Override + public Object read(ByteBuffer buff, int tag) { + int len = DataUtils.readVarInt(buff); + byte[] data = Utils.newBytes(len); + int size = data.length * 2; + // adjust the average size + // using an exponential moving average + averageSize = (size + 15 * averageSize) / 16; + buff.get(data); + return deserialize(data); + } + } + +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/StringDataType.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/StringDataType.java new file mode 100644 index 000000000..4b954809d --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/type/StringDataType.java @@ -0,0 +1,57 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.type; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.DataUtils; +import org.dizitart.no2.mvstore.compat.v1.mvstore.WriteBuffer; + +import java.nio.ByteBuffer; + +/** + * A string type. + */ +public class StringDataType implements DataType { + + public static final StringDataType INSTANCE = new StringDataType(); + + @Override + public int compare(Object a, Object b) { + return a.toString().compareTo(b.toString()); + } + + @Override + public int getMemory(Object obj) { + return 24 + 2 * obj.toString().length(); + } + + @Override + public void read(ByteBuffer buff, Object[] obj, int len, boolean key) { + for (int i = 0; i < len; i++) { + obj[i] = read(buff); + } + } + + @Override + public void write(WriteBuffer buff, Object[] obj, int len, boolean key) { + for (int i = 0; i < len; i++) { + write(buff, obj[i]); + } + } + + @Override + public String read(ByteBuffer buff) { + return DataUtils.readString(buff); + } + + @Override + public void write(WriteBuffer buff, Object obj) { + String s = obj.toString(); + int len = s.length(); + buff.putVarInt(len).putStringData(s, len); + } + +} + diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/Bits.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/Bits.java new file mode 100644 index 000000000..e0e5fc38d --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/Bits.java @@ -0,0 +1,138 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ +package org.dizitart.no2.mvstore.compat.v1.mvstore.util; + +import java.util.UUID; + +public final class Bits { + public static int compareNotNull(char[] var0, char[] var1) { + if (var0 == var1) { + return 0; + } else { + int var2 = Math.min(var0.length, var1.length); + + for(int var3 = 0; var3 < var2; ++var3) { + char var4 = var0[var3]; + char var5 = var1[var3]; + if (var4 != var5) { + return var4 > var5 ? 1 : -1; + } + } + + return Integer.signum(var0.length - var1.length); + } + } + + public static int compareNotNullSigned(byte[] var0, byte[] var1) { + if (var0 == var1) { + return 0; + } else { + int var2 = Math.min(var0.length, var1.length); + + for(int var3 = 0; var3 < var2; ++var3) { + byte var4 = var0[var3]; + byte var5 = var1[var3]; + if (var4 != var5) { + return var4 > var5 ? 1 : -1; + } + } + + return Integer.signum(var0.length - var1.length); + } + } + + public static int compareNotNullUnsigned(byte[] var0, byte[] var1) { + if (var0 == var1) { + return 0; + } else { + int var2 = Math.min(var0.length, var1.length); + + for(int var3 = 0; var3 < var2; ++var3) { + int var4 = var0[var3] & 255; + int var5 = var1[var3] & 255; + if (var4 != var5) { + return var4 > var5 ? 1 : -1; + } + } + + return Integer.signum(var0.length - var1.length); + } + } + + public static int readInt(byte[] var0, int var1) { + return (var0[var1++] << 24) + ((var0[var1++] & 255) << 16) + ((var0[var1++] & 255) << 8) + (var0[var1] & 255); + } + + public static int readIntLE(byte[] var0, int var1) { + return (var0[var1++] & 255) + ((var0[var1++] & 255) << 8) + ((var0[var1++] & 255) << 16) + (var0[var1] << 24); + } + + public static long readLong(byte[] var0, int var1) { + return ((long)readInt(var0, var1) << 32) + ((long)readInt(var0, var1 + 4) & 4294967295L); + } + + public static long readLongLE(byte[] var0, int var1) { + return ((long)readIntLE(var0, var1) & 4294967295L) + ((long)readIntLE(var0, var1 + 4) << 32); + } + + public static double readDouble(byte[] var0, int var1) { + return Double.longBitsToDouble(readLong(var0, var1)); + } + + public static double readDoubleLE(byte[] var0, int var1) { + return Double.longBitsToDouble(readLongLE(var0, var1)); + } + + public static byte[] uuidToBytes(long var0, long var2) { + byte[] var4 = new byte[16]; + + for(int var5 = 0; var5 < 8; ++var5) { + var4[var5] = (byte)((int)(var0 >> 8 * (7 - var5) & 255L)); + var4[8 + var5] = (byte)((int)(var2 >> 8 * (7 - var5) & 255L)); + } + + return var4; + } + + public static byte[] uuidToBytes(UUID var0) { + return uuidToBytes(var0.getMostSignificantBits(), var0.getLeastSignificantBits()); + } + + public static void writeInt(byte[] var0, int var1, int var2) { + var0[var1++] = (byte)(var2 >> 24); + var0[var1++] = (byte)(var2 >> 16); + var0[var1++] = (byte)(var2 >> 8); + var0[var1] = (byte)var2; + } + + public static void writeIntLE(byte[] var0, int var1, int var2) { + var0[var1++] = (byte)var2; + var0[var1++] = (byte)(var2 >> 8); + var0[var1++] = (byte)(var2 >> 16); + var0[var1] = (byte)(var2 >> 24); + } + + public static void writeLong(byte[] var0, int var1, long var2) { + writeInt(var0, var1, (int)(var2 >> 32)); + writeInt(var0, var1 + 4, (int)var2); + } + + public static void writeLongLE(byte[] var0, int var1, long var2) { + writeIntLE(var0, var1, (int)var2); + writeIntLE(var0, var1 + 4, (int)(var2 >> 32)); + } + + public static void writeDouble(byte[] var0, int var1, double var2) { + writeLong(var0, var1, Double.doubleToRawLongBits(var2)); + } + + public static void writeDoubleLE(byte[] var0, int var1, double var2) { + writeLongLE(var0, var1, Double.doubleToRawLongBits(var2)); + } + + private Bits() { + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/IOUtils.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/IOUtils.java new file mode 100644 index 000000000..876fa8fa7 --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/IOUtils.java @@ -0,0 +1,273 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.util; + +import org.dizitart.no2.mvstore.compat.v1.mvstore.SysProperties; +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FileUtils; +import org.h2.jdbc.JdbcException; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +public class IOUtils { + private IOUtils() { + } + + public static void closeSilently(AutoCloseable var0) { + if (var0 != null) { + try { + trace("closeSilently", (String)null, var0); + var0.close(); + } catch (Exception var2) { + } + } + + } + + public static void skipFully(InputStream var0, long var1) throws IOException { + try { + while(var1 > 0L) { + long var3 = var0.skip(var1); + if (var3 <= 0L) { + throw new EOFException(); + } + + var1 -= var3; + } + + } catch (Exception var5) { + throw convertToIOException(var5); + } + } + + public static void skipFully(Reader var0, long var1) throws IOException { + try { + while(var1 > 0L) { + long var3 = var0.skip(var1); + if (var3 <= 0L) { + throw new EOFException(); + } + + var1 -= var3; + } + + } catch (Exception var5) { + throw convertToIOException(var5); + } + } + + public static long copyAndClose(InputStream var0, OutputStream var1) throws IOException { + long var4; + try { + long var2 = copyAndCloseInput(var0, var1); + var1.close(); + var4 = var2; + } catch (Exception var9) { + throw convertToIOException(var9); + } finally { + closeSilently(var1); + } + + return var4; + } + + public static long copyAndCloseInput(InputStream var0, OutputStream var1) throws IOException { + long var2; + try { + var2 = copy(var0, var1); + } catch (Exception var7) { + throw convertToIOException(var7); + } finally { + closeSilently(var0); + } + + return var2; + } + + public static long copy(InputStream var0, OutputStream var1) throws IOException { + return copy(var0, var1, Long.MAX_VALUE); + } + + public static long copy(InputStream var0, OutputStream var1, long var2) throws IOException { + try { + long var4 = 0L; + int var6 = (int)Math.min(var2, 4096L); + + for(byte[] var7 = new byte[var6]; var2 > 0L; var6 = (int)Math.min(var2, 4096L)) { + var6 = var0.read(var7, 0, var6); + if (var6 < 0) { + break; + } + + if (var1 != null) { + var1.write(var7, 0, var6); + } + + var4 += (long)var6; + var2 -= (long)var6; + } + + return var4; + } catch (Exception var8) { + throw convertToIOException(var8); + } + } + + public static long copyAndCloseInput(Reader var0, Writer var1, long var2) throws IOException { + try { + long var4 = 0L; + int var6 = (int)Math.min(var2, 4096L); + char[] var7 = new char[var6]; + + while(true) { + if (var2 > 0L) { + var6 = var0.read(var7, 0, var6); + if (var6 >= 0) { + if (var1 != null) { + var1.write(var7, 0, var6); + } + + var2 -= (long)var6; + var6 = (int)Math.min(var2, 4096L); + var4 += (long)var6; + continue; + } + } + + long var8 = var4; + return var8; + } + } catch (Exception var13) { + throw convertToIOException(var13); + } finally { + var0.close(); + } + } + + public static byte[] readBytesAndClose(InputStream var0, int var1) throws IOException { + byte[] var4; + try { + if (var1 <= 0) { + var1 = Integer.MAX_VALUE; + } + + int var2 = Math.min(4096, var1); + ByteArrayOutputStream var3 = new ByteArrayOutputStream(var2); + copy(var0, var3, (long)var1); + var4 = var3.toByteArray(); + } catch (Exception var8) { + throw convertToIOException(var8); + } finally { + var0.close(); + } + + return var4; + } + + public static String readStringAndClose(Reader var0, int var1) throws IOException { + String var4; + try { + if (var1 <= 0) { + var1 = Integer.MAX_VALUE; + } + + int var2 = Math.min(4096, var1); + StringWriter var3 = new StringWriter(var2); + copyAndCloseInput(var0, var3, (long)var1); + var4 = var3.toString(); + } finally { + var0.close(); + } + + return var4; + } + + public static int readFully(InputStream var0, byte[] var1, int var2) throws IOException { + try { + int var3 = 0; + + int var5; + for(int var4 = Math.min(var2, var1.length); var4 > 0; var4 -= var5) { + var5 = var0.read(var1, var3, var4); + if (var5 < 0) { + break; + } + + var3 += var5; + } + + return var3; + } catch (Exception var6) { + throw convertToIOException(var6); + } + } + + public static int readFully(Reader var0, char[] var1, int var2) throws IOException { + try { + int var3 = 0; + + int var5; + for(int var4 = Math.min(var2, var1.length); var4 > 0; var4 -= var5) { + var5 = var0.read(var1, var3, var4); + if (var5 < 0) { + break; + } + + var3 += var5; + } + + return var3; + } catch (Exception var6) { + throw convertToIOException(var6); + } + } + + public static Reader getBufferedReader(InputStream var0) { + return var0 == null ? null : new BufferedReader(new InputStreamReader(var0, StandardCharsets.UTF_8)); + } + + public static Reader getReader(InputStream var0) { + return var0 == null ? null : new BufferedReader(new InputStreamReader(var0, StandardCharsets.UTF_8)); + } + + public static Writer getBufferedWriter(OutputStream var0) { + return var0 == null ? null : new BufferedWriter(new OutputStreamWriter(var0, StandardCharsets.UTF_8)); + } + + public static Reader getAsciiReader(InputStream var0) { + return var0 == null ? null : new InputStreamReader(var0, StandardCharsets.US_ASCII); + } + + public static void trace(String var0, String var1, Object var2) { + if (SysProperties.TRACE_IO) { + System.out.println("IOUtils." + var0 + ' ' + var1 + ' ' + var2); + } + + } + + public static InputStream getInputStreamFromString(String var0) { + return var0 == null ? null : new ByteArrayInputStream(var0.getBytes(StandardCharsets.UTF_8)); + } + + public static void copyFiles(String var0, String var1) throws IOException { + InputStream var2 = FileUtils.newInputStream(var0); + OutputStream var3 = FileUtils.newOutputStream(var1, false); + copyAndClose(var2, var3); + } + + public static IOException convertToIOException(Throwable var0) { + if (var0 instanceof IOException) { + return (IOException)var0; + } else { + if (var0 instanceof JdbcException && var0.getCause() != null) { + var0 = var0.getCause(); + } + + return new IOException(var0.toString(), var0); + } + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/SortedProperties.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/SortedProperties.java new file mode 100644 index 000000000..5e59ac4ee --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/SortedProperties.java @@ -0,0 +1,164 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.util; + + +import org.dizitart.no2.mvstore.compat.v1.mvstore.fs.FileUtils; +import org.h2.util.Utils; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; + +public class SortedProperties extends Properties { + private static final long serialVersionUID = 1L; + + public SortedProperties() { + } + + public synchronized Enumeration keys() { + Vector var1 = new Vector(); + Iterator var2 = this.keySet().iterator(); + + while(var2.hasNext()) { + Object var3 = var2.next(); + var1.add(var3.toString()); + } + + Collections.sort(var1); + return (new Vector(var1)).elements(); + } + + public static boolean getBooleanProperty(Properties var0, String var1, boolean var2) { + try { + return Utils.parseBoolean(var0.getProperty(var1, (String)null), var2, true); + } catch (IllegalArgumentException var4) { + var4.printStackTrace(); + return var2; + } + } + + public static int getIntProperty(Properties var0, String var1, int var2) { + String var3 = var0.getProperty(var1, Integer.toString(var2)); + + try { + return Integer.decode(var3); + } catch (Exception var5) { + var5.printStackTrace(); + return var2; + } + } + + public static String getStringProperty(Properties var0, String var1, String var2) { + return var0.getProperty(var1, var2); + } + + public static synchronized SortedProperties loadProperties(String var0) throws IOException { + SortedProperties var1 = new SortedProperties(); + if (FileUtils.exists(var0)) { + InputStream var2 = FileUtils.newInputStream(var0); + Throwable var3 = null; + + try { + var1.load(var2); + } catch (Throwable var12) { + var3 = var12; + throw var12; + } finally { + if (var2 != null) { + if (var3 != null) { + try { + var2.close(); + } catch (Throwable var11) { + var3.addSuppressed(var11); + } + } else { + var2.close(); + } + } + + } + } + + return var1; + } + + public synchronized void store(String var1) throws IOException { + ByteArrayOutputStream var2 = new ByteArrayOutputStream(); + this.store(var2, (String)null); + ByteArrayInputStream var3 = new ByteArrayInputStream(var2.toByteArray()); + InputStreamReader var4 = new InputStreamReader(var3, StandardCharsets.ISO_8859_1); + LineNumberReader var5 = new LineNumberReader(var4); + + OutputStreamWriter var6; + try { + var6 = new OutputStreamWriter(FileUtils.newOutputStream(var1, false)); + } catch (Exception var18) { + throw new IOException(var18.toString(), var18); + } + + PrintWriter var7 = new PrintWriter(new BufferedWriter(var6)); + Throwable var8 = null; + + try { + while(true) { + String var9 = var5.readLine(); + if (var9 == null) { + return; + } + + if (!var9.startsWith("#")) { + var7.print(var9 + "\n"); + } + } + } catch (Throwable var19) { + var8 = var19; + throw var19; + } finally { + if (var7 != null) { + if (var8 != null) { + try { + var7.close(); + } catch (Throwable var17) { + var8.addSuppressed(var17); + } + } else { + var7.close(); + } + } + + } + } + + public synchronized String toLines() { + StringBuilder var1 = new StringBuilder(); + Iterator var2 = (new TreeMap(this)).entrySet().iterator(); + + while(var2.hasNext()) { + Map.Entry var3 = (Map.Entry)var2.next(); + var1.append(var3.getKey()).append('=').append(var3.getValue()).append('\n'); + } + + return var1.toString(); + } + + public static SortedProperties fromLines(String var0) { + SortedProperties var1 = new SortedProperties(); + String[] var2 = StringUtils.arraySplit(var0, '\n', true); + int var3 = var2.length; + + for(int var4 = 0; var4 < var3; ++var4) { + String var5 = var2[var4]; + int var6 = var5.indexOf(61); + if (var6 > 0) { + var1.put(var5.substring(0, var6), var5.substring(var6 + 1)); + } + } + + return var1; + } +} diff --git a/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/StringUtils.java b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/StringUtils.java new file mode 100644 index 000000000..5094ac40c --- /dev/null +++ b/nitrite-mvstore-adapter/src/main/java/org/dizitart/no2/mvstore/compat/v1/mvstore/util/StringUtils.java @@ -0,0 +1,912 @@ +/* + * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0, + * and the EPL 1.0 (https://h2database.com/html/license.html). + * Initial Developer: H2 Group + */ + +package org.dizitart.no2.mvstore.compat.v1.mvstore.util; + +import org.h2.engine.SysProperties; +import org.h2.message.DbException; +import org.h2.util.Utils; + +import java.io.ByteArrayOutputStream; +import java.lang.ref.SoftReference; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public class StringUtils { + private static SoftReference softCache; + private static long softCacheCreatedNs; + private static final char[] HEX = "0123456789abcdef".toCharArray(); + private static final int[] HEX_DECODE = new int[103]; + private static final int TO_UPPER_CACHE_LENGTH = 2048; + private static final int TO_UPPER_CACHE_MAX_ENTRY_LENGTH = 64; + private static final String[][] TO_UPPER_CACHE = new String[2048][]; + + private StringUtils() { + } + + private static String[] getCache() { + String[] var0; + if (softCache != null) { + var0 = (String[])softCache.get(); + if (var0 != null) { + return var0; + } + } + + long var1 = System.nanoTime(); + if (softCacheCreatedNs != 0L && var1 - softCacheCreatedNs < TimeUnit.SECONDS.toNanos(5L)) { + return null; + } else { + String[] var3; + try { + var0 = new String[SysProperties.OBJECT_CACHE_SIZE]; + softCache = new SoftReference(var0); + var3 = var0; + } finally { + softCacheCreatedNs = System.nanoTime(); + } + + return var3; + } + } + + public static String toUpperEnglish(String var0) { + if (var0.length() > 64) { + return var0.toUpperCase(Locale.ENGLISH); + } else { + int var1 = var0.hashCode() & 2047; + String[] var2 = TO_UPPER_CACHE[var1]; + if (var2 != null && var2[0].equals(var0)) { + return var2[1]; + } else { + String var3 = var0.toUpperCase(Locale.ENGLISH); + var2 = new String[]{var0, var3}; + TO_UPPER_CACHE[var1] = var2; + return var3; + } + } + } + + public static String toLowerEnglish(String var0) { + return var0.toLowerCase(Locale.ENGLISH); + } + + public static String quoteStringSQL(String var0) { + return var0 == null ? "NULL" : quoteStringSQL(new StringBuilder(var0.length() + 2), var0).toString(); + } + + public static StringBuilder quoteStringSQL(StringBuilder var0, String var1) { + if (var1 == null) { + return var0.append("NULL"); + } else { + int var2 = var0.length(); + int var3 = var1.length(); + var0.append('\''); + + for(int var4 = 0; var4 < var3; ++var4) { + char var5 = var1.charAt(var4); + if (var5 == '\'') { + var0.append(var5); + } else if (var5 < ' ' || var5 > 127) { + var0.setLength(var2); + var0.append("STRINGDECODE('"); + javaEncode(var1, var0, true); + return var0.append("')"); + } + + var0.append(var5); + } + + return var0.append('\''); + } + } + + public static String javaEncode(String var0) { + StringBuilder var1 = new StringBuilder(var0.length()); + javaEncode(var0, var1, false); + return var1.toString(); + } + + public static void javaEncode(String var0, StringBuilder var1, boolean var2) { + int var3 = var0.length(); + + for(int var4 = 0; var4 < var3; ++var4) { + char var5 = var0.charAt(var4); + switch (var5) { + case '\t': + var1.append("\\t"); + break; + case '\n': + var1.append("\\n"); + break; + case '\f': + var1.append("\\f"); + break; + case '\r': + var1.append("\\r"); + break; + case '"': + var1.append("\\\""); + break; + case '\'': + if (var2) { + var1.append('\''); + } + + var1.append('\''); + break; + case '\\': + var1.append("\\\\"); + break; + default: + if (var5 >= ' ' && var5 < 128) { + var1.append(var5); + } else { + var1.append("\\u").append(HEX[var5 >>> 12]).append(HEX[var5 >>> 8 & 15]).append(HEX[var5 >>> 4 & 15]).append(HEX[var5 & 15]); + } + } + } + + } + + public static String addAsterisk(String var0, int var1) { + if (var0 != null) { + int var2 = var0.length(); + var1 = Math.min(var1, var2); + var0 = (new StringBuilder(var2 + 3)).append(var0, 0, var1).append("[*]").append(var0, var1, var2).toString(); + } + + return var0; + } + + private static DbException getFormatException(String var0, int var1) { + return DbException.get(90095, addAsterisk(var0, var1)); + } + + public static String javaDecode(String var0) { + int var1 = var0.length(); + StringBuilder var2 = new StringBuilder(var1); + + for(int var3 = 0; var3 < var1; ++var3) { + char var4 = var0.charAt(var3); + if (var4 == '\\') { + if (var3 + 1 >= var0.length()) { + throw getFormatException(var0, var3); + } + + ++var3; + var4 = var0.charAt(var3); + switch (var4) { + case '"': + var2.append('"'); + break; + case '#': + var2.append('#'); + break; + case ':': + var2.append(':'); + break; + case '=': + var2.append('='); + break; + case '\\': + var2.append('\\'); + break; + case 'b': + var2.append('\b'); + break; + case 'f': + var2.append('\f'); + break; + case 'n': + var2.append('\n'); + break; + case 'r': + var2.append('\r'); + break; + case 't': + var2.append('\t'); + break; + case 'u': + try { + var4 = (char)Integer.parseInt(var0.substring(var3 + 1, var3 + 5), 16); + } catch (NumberFormatException var7) { + throw getFormatException(var0, var3); + } + + var3 += 4; + var2.append(var4); + break; + default: + if (var4 < '0' || var4 > '9') { + throw getFormatException(var0, var3); + } + + try { + var4 = (char)Integer.parseInt(var0.substring(var3, var3 + 3), 8); + } catch (NumberFormatException var6) { + throw getFormatException(var0, var3); + } + + var3 += 2; + var2.append(var4); + } + } else { + var2.append(var4); + } + } + + return var2.toString(); + } + + public static String quoteJavaString(String var0) { + if (var0 == null) { + return "null"; + } else { + StringBuilder var1 = (new StringBuilder(var0.length() + 2)).append('"'); + javaEncode(var0, var1, false); + return var1.append('"').toString(); + } + } + + public static String quoteJavaStringArray(String[] var0) { + if (var0 == null) { + return "null"; + } else { + StringBuilder var1 = new StringBuilder("new String[]{"); + + for(int var2 = 0; var2 < var0.length; ++var2) { + if (var2 > 0) { + var1.append(", "); + } + + var1.append(quoteJavaString(var0[var2])); + } + + return var1.append('}').toString(); + } + } + + public static String quoteJavaIntArray(int[] var0) { + if (var0 == null) { + return "null"; + } else { + StringBuilder var1 = new StringBuilder("new int[]{"); + + for(int var2 = 0; var2 < var0.length; ++var2) { + if (var2 > 0) { + var1.append(", "); + } + + var1.append(var0[var2]); + } + + return var1.append('}').toString(); + } + } + + public static String unEnclose(String var0) { + return var0.startsWith("(") && var0.endsWith(")") ? var0.substring(1, var0.length() - 1) : var0; + } + + public static String urlEncode(String var0) { + try { + return URLEncoder.encode(var0, "UTF-8"); + } catch (Exception var2) { + throw DbException.convert(var2); + } + } + + public static String urlDecode(String var0) { + int var1 = var0.length(); + byte[] var2 = new byte[var1]; + int var3 = 0; + + for(int var4 = 0; var4 < var1; ++var4) { + char var5 = var0.charAt(var4); + if (var5 == '+') { + var2[var3++] = 32; + } else if (var5 == '%') { + var2[var3++] = (byte)Integer.parseInt(var0.substring(var4 + 1, var4 + 3), 16); + var4 += 2; + } else { + if (var5 > 127 || var5 < ' ') { + throw new IllegalArgumentException("Unexpected char " + var5 + " decoding " + var0); + } + + var2[var3++] = (byte)var5; + } + } + + return new String(var2, 0, var3, StandardCharsets.UTF_8); + } + + public static String[] arraySplit(String var0, char var1, boolean var2) { + if (var0 == null) { + return null; + } else { + int var3 = var0.length(); + if (var3 == 0) { + return new String[0]; + } else { + ArrayList var4 = Utils.newSmallArrayList(); + StringBuilder var5 = new StringBuilder(var3); + + for(int var6 = 0; var6 < var3; ++var6) { + char var7 = var0.charAt(var6); + if (var7 == var1) { + String var8 = var5.toString(); + var4.add(var2 ? var8.trim() : var8); + var5.setLength(0); + } else if (var7 == '\\' && var6 < var3 - 1) { + ++var6; + var5.append(var0.charAt(var6)); + } else { + var5.append(var7); + } + } + + String var9 = var5.toString(); + var4.add(var2 ? var9.trim() : var9); + return (String[])var4.toArray(new String[0]); + } + } + } + + public static String arrayCombine(String[] var0, char var1) { + StringBuilder var2 = new StringBuilder(); + + for(int var3 = 0; var3 < var0.length; ++var3) { + if (var3 > 0) { + var2.append(var1); + } + + String var4 = var0[var3]; + if (var4 != null) { + int var5 = 0; + + for(int var6 = var4.length(); var5 < var6; ++var5) { + char var7 = var4.charAt(var5); + if (var7 == '\\' || var7 == var1) { + var2.append('\\'); + } + + var2.append(var7); + } + } + } + + return var2.toString(); + } + + public static StringBuilder join(StringBuilder var0, ArrayList var1, String var2) { + int var3 = 0; + + for(int var4 = var1.size(); var3 < var4; ++var3) { + if (var3 > 0) { + var0.append(var2); + } + + var0.append((String)var1.get(var3)); + } + + return var0; + } + + public static String xmlAttr(String var0, String var1) { + return " " + var0 + "=\"" + xmlText(var1) + "\""; + } + + public static String xmlNode(String var0, String var1, String var2) { + return xmlNode(var0, var1, var2, true); + } + + public static String xmlNode(String var0, String var1, String var2, boolean var3) { + StringBuilder var4 = new StringBuilder(); + var4.append('<').append(var0); + if (var1 != null) { + var4.append(var1); + } + + if (var2 == null) { + var4.append("/>\n"); + return var4.toString(); + } else { + var4.append('>'); + if (var3 && var2.indexOf(10) >= 0) { + var4.append('\n'); + indent(var4, var2, 4, true); + } else { + var4.append(var2); + } + + var4.append("\n"); + return var4.toString(); + } + } + + public static StringBuilder indent(StringBuilder var0, String var1, int var2, boolean var3) { + int var4 = 0; + + int var6; + for(int var5 = var1.length(); var4 < var5; var4 = var6) { + for(var6 = 0; var6 < var2; ++var6) { + var0.append(' '); + } + + var6 = var1.indexOf(10, var4); + var6 = var6 < 0 ? var5 : var6 + 1; + var0.append(var1, var4, var6); + } + + if (var3 && !var1.endsWith("\n")) { + var0.append('\n'); + } + + return var0; + } + + public static String xmlComment(String var0) { + int var1 = 0; + + while(true) { + var1 = var0.indexOf("--", var1); + if (var1 < 0) { + if (var0.indexOf(10) >= 0) { + StringBuilder var2 = (new StringBuilder(var0.length() + 18)).append("\n").toString(); + } else { + return "\n"; + } + } + + var0 = var0.substring(0, var1 + 1) + " " + var0.substring(var1 + 1); + } + } + + public static String xmlCData(String var0) { + if (var0.contains("]]>")) { + return xmlText(var0); + } else { + boolean var1 = var0.endsWith("\n"); + var0 = ""; + return var1 ? var0 + "\n" : var0; + } + } + + public static String xmlStartDoc() { + return "\n"; + } + + public static String xmlText(String var0) { + return xmlText(var0, false); + } + + public static String xmlText(String var0, boolean var1) { + int var2 = var0.length(); + StringBuilder var3 = new StringBuilder(var2); + + for(int var4 = 0; var4 < var2; ++var4) { + char var5 = var0.charAt(var4); + switch (var5) { + case '\t': + var3.append(var5); + break; + case '\n': + case '\r': + if (var1) { + var3.append("&#x").append(Integer.toHexString(var5)).append(';'); + } else { + var3.append(var5); + } + break; + case '"': + var3.append("""); + break; + case '&': + var3.append("&"); + break; + case '\'': + var3.append("'"); + break; + case '<': + var3.append("<"); + break; + case '>': + var3.append(">"); + break; + default: + if (var5 >= ' ' && var5 <= 127) { + var3.append(var5); + } else { + var3.append("&#x").append(Integer.toHexString(var5)).append(';'); + } + } + } + + return var3.toString(); + } + + public static String replaceAll(String var0, String var1, String var2) { + int var3 = var0.indexOf(var1); + if (var3 >= 0 && !var1.isEmpty()) { + StringBuilder var4 = new StringBuilder(var0.length() - var1.length() + var2.length()); + int var5 = 0; + + do { + var4.append(var0, var5, var3).append(var2); + var5 = var3 + var1.length(); + var3 = var0.indexOf(var1, var5); + } while(var3 >= 0); + + var4.append(var0, var5, var0.length()); + return var4.toString(); + } else { + return var0; + } + } + + public static String quoteIdentifier(String var0) { + return quoteIdentifier(new StringBuilder(var0.length() + 2), var0).toString(); + } + + public static StringBuilder quoteIdentifier(StringBuilder var0, String var1) { + var0.append('"'); + int var2 = 0; + + for(int var3 = var1.length(); var2 < var3; ++var2) { + char var4 = var1.charAt(var2); + if (var4 == '"') { + var0.append(var4); + } + + var0.append(var4); + } + + return var0.append('"'); + } + + public static boolean isNullOrEmpty(String var0) { + return var0 == null || var0.isEmpty(); + } + + public static String quoteRemarkSQL(String var0) { + var0 = replaceAll(var0, "*/", "++/"); + return replaceAll(var0, "/*", "/++"); + } + + public static String pad(String var0, int var1, String var2, boolean var3) { + if (var1 < 0) { + var1 = 0; + } + + if (var1 < var0.length()) { + return var0.substring(0, var1); + } else if (var1 == var0.length()) { + return var0; + } else { + char var4; + if (var2 != null && !var2.isEmpty()) { + var4 = var2.charAt(0); + } else { + var4 = ' '; + } + + StringBuilder var5 = new StringBuilder(var1); + var1 -= var0.length(); + if (var3) { + var5.append(var0); + } + + for(int var6 = 0; var6 < var1; ++var6) { + var5.append(var4); + } + + if (!var3) { + var5.append(var0); + } + + return var5.toString(); + } + } + + public static char[] cloneCharArray(char[] var0) { + if (var0 == null) { + return null; + } else { + int var1 = var0.length; + return var1 == 0 ? var0 : Arrays.copyOf(var0, var1); + } + } + + public static String trim(String var0, boolean var1, boolean var2, String var3) { + char var4 = var3 != null && !var3.isEmpty() ? var3.charAt(0) : 32; + int var5 = 0; + int var6 = var0.length(); + if (var1) { + while(var5 < var6 && var0.charAt(var5) == var4) { + ++var5; + } + } + + if (var2) { + while(var6 > var5 && var0.charAt(var6 - 1) == var4) { + --var6; + } + } + + return var0.substring(var5, var6); + } + + public static String trimSubstring(String var0, int var1) { + return trimSubstring(var0, var1, var0.length()); + } + + public static String trimSubstring(String var0, int var1, int var2) { + while(var1 < var2 && var0.charAt(var1) <= ' ') { + ++var1; + } + + while(var1 < var2 && var0.charAt(var2 - 1) <= ' ') { + --var2; + } + + return var0.substring(var1, var2); + } + + public static StringBuilder trimSubstring(StringBuilder var0, String var1, int var2, int var3) { + while(var2 < var3 && var1.charAt(var2) <= ' ') { + ++var2; + } + + while(var2 < var3 && var1.charAt(var3 - 1) <= ' ') { + --var3; + } + + return var0.append(var1, var2, var3); + } + + public static String cache(String var0) { + if (!SysProperties.OBJECT_CACHE) { + return var0; + } else if (var0 == null) { + return var0; + } else if (var0.isEmpty()) { + return ""; + } else { + String[] var1 = getCache(); + if (var1 != null) { + int var2 = var0.hashCode(); + int var3 = var2 & SysProperties.OBJECT_CACHE_SIZE - 1; + String var4 = var1[var3]; + if (var0.equals(var4)) { + return var4; + } + + var1[var3] = var0; + } + + return var0; + } + } + + public static void clearCache() { + softCache = null; + } + + public static int parseUInt31(String var0, int var1, int var2) { + if (var2 <= var0.length() && var1 >= 0 && var1 <= var2) { + if (var1 == var2) { + throw new NumberFormatException(""); + } else { + int var3 = 0; + + for(int var4 = var1; var4 < var2; ++var4) { + char var5 = var0.charAt(var4); + if (var5 < '0' || var5 > '9' || var3 > 214748364) { + throw new NumberFormatException(var0.substring(var1, var2)); + } + + var3 = var3 * 10 + var5 - 48; + if (var3 < 0) { + throw new NumberFormatException(var0.substring(var1, var2)); + } + } + + return var3; + } + } else { + throw new IndexOutOfBoundsException(); + } + } + + public static byte[] convertHexToBytes(String var0) { + int var1 = var0.length(); + if (var1 % 2 != 0) { + throw DbException.get(90003, var0); + } else { + var1 /= 2; + byte[] var2 = new byte[var1]; + int var3 = 0; + int[] var4 = HEX_DECODE; + + try { + for(int var5 = 0; var5 < var1; ++var5) { + int var6 = var4[var0.charAt(var5 + var5)] << 4 | var4[var0.charAt(var5 + var5 + 1)]; + var3 |= var6; + var2[var5] = (byte)var6; + } + } catch (ArrayIndexOutOfBoundsException var7) { + throw DbException.get(90004, var0); + } + + if ((var3 & -256) != 0) { + throw DbException.get(90004, var0); + } else { + return var2; + } + } + } + + public static ByteArrayOutputStream convertHexWithSpacesToBytes(ByteArrayOutputStream var0, String var1) { + int var2 = var1.length(); + if (var0 == null) { + var0 = new ByteArrayOutputStream(var2 / 2); + } + + int var3 = 0; + int[] var4 = HEX_DECODE; + + try { + int var5 = 0; + + while(var5 < var2) { + char var6 = var1.charAt(var5++); + if (var6 != ' ') { + char var7; + do { + if (var5 >= var2) { + if (((var3 | var4[var6]) & -256) != 0) { + throw DbException.get(90004, var1); + } + + throw DbException.get(90003, var1); + } + + var7 = var1.charAt(var5++); + } while(var7 == ' '); + + int var8 = var4[var6] << 4 | var4[var7]; + var3 |= var8; + var0.write(var8); + } + } + } catch (ArrayIndexOutOfBoundsException var9) { + throw DbException.get(90004, var1); + } + + if ((var3 & -256) != 0) { + throw DbException.get(90004, var1); + } else { + return var0; + } + } + + public static String convertBytesToHex(byte[] var0) { + return convertBytesToHex(var0, var0.length); + } + + public static String convertBytesToHex(byte[] var0, int var1) { + char[] var2 = new char[var1 + var1]; + char[] var3 = HEX; + + for(int var4 = 0; var4 < var1; ++var4) { + int var5 = var0[var4] & 255; + var2[var4 + var4] = var3[var5 >> 4]; + var2[var4 + var4 + 1] = var3[var5 & 15]; + } + + return new String(var2); + } + + public static StringBuilder convertBytesToHex(StringBuilder var0, byte[] var1) { + return convertBytesToHex(var0, var1, var1.length); + } + + public static StringBuilder convertBytesToHex(StringBuilder var0, byte[] var1, int var2) { + char[] var3 = HEX; + + for(int var4 = 0; var4 < var2; ++var4) { + int var5 = var1[var4] & 255; + var0.append(var3[var5 >>> 4]).append(var3[var5 & 15]); + } + + return var0; + } + + public static StringBuilder appendHex(StringBuilder var0, long var1, int var3) { + char[] var4 = HEX; + int var5 = var3 * 8; + + while(var5 > 0) { + var5 -= 4; + StringBuilder var10000 = var0.append(var4[(int)(var1 >> var5) & 15]); + var5 -= 4; + var10000.append(var4[(int)(var1 >> var5) & 15]); + } + + return var0; + } + + public static boolean isNumber(String var0) { + int var1 = var0.length(); + if (var1 == 0) { + return false; + } else { + for(int var2 = 0; var2 < var1; ++var2) { + if (!Character.isDigit(var0.charAt(var2))) { + return false; + } + } + + return true; + } + } + + public static boolean isWhitespaceOrEmpty(String var0) { + int var1 = 0; + + for(int var2 = var0.length(); var1 < var2; ++var1) { + if (var0.charAt(var1) > ' ') { + return false; + } + } + + return true; + } + + public static void appendZeroPadded(StringBuilder var0, int var1, long var2) { + if (var1 == 2) { + if (var2 < 10L) { + var0.append('0'); + } + + var0.append(var2); + } else { + String var4 = Long.toString(var2); + + for(var1 -= var4.length(); var1 > 0; --var1) { + var0.append('0'); + } + + var0.append(var4); + } + + } + + public static String escapeMetaDataPattern(String var0) { + return var0 != null && !var0.isEmpty() ? replaceAll(var0, "\\", "\\\\") : var0; + } + + static { + int var0; + for(var0 = 0; var0 < HEX_DECODE.length; ++var0) { + HEX_DECODE[var0] = -1; + } + + for(var0 = 0; var0 <= 9; HEX_DECODE[var0 + 48] = var0++) { + } + + for(var0 = 0; var0 <= 5; ++var0) { + HEX_DECODE[var0 + 97] = HEX_DECODE[var0 + 65] = var0 + 10; + } + + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/compat/v3/MVMapBuilderTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/compat/v3/MVMapBuilderTest.java deleted file mode 100644 index 9488930dc..000000000 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/compat/v3/MVMapBuilderTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.mvstore.compat.v3; - -import org.junit.Test; - -import static org.junit.Assert.assertTrue; - -public class MVMapBuilderTest { - @Test - public void testConstructor() { - MVMapBuilder actualMvMapBuilder = new MVMapBuilder<>(); - assertTrue(actualMvMapBuilder.getKeyType() instanceof NitriteDataType); - assertTrue(actualMvMapBuilder.getValueType() instanceof NitriteDataType); - } -} - diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataTypeTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataTypeTest.java deleted file mode 100644 index 35b31ccff..000000000 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/mvstore/compat/v3/NitriteDataTypeTest.java +++ /dev/null @@ -1,754 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.mvstore.compat.v3; - -import org.h2.mvstore.WriteBuffer; -import org.junit.Test; - -import java.io.UnsupportedEncodingException; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.UUID; - -import static org.junit.Assert.*; - -public class NitriteDataTypeTest { - @Test - public void testAutoDetectDataTypeGetType() { - assertTrue((new NitriteDataType.BigDecimalType(new NitriteDataType())) - .getType("42") instanceof NitriteDataType.StringType); - assertEquals(4, - (new NitriteDataType.BigDecimalType(new NitriteDataType())).getType(0).typeId); - } - - @Test - public void testBigDecimalTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.BigDecimalType actualBigDecimalType = new NitriteDataType.BigDecimalType(nitriteDataType); - assertEquals(9, actualBigDecimalType.typeId); - assertSame(actualBigDecimalType.base, nitriteDataType); - } - - @Test - public void testBigIntegerTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.BigIntegerType actualBigIntegerType = new NitriteDataType.BigIntegerType(nitriteDataType); - assertEquals(6, actualBigIntegerType.typeId); - assertSame(actualBigIntegerType.base, nitriteDataType); - } - - @Test - public void testBooleanTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.BooleanType actualBooleanType = new NitriteDataType.BooleanType(nitriteDataType); - assertEquals(1, actualBooleanType.typeId); - assertSame(actualBooleanType.base, nitriteDataType); - } - - @Test - public void testByteTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.ByteType actualByteType = new NitriteDataType.ByteType(nitriteDataType); - assertEquals(2, actualByteType.typeId); - assertSame(actualByteType.base, nitriteDataType); - } - - @Test - public void testCharacterTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.CharacterType actualCharacterType = new NitriteDataType.CharacterType(nitriteDataType); - assertEquals(10, actualCharacterType.typeId); - assertSame(actualCharacterType.base, nitriteDataType); - } - - @Test - public void testDateTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.DateType actualDateType = new NitriteDataType.DateType(nitriteDataType); - assertEquals(13, actualDateType.typeId); - assertSame(actualDateType.base, nitriteDataType); - } - - @Test - public void testDoubleTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.DoubleType actualDoubleType = new NitriteDataType.DoubleType(nitriteDataType); - assertEquals(8, actualDoubleType.typeId); - assertSame(actualDoubleType.base, nitriteDataType); - } - - @Test - public void testFloatTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.FloatType actualFloatType = new NitriteDataType.FloatType(nitriteDataType); - assertEquals(7, actualFloatType.typeId); - assertSame(actualFloatType.base, nitriteDataType); - } - - @Test - public void testIntegerTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.IntegerType actualIntegerType = new NitriteDataType.IntegerType(nitriteDataType); - assertEquals(4, actualIntegerType.typeId); - assertSame(actualIntegerType.base, nitriteDataType); - } - - @Test - public void testIsBigInteger() { - assertFalse(NitriteDataType.isBigInteger("Obj")); - assertFalse(NitriteDataType.isBigInteger(null)); - } - - @Test - public void testIsBigDecimal() { - assertFalse(NitriteDataType.isBigDecimal("Obj")); - assertFalse(NitriteDataType.isBigDecimal(null)); - } - - @Test - public void testIsDate() { - assertFalse(NitriteDataType.isDate("Obj")); - assertFalse(NitriteDataType.isDate(null)); - } - - @Test - public void testIsArray() { - assertFalse(NitriteDataType.isArray("Obj")); - assertFalse(NitriteDataType.isArray(null)); - } - - @Test - public void testGetCommonClassId() { - assertEquals(8, NitriteDataType.getCommonClassId(Object.class).intValue()); - assertNull(NitriteDataType.getCommonClassId(null)); - } - - @Test - public void testLongTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.LongType actualLongType = new NitriteDataType.LongType(nitriteDataType); - assertEquals(5, actualLongType.typeId); - assertSame(actualLongType.base, nitriteDataType); - } - - @Test - public void testNullTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.NullType actualNullType = new NitriteDataType.NullType(nitriteDataType); - assertEquals(0, actualNullType.typeId); - assertSame(actualNullType.base, nitriteDataType); - } - - @Test - public void testObjectArrayTypeConstructor() { - assertEquals(14, (new NitriteDataType.ObjectArrayType(new NitriteDataType())).typeId); - } - - @Test - public void testSerialize() { - byte[] actualSerializeResult = NitriteDataType.serialize("Obj"); - assertEquals(10, actualSerializeResult.length); - assertEquals((byte) 0, actualSerializeResult[2]); - assertEquals((byte) 5, actualSerializeResult[3]); - assertEquals('t', actualSerializeResult[4]); - assertEquals((byte) 0, actualSerializeResult[5]); - assertEquals((byte) 3, actualSerializeResult[6]); - assertEquals('O', actualSerializeResult[7]); - assertEquals('b', actualSerializeResult[8]); - assertEquals('j', actualSerializeResult[9]); - } - - @Test - public void testCompareNotNull() throws UnsupportedEncodingException { - byte[] data1 = "AAAAAAAA".getBytes("UTF-8"); - assertEquals(0, NitriteDataType.compareNotNull(data1, "AAAAAAAA".getBytes("UTF-8"))); - } - - @Test - public void testCompareNotNull2() throws UnsupportedEncodingException { - assertEquals(-1, - NitriteDataType.compareNotNull(new byte[]{0, 'A', 'A', 'A', 'A', 'A', 'A', 'A'}, "AAAAAAAA".getBytes("UTF-8"))); - } - - @Test - public void testCompareNotNull3() throws UnsupportedEncodingException { - byte[] data1 = "ï¿¿AAAAAAA".getBytes("UTF-8"); - assertEquals(1, NitriteDataType.compareNotNull(data1, "AAAAAAAA".getBytes("UTF-8"))); - } - - @Test - public void testAutoDetectDataTypeCompare() { - assertEquals(-1, (new NitriteDataType.BigDecimalType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.BigDecimalType(new NitriteDataType())).compare(0, "B Obj")); - } - - @Test - public void testBigDecimalTypeCompare() { - assertEquals(-1, (new NitriteDataType.BigDecimalType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.BigDecimalType(new NitriteDataType())).compare(0, "B Obj")); - } - - @Test - public void testBigIntegerTypeCompare() { - assertEquals(-1, (new NitriteDataType.BigIntegerType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.BigIntegerType(new NitriteDataType())).compare(0, "B Obj")); - } - - @Test - public void testBooleanTypeCompare() { - assertEquals(-1, (new NitriteDataType.BooleanType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.BooleanType(new NitriteDataType())).compare(true, "B Obj")); - assertEquals(0, (new NitriteDataType.BooleanType(new NitriteDataType())).compare(true, true)); - } - - @Test - public void testByteTypeCompare() { - assertEquals(-1, (new NitriteDataType.ByteType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.ByteType(new NitriteDataType())).compare((byte) 'A', "B Obj")); - assertEquals(0, (new NitriteDataType.ByteType(new NitriteDataType())).compare((byte) 'A', (byte) 'A')); - } - - @Test - public void testCharacterTypeCompare() { - assertEquals(-1, (new NitriteDataType.CharacterType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.CharacterType(new NitriteDataType())).compare('\u0000', "B Obj")); - assertEquals(0, (new NitriteDataType.CharacterType(new NitriteDataType())).compare('\u0000', '\u0000')); - } - - @Test - public void testCompare() { - assertEquals(0, (new NitriteDataType()).compare("42", "42")); - assertEquals(-1, (new NitriteDataType()).compare(0, "42")); - assertEquals(1, (new NitriteDataType()).compare("42", 0)); - assertEquals(0, (new NitriteDataType()).compare(0, 0)); - } - - @Test - public void testDateTypeCompare() { - assertEquals(-1, (new NitriteDataType.DateType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.DateType(new NitriteDataType())).compare(0, "B Obj")); - } - - @Test - public void testDoubleTypeCompare() { - assertEquals(-1, (new NitriteDataType.DoubleType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.DoubleType(new NitriteDataType())).compare(0, "B Obj")); - assertEquals(-1, (new NitriteDataType.DoubleType(new NitriteDataType())).compare(10.0, "B Obj")); - assertEquals(0, (new NitriteDataType.DoubleType(new NitriteDataType())).compare(10.0, 10.0)); - } - - @Test - public void testFloatTypeCompare() { - assertEquals(-1, (new NitriteDataType.FloatType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.FloatType(new NitriteDataType())).compare(0, "B Obj")); - assertEquals(-1, (new NitriteDataType.FloatType(new NitriteDataType())).compare(10.0f, "B Obj")); - assertEquals(0, (new NitriteDataType.FloatType(new NitriteDataType())).compare(10.0f, 10.0f)); - } - - @Test - public void testIntegerTypeCompare() { - assertEquals(-1, (new NitriteDataType.IntegerType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.IntegerType(new NitriteDataType())).compare(0, "B Obj")); - assertEquals(0, (new NitriteDataType.IntegerType(new NitriteDataType())).compare(0, 0)); - } - - @Test - public void testLongTypeCompare() { - assertEquals(-1, (new NitriteDataType.LongType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.LongType(new NitriteDataType())).compare(0L, "B Obj")); - assertEquals(0, (new NitriteDataType.LongType(new NitriteDataType())).compare(0L, 0L)); - } - - @Test - public void testNullTypeCompare() { - assertEquals(-1, (new NitriteDataType.NullType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.NullType(new NitriteDataType())).compare(0, "B Obj")); - assertEquals(-1, (new NitriteDataType.NullType(new NitriteDataType())).compare(null, "B Obj")); - assertEquals(1, (new NitriteDataType.NullType(new NitriteDataType())).compare("A Obj", null)); - assertEquals(0, (new NitriteDataType.NullType(new NitriteDataType())).compare(null, null)); - } - - @Test - public void testObjectArrayTypeCompare() { - assertEquals(-1, (new NitriteDataType.ObjectArrayType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.ObjectArrayType(new NitriteDataType())).compare(0, "B Obj")); - } - - @Test - public void testSerializedObjectTypeCompare() { - assertEquals(-1, (new NitriteDataType.SerializedObjectType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.SerializedObjectType(new NitriteDataType())).compare(0, "B Obj")); - assertEquals(0, (new NitriteDataType.SerializedObjectType(new NitriteDataType())).compare(0, 0)); - } - - @Test - public void testShortTypeCompare() { - assertEquals(-1, (new NitriteDataType.ShortType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.ShortType(new NitriteDataType())).compare((short) 0, "B Obj")); - assertEquals(0, (new NitriteDataType.ShortType(new NitriteDataType())).compare((short) 0, (short) 0)); - } - - @Test - public void testStringTypeCompare() { - assertEquals(-1, (new NitriteDataType.StringType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.StringType(new NitriteDataType())).compare(0, "B Obj")); - assertEquals(1, (new NitriteDataType.StringType(new NitriteDataType())).compare("A Obj", 0)); - assertEquals(-1, (new NitriteDataType.StringType(new NitriteDataType())).compare(4, "B Obj")); - assertEquals(0, (new NitriteDataType.StringType(new NitriteDataType())).compare(0, 0)); - } - - @Test - public void testUUIDTypeCompare() { - assertEquals(-1, (new NitriteDataType.UUIDType(new NitriteDataType())).compare("A Obj", "B Obj")); - assertEquals(-1, (new NitriteDataType.UUIDType(new NitriteDataType())).compare(0, "B Obj")); - } - - @Test - public void testUUIDTypeCompare2() { - NitriteDataType.UUIDType uuidType = new NitriteDataType.UUIDType(new NitriteDataType()); - assertEquals(1, uuidType.compare(UUID.randomUUID(), "B Obj")); - } - - @Test - public void testAutoDetectDataTypeGetMemory() { - assertEquals(28, (new NitriteDataType.BigDecimalType(new NitriteDataType())).getMemory("42")); - assertEquals(24, (new NitriteDataType.BigDecimalType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testBigDecimalTypeGetMemory() { - assertEquals(30, (new NitriteDataType.BigDecimalType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.BigDecimalType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testBigDecimalTypeGetMemory2() { - NitriteDataType nitriteDataType = new NitriteDataType(); - nitriteDataType.switchType(1); - assertEquals(30, (new NitriteDataType.BigDecimalType(nitriteDataType)).getMemory("Obj")); - } - - @Test - public void testBigIntegerTypeGetMemory() { - assertEquals(30, (new NitriteDataType.BigIntegerType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.BigIntegerType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testBigIntegerTypeGetMemory2() { - NitriteDataType nitriteDataType = new NitriteDataType(); - nitriteDataType.switchType(1); - assertEquals(30, (new NitriteDataType.BigIntegerType(nitriteDataType)).getMemory("Obj")); - } - - @Test - public void testBooleanTypeGetMemory() { - assertEquals(30, (new NitriteDataType.BooleanType(new NitriteDataType())).getMemory("Obj")); - assertEquals(0, (new NitriteDataType.BooleanType(new NitriteDataType())).getMemory(true)); - assertEquals(24, (new NitriteDataType.BooleanType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testByteTypeGetMemory() { - assertEquals(30, (new NitriteDataType.ByteType(new NitriteDataType())).getMemory("Obj")); - assertEquals(0, (new NitriteDataType.ByteType(new NitriteDataType())).getMemory((byte) 'A')); - assertEquals(24, (new NitriteDataType.ByteType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testCharacterTypeGetMemory() { - assertEquals(30, (new NitriteDataType.CharacterType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.CharacterType(new NitriteDataType())).getMemory('\u0000')); - assertEquals(24, (new NitriteDataType.CharacterType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testDateTypeGetMemory() { - assertEquals(30, (new NitriteDataType.DateType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.DateType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testDateTypeGetMemory2() { - NitriteDataType nitriteDataType = new NitriteDataType(); - nitriteDataType.switchType(1); - assertEquals(30, (new NitriteDataType.DateType(nitriteDataType)).getMemory("Obj")); - } - - @Test - public void testDoubleTypeGetMemory() { - assertEquals(30, (new NitriteDataType.DoubleType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.DoubleType(new NitriteDataType())).getMemory(0)); - assertEquals(30, (new NitriteDataType.DoubleType(new NitriteDataType())).getMemory(10.0)); - } - - @Test - public void testFloatTypeGetMemory() { - assertEquals(30, (new NitriteDataType.FloatType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.FloatType(new NitriteDataType())).getMemory(0)); - assertEquals(24, (new NitriteDataType.FloatType(new NitriteDataType())).getMemory(10.0f)); - } - - @Test - public void testGetMemory() { - assertEquals(30, (new NitriteDataType()).getMemory("Obj")); - assertEquals(24, (new NitriteDataType()).getMemory(0)); - } - - @Test - public void testGetMemory2() { - NitriteDataType nitriteDataType = new NitriteDataType(); - nitriteDataType.switchType(1); - assertEquals(30, nitriteDataType.getMemory("Obj")); - } - - @Test - public void testIntegerTypeGetMemory() { - assertEquals(30, (new NitriteDataType.IntegerType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.IntegerType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testIntegerTypeGetMemory2() { - NitriteDataType nitriteDataType = new NitriteDataType(); - nitriteDataType.switchType(1); - assertEquals(30, (new NitriteDataType.IntegerType(nitriteDataType)).getMemory("Obj")); - } - - @Test - public void testLongTypeGetMemory() { - assertEquals(30, (new NitriteDataType.LongType(new NitriteDataType())).getMemory("Obj")); - assertEquals(30, (new NitriteDataType.LongType(new NitriteDataType())).getMemory(0L)); - assertEquals(24, (new NitriteDataType.LongType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testNullTypeGetMemory() { - assertEquals(30, (new NitriteDataType.NullType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.NullType(new NitriteDataType())).getMemory(0)); - assertEquals(0, (new NitriteDataType.NullType(new NitriteDataType())).getMemory(null)); - } - - @Test - public void testObjectArrayTypeGetMemory() { - assertEquals(30, (new NitriteDataType.ObjectArrayType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.ObjectArrayType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testObjectArrayTypeGetMemory2() { - NitriteDataType nitriteDataType = new NitriteDataType(); - nitriteDataType.switchType(1); - assertEquals(30, (new NitriteDataType.ObjectArrayType(nitriteDataType)).getMemory("Obj")); - } - - @Test - public void testSerializedObjectTypeGetMemory() { - assertEquals(30, (new NitriteDataType.SerializedObjectType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.SerializedObjectType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testSerializedObjectTypeGetMemory2() { - NitriteDataType.SerializedObjectType serializedObjectType = new NitriteDataType.SerializedObjectType( - new NitriteDataType()); - serializedObjectType.write(new WriteBuffer(), 19088743); - assertEquals(30, serializedObjectType.getMemory("Obj")); - } - - @Test - public void testShortTypeGetMemory() { - assertEquals(30, (new NitriteDataType.ShortType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.ShortType(new NitriteDataType())).getMemory((short) 0)); - assertEquals(24, (new NitriteDataType.ShortType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testStringTypeGetMemory() { - assertEquals(30, (new NitriteDataType.StringType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.StringType(new NitriteDataType())).getMemory(0)); - assertEquals(24, (new NitriteDataType.StringType(new NitriteDataType())).getMemory(4)); - } - - @Test - public void testStringTypeGetMemory2() { - NitriteDataType nitriteDataType = new NitriteDataType(); - nitriteDataType.switchType(1); - assertEquals(24, (new NitriteDataType.StringType(nitriteDataType)).getMemory(0)); - } - - @Test - public void testUUIDTypeGetMemory() { - assertEquals(30, (new NitriteDataType.UUIDType(new NitriteDataType())).getMemory("Obj")); - assertEquals(24, (new NitriteDataType.UUIDType(new NitriteDataType())).getMemory(0)); - } - - @Test - public void testUUIDTypeGetMemory2() { - NitriteDataType.UUIDType uuidType = new NitriteDataType.UUIDType(new NitriteDataType()); - assertEquals(40, uuidType.getMemory(UUID.randomUUID())); - } - - @Test - public void testBigDecimalTypeRead() { - Object actualReadResult = (new NitriteDataType.BigDecimalType(new NitriteDataType())).read(null, 46); - assertSame(((BigDecimal) actualReadResult).ZERO, actualReadResult); - } - - @Test - public void testBigDecimalTypeRead2() { - Object actualReadResult = (new NitriteDataType.BigDecimalType(new NitriteDataType())).read(null, 47); - assertSame(((BigDecimal) actualReadResult).ONE, actualReadResult); - } - - @Test - public void testBigIntegerTypeRead() { - Object actualReadResult = (new NitriteDataType.BigIntegerType(new NitriteDataType())).read(null, 37); - assertSame(((BigInteger) actualReadResult).ZERO, actualReadResult); - } - - @Test - public void testBigIntegerTypeRead2() { - Object actualReadResult = (new NitriteDataType.BigIntegerType(new NitriteDataType())).read(null, 38); - assertSame(((BigInteger) actualReadResult).ONE, actualReadResult); - } - - @Test - public void testNullTypeRead() { - assertNull((new NitriteDataType.NullType(new NitriteDataType())).read(null, 1)); - } - - @Test - public void testStringTypeRead2() { - assertEquals("", (new NitriteDataType.StringType(new NitriteDataType())).read(null, 88)); - } - - @Test - public void testAutoDetectDataTypeWrite() { - NitriteDataType.BigDecimalType bigDecimalType = new NitriteDataType.BigDecimalType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - bigDecimalType.write(writeBuffer, "42"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testAutoDetectDataTypeWrite2() { - NitriteDataType.BigDecimalType bigDecimalType = new NitriteDataType.BigDecimalType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - bigDecimalType.write(writeBuffer, new Object[]{"42", "42", "42"}, 3, true); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testAutoDetectDataTypeWrite3() { - NitriteDataType.BigDecimalType bigDecimalType = new NitriteDataType.BigDecimalType(null); - assertThrows(ArrayIndexOutOfBoundsException.class, - () -> bigDecimalType.write(new WriteBuffer(), new Object[]{}, 3, true)); - } - - @Test - public void testBigDecimalTypeWrite() { - NitriteDataType.BigDecimalType bigDecimalType = new NitriteDataType.BigDecimalType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - bigDecimalType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testBigIntegerTypeWrite() { - NitriteDataType.BigIntegerType bigIntegerType = new NitriteDataType.BigIntegerType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - bigIntegerType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testBooleanTypeWrite() { - NitriteDataType.BooleanType booleanType = new NitriteDataType.BooleanType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - booleanType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testByteTypeWrite() { - NitriteDataType.ByteType byteType = new NitriteDataType.ByteType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - byteType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testCharacterTypeWrite() { - NitriteDataType.CharacterType characterType = new NitriteDataType.CharacterType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - characterType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testDateTypeWrite() { - NitriteDataType.DateType dateType = new NitriteDataType.DateType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - dateType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testDoubleTypeWrite() { - NitriteDataType.DoubleType doubleType = new NitriteDataType.DoubleType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - doubleType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testFloatTypeWrite() { - NitriteDataType.FloatType floatType = new NitriteDataType.FloatType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - floatType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testIntegerTypeWrite() { - NitriteDataType.IntegerType integerType = new NitriteDataType.IntegerType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - integerType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testLongTypeWrite() { - NitriteDataType.LongType longType = new NitriteDataType.LongType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - longType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testNullTypeWrite() { - NitriteDataType.NullType nullType = new NitriteDataType.NullType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - nullType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testObjectArrayTypeWrite() { - NitriteDataType.ObjectArrayType objectArrayType = new NitriteDataType.ObjectArrayType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - objectArrayType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testSerializedObjectTypeWrite() { - NitriteDataType.SerializedObjectType serializedObjectType = new NitriteDataType.SerializedObjectType( - new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - serializedObjectType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testShortTypeWrite() { - NitriteDataType.ShortType shortType = new NitriteDataType.ShortType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - shortType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testStringTypeWrite() { - NitriteDataType.StringType stringType = new NitriteDataType.StringType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - stringType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testUUIDTypeWrite() { - NitriteDataType.UUIDType uuidType = new NitriteDataType.UUIDType(new NitriteDataType()); - WriteBuffer writeBuffer = new WriteBuffer(); - uuidType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testSerializedObjectTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.SerializedObjectType actualSerializedObjectType = new NitriteDataType.SerializedObjectType( - nitriteDataType); - assertEquals(19, actualSerializedObjectType.typeId); - assertSame(actualSerializedObjectType.base, nitriteDataType); - } - - @Test - public void testShortTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.ShortType actualShortType = new NitriteDataType.ShortType(nitriteDataType); - assertEquals(3, actualShortType.typeId); - assertSame(actualShortType.base, nitriteDataType); - } - - @Test - public void testStringTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.StringType actualStringType = new NitriteDataType.StringType(nitriteDataType); - assertEquals(11, actualStringType.typeId); - assertSame(actualStringType.base, nitriteDataType); - } - - @Test - public void testUUIDTypeConstructor() { - NitriteDataType nitriteDataType = new NitriteDataType(); - NitriteDataType.UUIDType actualUuidType = new NitriteDataType.UUIDType(nitriteDataType); - assertEquals(12, actualUuidType.typeId); - assertSame(actualUuidType.base, nitriteDataType); - } - - @Test - public void testWrite() { - NitriteDataType nitriteDataType = new NitriteDataType(); - WriteBuffer writeBuffer = new WriteBuffer(); - nitriteDataType.write(writeBuffer, "Obj"); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testWrite4() { - NitriteDataType nitriteDataType = new NitriteDataType(); - WriteBuffer writeBuffer = new WriteBuffer(); - nitriteDataType.write(writeBuffer, new Object[]{"42", "42", "42"}, 3, true); - assertEquals(1048576, writeBuffer.capacity()); - } - - @Test - public void testWrite5() { - assertThrows(ArrayIndexOutOfBoundsException.class, - () -> (new NitriteDataType()).write(null, new Object[]{}, 3, true)); - } - - @Test - public void testSwitchType() { - assertTrue((new NitriteDataType()).switchType("Obj") instanceof NitriteDataType.StringType); - assertEquals(4, (new NitriteDataType()).switchType(0).typeId); - } -} - diff --git a/nitrite/src/main/resources/version b/nitrite/src/main/resources/version index cc868b62c..99eba4de9 100644 --- a/nitrite/src/main/resources/version +++ b/nitrite/src/main/resources/version @@ -1 +1 @@ -4.0.1 \ No newline at end of file +4.1.0 \ No newline at end of file From b5c9e5284db194d16fe7c5a30f677594c5dbebb0 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Sat, 13 Aug 2022 11:34:09 +0530 Subject: [PATCH 60/78] refactoring --- .../no2/integration/repository/data/Book.java | 2 +- .../integration/repository/data/BookId.java | 3 - .../no2/integration/repository/data/Book.java | 2 +- .../integration/repository/data/BookId.java | 3 - .../no2/integration/repository/data/Book.java | 2 +- .../integration/repository/data/BookId.java | 3 - .../java/org/dizitart/no2/common/Fields.java | 2 +- .../no2/repository/AnnotationScanner.java | 21 ++---- .../no2/repository/IndexValidator.java | 50 +++++-------- .../no2/repository/ObjectIdField.java | 74 ++++--------------- .../no2/repository/RepositoryOperations.java | 2 +- .../no2/repository/annotations/Embedded.java | 47 ------------ .../no2/repository/annotations/Id.java | 7 ++ .../no2/collection/FindOptionsTest.java | 8 ++ .../operation/ReadOperationsTest.java | 7 +- .../meta/AttributesAwareTest.java | 21 +++++- .../meta/AttributesTest.java | 20 ++++- .../no2/integration/repository/data/Book.java | 2 +- .../integration/repository/data/BookId.java | 3 - .../no2/repository/AnnotationScannerTest.java | 6 +- 20 files changed, 106 insertions(+), 179 deletions(-) delete mode 100644 nitrite/src/main/java/org/dizitart/no2/repository/annotations/Embedded.java rename nitrite/src/test/java/org/dizitart/no2/{collection => common}/meta/AttributesAwareTest.java (60%) rename nitrite/src/test/java/org/dizitart/no2/{collection => common}/meta/AttributesTest.java (60%) diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index 5bffe69e8..9d21f72e8 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -37,7 +37,7 @@ }) public class Book { @JsonProperty("book_id") - @Id(fieldName = "book_id") + @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) private BookId bookId; private String publisher; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java index 988908919..b051dfe18 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java @@ -19,17 +19,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; -import org.dizitart.no2.repository.annotations.Embedded; /** * @author Anindya Chatterjee */ @Data public class BookId { - @Embedded(order = 0) private String isbn; - @Embedded(order = 1, fieldName = "book_name") @JsonProperty("book_name") private String name; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index 5a147769c..c80c7b060 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -40,7 +40,7 @@ @Index(value = { "price", "publisher" }) }) public class Book implements Mappable { - @Id(fieldName = "book_id") + @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) private BookId bookId; private String publisher; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java index 239b493d3..2afcc42f6 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java @@ -21,7 +21,6 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.NitriteMapper; -import org.dizitart.no2.repository.annotations.Embedded; import static org.dizitart.no2.collection.Document.createDocument; @@ -30,10 +29,8 @@ */ @Data public class BookId implements Mappable { - @Embedded(order = 0) private String isbn; - @Embedded(order = 1, fieldName = "book_name") private String name; private String author; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index 5a147769c..c80c7b060 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -40,7 +40,7 @@ @Index(value = { "price", "publisher" }) }) public class Book implements Mappable { - @Id(fieldName = "book_id") + @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) private BookId bookId; private String publisher; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java index 239b493d3..2afcc42f6 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java @@ -21,7 +21,6 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.NitriteMapper; -import org.dizitart.no2.repository.annotations.Embedded; import static org.dizitart.no2.collection.Document.createDocument; @@ -30,10 +29,8 @@ */ @Data public class BookId implements Mappable { - @Embedded(order = 0) private String isbn; - @Embedded(order = 1, fieldName = "book_name") private String name; private String author; diff --git a/nitrite/src/main/java/org/dizitart/no2/common/Fields.java b/nitrite/src/main/java/org/dizitart/no2/common/Fields.java index 66ba15317..bc89affb2 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/Fields.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/Fields.java @@ -91,7 +91,7 @@ public boolean startsWith(Fields other) { int length = Math.min(fieldNames.size(), other.fieldNames.size()); - // if other is greater then it is not a prefix of this field + // if other is greater than it is not a prefix of this field if (other.fieldNames.size() > length) return false; for (int i = 0; i < length; i++) { diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java b/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java index eb74e2c1e..d34b352ce 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java @@ -63,14 +63,14 @@ public void createIndices() { public void createIdIndex() { if (objectIdField != null) { - String[] fieldNames = objectIdField.getFieldNames(nitriteMapper); + String[] fieldNames = objectIdField.getEmbeddedFieldNames(); if (!collection.hasIndex(fieldNames)) { collection.createIndex(fieldNames); } } } - public void scanIndices() { + public void performScan() { // populate from @Indices scanIndicesAnnotation(); @@ -137,7 +137,7 @@ private void scanIdAnnotation() { if (field.isAnnotationPresent(Id.class)) { Id id = field.getAnnotation(Id.class); String fieldName = StringUtils.isNullOrEmpty(id.fieldName()) ? field.getName() : id.fieldName(); - indexValidator.validate(field.getType(), fieldName, nitriteMapper); + indexValidator.validateId(id, field.getType(), fieldName, nitriteMapper); if (alreadyIdFound) { throw new NotIdentifiableException("Multiple id fields found for the type"); } else { @@ -145,24 +145,13 @@ private void scanIdAnnotation() { objectIdField = new ObjectIdField(); objectIdField.setField(field); objectIdField.setIdFieldName(fieldName); - objectIdField.setEmbedded(isEmbeddedId(field)); + objectIdField.setEmbedded(id.embeddedFields().length > 0); + objectIdField.setFieldNames(id.embeddedFields()); } } } } - private boolean isEmbeddedId(Field field) { - List fields = reflector.getAllFields(field.getType()); - if (fields.size() == 0) return false; - - for (Field f : fields) { - if (f.isAnnotationPresent(Embedded.class)) { - return true; - } - } - return false; - } - private void populateIndex(List indexList) { for (Index index : indexList) { String[] names = index.value(); diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java b/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java index 64b2eba63..521014623 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java @@ -17,17 +17,12 @@ package org.dizitart.no2.repository; -import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.IndexingException; -import org.dizitart.no2.repository.annotations.Embedded; +import org.dizitart.no2.repository.annotations.Id; -import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.List; - -import static org.dizitart.no2.common.util.DocumentUtils.skeletonDocument; /** * @author Anindya Chatterjee @@ -58,32 +53,25 @@ public void validate(Class fieldType, String field, NitriteMapper nitriteMapp return; } - Document document; - try { - document = skeletonDocument(nitriteMapper, fieldType); - if (document.size() > 0) { - // compound index - boolean embeddedFieldFound = false; - List fields = reflector.getAllFields(fieldType); - for (Field indexField : fields) { - if (indexField.isAnnotationPresent(Embedded.class)) { - embeddedFieldFound = true; - break; - } - } + if (!Comparable.class.isAssignableFrom(fieldType)) { + throw new IndexingException("Cannot create index on non comparable field " + field); + } + } + + public void validateId(Id id, Class fieldType, String field, NitriteMapper nitriteMapper) { + if (fieldType.isPrimitive() + || fieldType == NitriteId.class + || fieldType.isInterface() + || nitriteMapper.isValueType(fieldType) + || Modifier.isAbstract(fieldType.getModifiers()) + || fieldType.isArray() + || Iterable.class.isAssignableFrom(fieldType)) { + // we will validate the solid class during insertion/update + return; + } - if (!embeddedFieldFound) { - throw new IndexingException("No embedded field found for object id"); - } - } else { - if (!Comparable.class.isAssignableFrom(fieldType)) { - throw new IndexingException("Cannot index on non comparable field " + field); - } - } - } catch (IndexingException ie) { - throw ie; - } catch (Throwable e) { - throw new IndexingException("Invalid type specified " + fieldType.getName() + " for indexing", e); + if (id.embeddedFields().length == 0) { + throw new IndexingException("Invalid Id field " + field); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectIdField.java b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectIdField.java index 6a885eff9..161b36751 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectIdField.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectIdField.java @@ -22,16 +22,10 @@ import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.mapper.NitriteMapper; -import org.dizitart.no2.common.util.StringUtils; -import org.dizitart.no2.exceptions.IndexingException; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.filters.NitriteFilter; -import org.dizitart.no2.repository.annotations.Embedded; import java.lang.reflect.Field; -import java.util.List; -import java.util.NavigableMap; -import java.util.TreeMap; import static org.dizitart.no2.filters.FluentFilter.where; @@ -39,9 +33,9 @@ * @author Anindya Chatterjee */ class ObjectIdField { - private final Reflector reflector; - private final IndexValidator indexValidator; - private String[] embeddedFieldNames; + @Getter + @Setter + private String[] fieldNames; @Getter @Setter @@ -55,60 +49,32 @@ class ObjectIdField { @Setter private String idFieldName; - public ObjectIdField() { - this.reflector = new Reflector(); - this.indexValidator = new IndexValidator(reflector); - } - - public String[] getFieldNames(NitriteMapper nitriteMapper) { - if (embeddedFieldNames != null) { - return embeddedFieldNames; - } - + public String[] getEmbeddedFieldNames() { if (!isEmbedded) { - embeddedFieldNames = new String[]{ idFieldName }; - return embeddedFieldNames; - } - - List fieldList = reflector.getAllFields(field.getType()); - NavigableMap orderedFieldName = new TreeMap<>(); - - boolean embeddedFieldFound = false; - for (Field field : fieldList) { - if (field.isAnnotationPresent(Embedded.class)) { - embeddedFieldFound = true; - Embedded embedded = field.getAnnotation(Embedded.class); - int order = embedded.order(); - String fieldName = StringUtils.isNullOrEmpty(embedded.fieldName()) - ? field.getName() : embedded.fieldName(); - - String name = this.idFieldName + NitriteConfig.getFieldSeparator() + fieldName; - indexValidator.validate(field.getType(), name, nitriteMapper); - - orderedFieldName.put(order, name); - } + return new String[]{ idFieldName }; } - if (!embeddedFieldFound) { - throw new IndexingException("No embedded field found for " + field.getName()); + String[] fieldNames = new String[this.fieldNames.length]; + for (int i = 0; i < this.fieldNames.length; i++) { + String name = this.idFieldName + NitriteConfig.getFieldSeparator() + this.fieldNames[i]; + fieldNames[i] = name; } - embeddedFieldNames = orderedFieldName.values().toArray(new String[0]); - return embeddedFieldNames; + return fieldNames; } public Filter createUniqueFilter(Object value, NitriteMapper nitriteMapper) { - if (embeddedFieldNames.length == 1) { + if (getEmbeddedFieldNames().length == 1) { return where(idFieldName).eq(value); } else { Document document = nitriteMapper.convert(value, Document.class); - Filter[] filters = new Filter[embeddedFieldNames.length]; + Filter[] filters = new Filter[fieldNames.length]; int index = 0; - for (String field : embeddedFieldNames) { - String docFieldName = getEmbeddedFieldName(field); - Object fieldValue = document.get(docFieldName); - filters[index++] = where(field).eq(fieldValue); + for (String field : fieldNames) { + String filterField = idFieldName + NitriteConfig.getFieldSeparator() + field; + Object fieldValue = document.get(field); + filters[index++] = where(filterField).eq(fieldValue); } NitriteFilter nitriteFilter = (NitriteFilter) Filter.and(filters); @@ -116,12 +82,4 @@ public Filter createUniqueFilter(Object value, NitriteMapper nitriteMapper) { return nitriteFilter; } } - - private String getEmbeddedFieldName(String fieldName) { - if (fieldName.contains(NitriteConfig.getFieldSeparator())) { - return fieldName.substring(fieldName.indexOf(NitriteConfig.getFieldSeparator()) + 1); - } else { - return fieldName; - } - } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java index 2a9599649..3b597f076 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java @@ -69,7 +69,7 @@ public RepositoryOperations(Class type, * Create indices. */ public void createIndices() { - annotationScanner.scanIndices(); + annotationScanner.performScan(); annotationScanner.createIndices(); annotationScanner.createIdIndex(); objectIdField = annotationScanner.getObjectIdField(); diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Embedded.java b/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Embedded.java deleted file mode 100644 index 70f680d40..000000000 --- a/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Embedded.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2017-2021 Nitrite author or authors. - * - * 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 org.dizitart.no2.repository.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Indicates that an annotated field is used to construct a composite id field. - * - * @author Anindya Chatterjee - * @since 4.0.0 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface Embedded { - /** - * Order of the field in compound index. - * - * @return the int - */ - int order(); - - /** - * The custom field name in compound index. - * - * @return the string - */ - String fieldName() default ""; -} diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Id.java b/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Id.java index b8d8f6a1b..afd9c5178 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Id.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Id.java @@ -36,4 +36,11 @@ * @return the string */ String fieldName() default ""; + + /** + * The name of the embedded fields. + * + * @return the string + */ + String[] embeddedFields() default {}; } diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/FindOptionsTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/FindOptionsTest.java index db71d7ff3..57698e77b 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/FindOptionsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/FindOptionsTest.java @@ -121,5 +121,13 @@ public void testThenOrderBy2() { FindOptions orderByResult = FindOptions.orderBy("Field Name", SortOrder.Ascending); assertSame(orderByResult, orderByResult.thenOrderBy("Field Name", SortOrder.Ascending)); } + + @Test + public void testDistinct() { + FindOptions findOptions = new FindOptions(); + FindOptions actualDistinctResult = findOptions.withDistinct(true); + assertSame(findOptions, actualDistinctResult); + assertTrue(actualDistinctResult.distinct()); + } } diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/operation/ReadOperationsTest.java b/nitrite/src/test/java/org/dizitart/no2/collection/operation/ReadOperationsTest.java index bbb99823d..0d5aef3a7 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/operation/ReadOperationsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/collection/operation/ReadOperationsTest.java @@ -27,6 +27,7 @@ import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.store.memory.InMemoryMap; import org.junit.Test; +import org.mockito.internal.verification.NoInteractions; import java.util.ArrayList; @@ -68,12 +69,16 @@ public void testFind2() { @Test public void testGetById() { + IndexOperations indexOperations = mock(IndexOperations.class); + when(indexOperations.listIndexes()).thenReturn(new ArrayList<>()); + NitriteConfig nitriteConfig = new NitriteConfig(); InMemoryMap nitriteMap = new InMemoryMap<>("Map Name", null); - ReadOperations readOperations = new ReadOperations("Collection Name", null, nitriteConfig, nitriteMap, + ReadOperations readOperations = new ReadOperations("Collection Name", indexOperations, nitriteConfig, nitriteMap, new ProcessorChain()); assertNull(readOperations.getById(NitriteId.newId())); + verify(indexOperations, new NoInteractions()).listIndexes(); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesAwareTest.java b/nitrite/src/test/java/org/dizitart/no2/common/meta/AttributesAwareTest.java similarity index 60% rename from nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesAwareTest.java rename to nitrite/src/test/java/org/dizitart/no2/common/meta/AttributesAwareTest.java index 75c2c61f1..888fa38e0 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesAwareTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/meta/AttributesAwareTest.java @@ -1,7 +1,22 @@ -package org.dizitart.no2.collection.meta; +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.common.meta; -import org.dizitart.no2.common.meta.Attributes; -import org.dizitart.no2.common.meta.AttributesAware; import org.junit.Assert; import org.junit.Test; diff --git a/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesTest.java b/nitrite/src/test/java/org/dizitart/no2/common/meta/AttributesTest.java similarity index 60% rename from nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesTest.java rename to nitrite/src/test/java/org/dizitart/no2/common/meta/AttributesTest.java index 5f651dbb7..2f1fa9fe5 100644 --- a/nitrite/src/test/java/org/dizitart/no2/collection/meta/AttributesTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/meta/AttributesTest.java @@ -1,6 +1,22 @@ -package org.dizitart.no2.collection.meta; +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.common.meta; -import org.dizitart.no2.common.meta.Attributes; import org.junit.Test; import java.util.Map; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index 5a147769c..c80c7b060 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -40,7 +40,7 @@ @Index(value = { "price", "publisher" }) }) public class Book implements Mappable { - @Id(fieldName = "book_id") + @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) private BookId bookId; private String publisher; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java index 239b493d3..2afcc42f6 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java @@ -21,7 +21,6 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.NitriteMapper; -import org.dizitart.no2.repository.annotations.Embedded; import static org.dizitart.no2.collection.Document.createDocument; @@ -30,10 +29,8 @@ */ @Data public class BookId implements Mappable { - @Embedded(order = 0) private String isbn; - @Embedded(order = 1, fieldName = "book_name") private String name; private String author; diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/AnnotationScannerTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/AnnotationScannerTest.java index 615c43162..1f3ca80a0 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/AnnotationScannerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/AnnotationScannerTest.java @@ -56,7 +56,7 @@ public void testScanIndices() { Class type = Object.class; AnnotationScanner annotationScanner = new AnnotationScanner(type, null, new NitriteBuilderTest.CustomNitriteMapper()); - annotationScanner.scanIndices(); + annotationScanner.performScan(); assertNull(annotationScanner.getObjectIdField()); } @@ -65,7 +65,7 @@ public void testScanIndices2() { Class type = Field.class; AnnotationScanner annotationScanner = new AnnotationScanner(type, null, new NitriteBuilderTest.CustomNitriteMapper()); - annotationScanner.scanIndices(); + annotationScanner.performScan(); assertNull(annotationScanner.getObjectIdField()); } @@ -75,7 +75,7 @@ public void testScanIndices3() { NitriteCollection collection = mock(NitriteCollection.class); AnnotationScanner annotationScanner = new AnnotationScanner(type, collection, new NitriteBuilderTest.CustomNitriteMapper()); - annotationScanner.scanIndices(); + annotationScanner.performScan(); assertNull(annotationScanner.getObjectIdField()); } } From 705a4550790630e36d9d9996e60c6d4ac0089a25 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 16 Aug 2022 23:13:27 +0530 Subject: [PATCH 61/78] refactoring --- .../java/org/dizitart/no2/common/streams/DocumentSorter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentSorter.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentSorter.java index d0361277c..64b7dc8ce 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentSorter.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/DocumentSorter.java @@ -22,7 +22,7 @@ import org.dizitart.no2.common.DBNull; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.common.tuples.Pair; -import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.exceptions.InvalidOperationException; import java.text.Collator; import java.util.Comparator; @@ -76,7 +76,7 @@ public int compare(Pair pair1, Pair pa // validate comparable if (!(value1 instanceof Comparable) || !(value2 instanceof Comparable)) { - throw new ValidationException("Cannot compare " + value1.getClass() + throw new InvalidOperationException("Cannot compare " + value1.getClass() + " and " + value2.getClass()); } From 83e5a0514d69be2d60887acc979c58ef32404488 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 22 Aug 2022 10:11:33 +0530 Subject: [PATCH 62/78] doc corection --- .../java/org/dizitart/no2/common/streams/IndexedStream.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/streams/IndexedStream.java b/nitrite/src/main/java/org/dizitart/no2/common/streams/IndexedStream.java index 9bd99e034..d94368fb9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/streams/IndexedStream.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/streams/IndexedStream.java @@ -27,7 +27,7 @@ import java.util.Set; /** - * Represents a nitrite nitrite stream backed by an index. + * Represents a nitrite stream backed by an index. * * @author Anindya Chatterjee * @since 4.0 From a14535f9ed5b345784963cfc28a65f6d4b1856ca Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 5 Sep 2022 10:00:53 +0530 Subject: [PATCH 63/78] entity decorator and entity converter --- TODO.md | 44 +- gradle.properties | 1 + nitrite-android-example/build.gradle | 7 - .../no2/example/android/MainActivity.java | 4 + .../dizitart/no2/example/android/User.java | 40 +- nitrite-bom/build.gradle | 3 +- .../no2/common/mapper/JacksonMapper.java | 173 ++---- .../common/mapper/JacksonMapperModule.java | 8 +- .../mapper/extensions/NitriteIdExtension.java | 52 -- .../NitriteIdDeserializer.java | 2 +- .../mapper/modules/NitriteIdModule.java | 36 ++ .../NitriteIdSerializer.java | 2 +- .../no2/common/mapper/JacksonMapperTest.java | 80 +-- .../NitriteIdDeserializerTest.java | 2 +- .../NitriteIdSerializerTest.java | 2 +- .../repository/JacksonModuleTest.java | 19 +- .../repository/ObjectCursorTest.java | 2 +- .../ObjectRepositoryNegativeTest.java | 2 +- .../repository/RepositoryFactoryTest.java | 2 +- .../no2/integration/repository/data/Note.java | 3 - .../java/org/dizitart/no2/NitriteTest.java | 91 ++- .../no2/integration/NitriteBuilderTest.java | 77 ++- .../no2/integration/NitriteStressTest.java | 132 ++-- .../CollectionFindNegativeTest.java | 3 +- .../collection/CollectionIndexTest.java | 2 +- .../CollectionSingleFieldIndexTest.java | 2 +- .../no2/integration/event/EventTest.java | 4 + .../integration/migration/MigrationTest.java | 5 + .../no2/integration/migration/NewClass.java | 79 ++- .../no2/integration/migration/OldClass.java | 79 ++- .../repository/BaseObjectRepositoryTest.java | 37 +- .../repository/CustomFieldSeparatorTest.java | 65 +- .../integration/repository/InternalClass.java | 31 +- .../repository/NitriteIdAsIdTest.java | 4 + .../ObjectRepositoryNegativeTest.java | 15 +- .../repository/ObjectRepositoryTest.java | 70 ++- .../repository/RepositoryFactoryTest.java | 2 +- .../repository/RepositoryJoinTest.java | 141 +++-- .../repository/RepositorySearchTest.java | 55 +- .../UniversalTextTokenizerTest.java | 34 +- .../no2/integration/repository/data/Book.java | 46 +- .../integration/repository/data/BookId.java | 38 +- .../repository/data/ChildClass.java | 35 +- .../integration/repository/data/ClassA.java | 46 +- .../integration/repository/data/ClassB.java | 17 +- .../repository/data/ClassBConverter.java | 47 ++ .../integration/repository/data/ClassC.java | 48 +- .../integration/repository/data/Company.java | 51 +- .../repository/data/DataGenerator.java | 27 + .../repository/data/ElemMatch.java | 58 +- .../integration/repository/data/Employee.java | 59 +- .../repository/data/EncryptedPerson.java | 40 +- .../no2/integration/repository/data/Note.java | 32 +- .../repository/data/ParentClass.java | 16 - .../repository/data/PersonEntity.java | 50 +- .../repository/data/ProductScore.java | 32 +- .../repository/data/RepeatableIndexTest.java | 38 +- .../repository/data/StressRecord.java | 43 +- .../repository/data/SubEmployee.java | 38 +- .../repository/data/SuperDuperClass.java | 15 +- .../repository/data/WithClassField.java | 31 +- .../repository/data/WithDateId.java | 32 +- .../repository/data/WithEmptyStringId.java | 28 +- .../repository/data/WithNitriteId.java | 34 +- .../repository/data/WithNullId.java | 34 +- .../repository/data/WithObjectId.java | 28 +- .../repository/data/WithOutGetterSetter.java | 31 +- .../repository/data/WithOutId.java | 34 +- .../data/WithPrivateConstructor.java | 31 +- .../repository/data/WithPublicField.java | 32 +- .../repository/data/WithTransientField.java | 30 +- .../repository/data/WithoutEmbeddedId.java | 61 +- .../repository/decorator/Manufacturer.java | 17 +- .../decorator/ManufacturerConverter.java | 45 ++ .../decorator/ManufacturerDecorator.java | 41 ++ .../repository/decorator/MiniProduct.java | 55 ++ .../repository/decorator/Product.java | 19 +- .../decorator/ProductConverter.java | 54 ++ .../decorator/ProductDecorator.java | 51 ++ .../repository/decorator/ProductId.java | 26 + .../decorator/ProductIdConverter.java | 43 ++ .../no2/integration/transaction/TxData.java | 32 +- .../integration/DataGateIntegrationTest.java | 6 +- .../rocksdb/formatter/NitriteSerializers.java | 2 +- .../java/org/dizitart/no2/NitriteTest.java | 102 ++-- .../no2/integration/NitriteBuilderTest.java | 77 ++- .../no2/integration/NitriteStressTest.java | 129 ++-- .../CollectionFindNegativeTest.java | 3 +- .../collection/CollectionIndexTest.java | 2 +- .../CollectionSingleFieldIndexTest.java | 2 +- .../no2/integration/event/EventTest.java | 4 + .../integration/migrate/MigrationTest.java | 5 + .../no2/integration/migrate/NewClass.java | 77 ++- .../no2/integration/migrate/OldClass.java | 77 ++- .../repository/BaseObjectRepositoryTest.java | 33 + .../repository/CustomFieldSeparatorTest.java | 65 +- .../integration/repository/InternalClass.java | 31 +- .../repository/NitriteIdAsIdTest.java | 4 + .../ObjectRepositoryNegativeTest.java | 15 +- .../repository/ObjectRepositoryTest.java | 64 +- .../repository/RepositoryFactoryTest.java | 2 +- .../repository/RepositoryJoinTest.java | 141 +++-- .../repository/RepositorySearchTest.java | 57 +- .../UniversalTextTokenizerTest.java | 34 +- .../no2/integration/repository/data/Book.java | 46 +- .../integration/repository/data/BookId.java | 38 +- .../repository/data/ChildClass.java | 35 +- .../integration/repository/data/ClassA.java | 46 +- .../integration/repository/data/ClassB.java | 17 +- .../repository/data/ClassBConverter.java | 47 ++ .../integration/repository/data/ClassC.java | 48 +- .../integration/repository/data/Company.java | 51 +- .../repository/data/DataGenerator.java | 27 + .../repository/data/ElemMatch.java | 58 +- .../integration/repository/data/Employee.java | 59 +- .../repository/data/EncryptedPerson.java | 40 +- .../no2/integration/repository/data/Note.java | 32 +- .../repository/data/ParentClass.java | 16 - .../repository/data/PersonEntity.java | 50 +- .../repository/data/ProductScore.java | 32 +- .../repository/data/RepeatableIndexTest.java | 38 +- .../repository/data/StressRecord.java | 43 +- .../repository/data/SubEmployee.java | 38 +- .../repository/data/SuperDuperClass.java | 15 +- .../repository/data/WithClassField.java | 31 +- .../repository/data/WithDateId.java | 32 +- .../repository/data/WithEmptyStringId.java | 28 +- .../repository/data/WithNitriteId.java | 34 +- .../repository/data/WithNullId.java | 34 +- .../repository/data/WithObjectId.java | 28 +- .../repository/data/WithOutGetterSetter.java | 31 +- .../repository/data/WithOutId.java | 34 +- .../data/WithPrivateConstructor.java | 31 +- .../repository/data/WithPublicField.java | 32 +- .../repository/data/WithTransientField.java | 30 +- .../repository/data/WithoutEmbeddedId.java | 61 +- .../repository/decorator/Manufacturer.java | 27 + .../decorator/ManufacturerConverter.java | 45 ++ .../decorator/ManufacturerDecorator.java | 41 ++ .../repository/decorator/MiniProduct.java | 55 ++ .../repository/decorator/Product.java | 28 + .../decorator/ProductConverter.java | 54 ++ .../decorator/ProductDecorator.java | 51 ++ .../repository/decorator/ProductId.java | 26 + .../decorator/ProductIdConverter.java | 43 ++ .../no2/integration/transaction/TxData.java | 32 +- .../dizitart/no2/rocksdb/GithubIssues.java | 36 +- .../dizitart/no2/spatial/GeometryUtils.java | 55 ++ .../dizitart/no2/spatial/SpatialIndex.java | 2 +- .../spatial/mapper/GeometryDeserializer.java | 18 +- .../no2/spatial/mapper/GeometryExtension.java | 56 -- .../no2/spatial/mapper/GeometryModule.java | 36 ++ .../spatial/mapper/GeometrySerializer.java | 8 +- .../org/dizitart/no2/spatial/TestUtil.java | 4 +- nitrite-support/build.gradle | 1 + .../org/dizitart/no2/support/Exporter.java | 2 +- .../no2/support/NitriteJsonImporter.java | 4 +- .../no2/support/BaseExternalTest.java | 10 +- .../org/dizitart/no2/support/Company.java | 73 +-- .../dizitart/no2/support/DataGenerator.java | 80 +-- .../org/dizitart/no2/support/Employee.java | 51 +- .../java/org/dizitart/no2/support/Note.java | 32 +- .../src/test/resources/english.stop | 571 ------------------ nitrite-support/src/test/resources/test.text | 50 -- nitrite/build.gradle | 1 - .../main/java/org/dizitart/no2/Nitrite.java | 85 ++- .../org/dizitart/no2/NitriteDatabase.java | 28 + .../collection/DefaultNitriteCollection.java | 12 +- .../dizitart/no2/collection/FindOptions.java | 1 + .../operation/DocumentIndexWriter.java | 4 +- .../collection/operation/FindOptimizer.java | 4 +- .../collection/operation/IndexManager.java | 2 +- .../collection/operation/IndexOperations.java | 10 +- .../org/dizitart/no2/common/Constants.java | 1 - .../no2/common/mapper/EntityConverter.java | 19 +- .../dizitart/no2/common/mapper/Mappable.java | 44 -- .../no2/common/mapper/NitriteMapper.java | 16 - ...eMapper.java => SimpleDocumentMapper.java} | 150 +++-- .../no2/common/module/PluginManager.java | 4 +- .../no2/common/util/DocumentUtils.java | 12 +- .../dizitart/no2/common/util/IndexUtils.java | 2 +- .../dizitart/no2/common/util/ObjectUtils.java | 110 ++-- .../no2/filters/FieldBasedFilter.java | 5 +- .../dizitart/no2/index/IndexDescriptor.java | 15 +- .../org/dizitart/no2/index/IndexFields.java | 76 +++ .../no2/migration/DatabaseInstruction.java | 41 +- .../no2/migration/InstructionSet.java | 39 +- .../no2/migration/NitriteInstructionSet.java | 4 +- .../no2/migration/commands/Rename.java | 2 +- .../no2/migration/commands/RenameField.java | 4 +- .../no2/repository/AnnotationScanner.java | 2 +- .../repository/DefaultObjectRepository.java | 33 +- .../no2/repository/EntityDecorator.java | 34 ++ .../no2/repository/EntityDecoratorReader.java | 126 ++++ .../org/dizitart/no2/repository/EntityId.java | 84 +++ .../no2/repository/IndexValidator.java | 27 +- .../dizitart/no2/repository/ObjectCursor.java | 5 +- .../no2/repository/RepositoryFactory.java | 63 +- .../no2/repository/RepositoryOperations.java | 42 +- .../org/dizitart/no2/store/StoreCatalog.java | 10 +- .../DefaultTransactionalCollection.java | 8 +- .../DefaultTransactionalRepository.java | 33 +- .../no2/transaction/NitriteTransaction.java | 74 +++ .../dizitart/no2/transaction/Transaction.java | 21 + .../org/dizitart/no2/NitriteBuilderTest.java | 21 +- .../org/dizitart/no2/NitriteConfigTest.java | 3 +- .../dizitart/no2/common/event/EventTest.java | 4 + .../dizitart/no2/common/mapper/Company.java | 59 +- .../no2/common/mapper/Department.java | 48 +- .../dizitart/no2/common/mapper/Employee.java | 48 +- .../no2/common/mapper/MappableDepartment.java | 59 +- .../no2/common/mapper/MappableEmployee.java | 57 +- .../no2/common/mapper/MappableMapperTest.java | 113 ---- .../no2/common/mapper/MapperTest.java | 43 +- .../mapper/SimpleDocumentMapperTest.java | 96 +++ .../no2/common/module/PluginManagerTest.java | 3 +- .../no2/common/util/DocumentUtilsTest.java | 42 +- .../no2/common/util/IndexUtilsTest.java | 2 +- .../dizitart/no2/common/util/NumbersTest.java | 4 +- .../no2/common/util/ObjectUtilsTest.java | 84 ++- .../dizitart/no2/index/IndexFieldsTest.java | 57 ++ .../no2/index/NitriteTextIndexerTest.java | 8 +- .../no2/integration/NitriteStressTest.java | 57 +- .../dizitart/no2/integration/NitriteTest.java | 99 +-- .../dizitart/no2/integration/StressTest.java | 78 ++- .../CollectionFindNegativeTest.java | 3 +- .../collection/CollectionIndexTest.java | 2 +- .../CollectionSingleFieldIndexTest.java | 2 +- .../repository/BaseObjectRepositoryTest.java | 46 ++ .../repository/CustomFieldSeparatorTest.java | 74 ++- .../integration/repository/InternalClass.java | 31 +- .../repository/NitriteIdAsIdTest.java | 4 + .../ObjectRepositoryNegativeTest.java | 15 +- .../repository/ObjectRepositoryTest.java | 204 ++++++- .../repository/RepositoryFactoryTest.java | 2 +- .../repository/RepositoryJoinTest.java | 146 +++-- .../repository/RepositorySearchTest.java | 57 +- .../repository/UnAnnotatedObjectTest.java | 66 +- .../UniversalTextTokenizerTest.java | 40 +- .../no2/integration/repository/data/Book.java | 46 +- .../integration/repository/data/BookId.java | 38 +- .../repository/data/ChildClass.java | 35 +- .../integration/repository/data/ClassA.java | 46 +- .../integration/repository/data/ClassB.java | 17 +- .../repository/data/ClassBConverter.java | 47 ++ .../integration/repository/data/ClassC.java | 48 +- .../integration/repository/data/Company.java | 51 +- .../repository/data/DataGenerator.java | 27 + .../repository/data/ElemMatch.java | 58 +- .../integration/repository/data/Employee.java | 59 +- .../repository/data/EncryptedPerson.java | 40 +- .../no2/integration/repository/data/Note.java | 32 +- .../repository/data/ParentClass.java | 16 - .../repository/data/PersonEntity.java | 50 +- .../repository/data/ProductScore.java | 32 +- .../repository/data/RepeatableIndexTest.java | 38 +- .../repository/data/StressRecord.java | 43 +- .../repository/data/SubEmployee.java | 38 +- .../repository/data/SuperDuperClass.java | 15 +- .../repository/data/WithClassField.java | 31 +- .../repository/data/WithDateId.java | 32 +- .../repository/data/WithEmptyStringId.java | 28 +- .../repository/data/WithNitriteId.java | 34 +- .../repository/data/WithNullId.java | 34 +- .../repository/data/WithObjectId.java | 28 +- .../repository/data/WithOutGetterSetter.java | 31 +- .../repository/data/WithOutId.java | 34 +- .../data/WithPrivateConstructor.java | 31 +- .../repository/data/WithPublicField.java | 32 +- .../repository/data/WithTransientField.java | 30 +- .../repository/data/WithoutEmbeddedId.java | 61 +- .../repository/decorator/Manufacturer.java | 27 + .../decorator/ManufacturerConverter.java | 45 ++ .../decorator/ManufacturerDecorator.java | 41 ++ .../repository/decorator/MiniProduct.java | 55 ++ .../repository/decorator/Product.java | 28 + .../decorator/ProductConverter.java | 54 ++ .../decorator/ProductDecorator.java | 51 ++ .../repository/decorator/ProductId.java | 26 + .../decorator/ProductIdConverter.java | 43 ++ .../no2/integration/transaction/TxData.java | 33 +- .../repository/EntityDecoratorReaderTest.java | 132 ++++ .../no2/repository/IndexValidatorTest.java | 6 +- .../no2/repository/ObjectCursorTest.java | 4 +- .../no2/repository/RepositoryFactoryTest.java | 40 +- .../org/dizitart/kno2/KNO2JacksonMapper.kt | 33 +- .../kotlin/org/dizitart/kno2/KNO2Module.kt | 4 +- .../org/dizitart/kno2/BackportJavaTimeTest.kt | 51 +- 288 files changed, 7399 insertions(+), 3968 deletions(-) delete mode 100644 nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdExtension.java rename nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/{extensions => modules}/NitriteIdDeserializer.java (96%) create mode 100644 nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdModule.java rename nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/{extensions => modules}/NitriteIdSerializer.java (96%) rename nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/{extensions => modules}/NitriteIdDeserializerTest.java (95%) rename nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/{extensions => modules}/NitriteIdSerializerTest.java (95%) create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java rename nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java => nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java (75%) create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java rename nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdExtensionTest.java => nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java (63%) create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java create mode 100644 nitrite-spatial/src/main/java/org/dizitart/no2/spatial/GeometryUtils.java delete mode 100644 nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryExtension.java create mode 100644 nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryModule.java delete mode 100644 nitrite-support/src/test/resources/english.stop delete mode 100644 nitrite-support/src/test/resources/test.text rename nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonExtension.java => nitrite/src/main/java/org/dizitart/no2/common/mapper/EntityConverter.java (61%) delete mode 100644 nitrite/src/main/java/org/dizitart/no2/common/mapper/Mappable.java rename nitrite/src/main/java/org/dizitart/no2/common/mapper/{MappableMapper.java => SimpleDocumentMapper.java} (63%) create mode 100644 nitrite/src/main/java/org/dizitart/no2/index/IndexFields.java create mode 100644 nitrite/src/main/java/org/dizitart/no2/repository/EntityDecorator.java create mode 100644 nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorReader.java create mode 100644 nitrite/src/main/java/org/dizitart/no2/repository/EntityId.java delete mode 100644 nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableMapperTest.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/common/mapper/SimpleDocumentMapperTest.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/index/IndexFieldsTest.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorReaderTest.java diff --git a/TODO.md b/TODO.md index b0fe2bdb8..bb90ca700 100644 --- a/TODO.md +++ b/TODO.md @@ -1,12 +1,6 @@ ##TODO: -1. fix codacy issues -2. nitrite explorer -4. add p2p replications via jgroups -5. add lucene indexer -6. nitrite cluster via jgroups -7. spring data rest / graphql api over nitrite cluster -8. data views (like rdbms view) + ## Other Articles @@ -158,6 +152,42 @@ https://blog.yugabyte.com/enhancing-rocksdb-for-speed-scale/ - http://www.cbcb.umd.edu/confcour/Spring2014/CMSC424/query_optimization.pdf - https://www.tutorialcup.com/dbms/query-optimization.htm - https://www.geeksforgeeks.org/query-optimization-in-relational-algebra/ + + +## TODO Test + +``` + +DatabaseInstruction dropRepository(Class type, String key) +DatabaseInstruction dropRepository(EntityDecorator decorator) +DatabaseInstruction dropRepository(EntityDecorator decorator, String key) +DatabaseInstruction dropRepository(String typeName) + + +RepositoryInstruction forRepository(Class type) +RepositoryInstruction forRepository(Class type, String key) +RepositoryInstruction forRepository(EntityDecorator decorator) +RepositoryInstruction forRepository(EntityDecorator decorator, String key) +RepositoryInstruction forRepository(String typeName) + + +EntityId + +IndexValidator + +ObjectCursor + +RepositoryFactory + + +Transaction :: ObjectRepository getRepository(EntityDecorator decorator); +Transaction :: ObjectRepository getRepository(EntityDecorator decorator, String key); + + +KNO2JacksonMapper +``` + + diff --git a/gradle.properties b/gradle.properties index 9215ee6b0..0c49d87ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,3 +20,4 @@ org.gradle.parallel=false nitriteVersion=4.1.0-SNAPSHOT android.useAndroidX=true android.enableJetifier=true +android.suppressUnsupportedCompileSdk=32 diff --git a/nitrite-android-example/build.gradle b/nitrite-android-example/build.gradle index f70835ce9..874c2dc3e 100644 --- a/nitrite-android-example/build.gradle +++ b/nitrite-android-example/build.gradle @@ -55,8 +55,6 @@ android { versionCode 1 versionName "1.0" -// multiDexEnabled true - testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } @@ -67,12 +65,7 @@ android { } } - dexOptions { - jumboMode true // required - } - compileOptions { -// coreLibraryDesugaringEnabled true sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } diff --git a/nitrite-android-example/src/main/java/org/dizitart/no2/example/android/MainActivity.java b/nitrite-android-example/src/main/java/org/dizitart/no2/example/android/MainActivity.java index f82b59251..d74f1973b 100644 --- a/nitrite-android-example/src/main/java/org/dizitart/no2/example/android/MainActivity.java +++ b/nitrite-android-example/src/main/java/org/dizitart/no2/example/android/MainActivity.java @@ -26,6 +26,7 @@ import android.widget.ProgressBar; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.events.CollectionEventListener; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.common.util.Iterables; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.mvstore.MVStoreModule; @@ -55,8 +56,11 @@ protected void onCreate(Bundle savedInstanceState) { String fileName = getFilesDir().getPath() + "/test.db"; Log.i("Nitrite", "Nitrite file - " + fileName); + SimpleDocumentMapper documentMapper = new SimpleDocumentMapper(); + documentMapper.registerEntityConverter(new User.Converter()); db = Nitrite.builder() .loadModule(new MVStoreModule(fileName)) + .loadModule(() -> Iterables.setOf(documentMapper)) .openOrCreate("test-user", "test-password"); repository = db.getRepository(User.class); diff --git a/nitrite-android-example/src/main/java/org/dizitart/no2/example/android/User.java b/nitrite-android-example/src/main/java/org/dizitart/no2/example/android/User.java index 628ef3acf..9aa932f44 100644 --- a/nitrite-android-example/src/main/java/org/dizitart/no2/example/android/User.java +++ b/nitrite-android-example/src/main/java/org/dizitart/no2/example/android/User.java @@ -17,13 +17,13 @@ package org.dizitart.no2.example.android; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** * @author Anindya Chatterjee. */ -public class User implements Mappable { +public class User { private String id; private String username; private String email; @@ -62,19 +62,29 @@ public void setEmail(String email) { this.email = email; } - @Override - public Document write(NitriteMapper mapper) { - Document document = Document.createDocument(); - document.put("id", id); - document.put("username", username); - document.put("email", email); - return document; - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return User.class; + } + + @Override + public Document toDocument(User entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + document.put("id", entity.id); + document.put("username", entity.username); + document.put("email", entity.email); + return document; + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = (String) document.get("id"); - username = (String) document.get("username"); - email = (String) document.get("email"); + @Override + public User fromDocument(Document document, NitriteMapper nitriteMapper) { + User entity = new User(); + entity.id = (String) document.get("id"); + entity.username = (String) document.get("username"); + entity.email = (String) document.get("email"); + return entity; + } } } diff --git a/nitrite-bom/build.gradle b/nitrite-bom/build.gradle index cacfebf09..4636b3517 100644 --- a/nitrite-bom/build.gradle +++ b/nitrite-bom/build.gradle @@ -22,7 +22,6 @@ dependencies { api project(":nitrite-rocksdb-adapter") api "org.slf4j:slf4j-api:1.7.32" - api "org.objenesis:objenesis:2.6" api "org.jasypt:jasypt:1.9.3" api "com.fasterxml.jackson.core:jackson-databind:2.13.3" api "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.3" @@ -31,7 +30,7 @@ dependencies { api "com.h2database:h2-mvstore:2.1.214" api "com.squareup.okhttp3:okhttp:4.9.3" api "org.rocksdb:rocksdbjni:7.2.2" - api "com.esotericsoftware.kryo:kryo5:5.2.1" + api "com.esotericsoftware.kryo:kryo5:5.3.0" api "org.locationtech.jts:jts-core:1.18.2" api "commons-codec:commons-codec:1.15" api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21" diff --git a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapper.java b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapper.java index 825c1c508..a261800e4 100644 --- a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapper.java +++ b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapper.java @@ -18,71 +18,46 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.AccessLevel; -import lombok.Getter; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.*; import lombok.extern.slf4j.Slf4j; import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.util.ObjectUtils; +import org.dizitart.no2.common.mapper.modules.NitriteIdModule; import org.dizitart.no2.exceptions.ObjectMappingException; -import org.dizitart.no2.common.mapper.extensions.NitriteIdExtension; import java.io.IOException; -import java.lang.reflect.Modifier; import java.util.*; -import static org.dizitart.no2.common.util.Iterables.listOf; +import static org.dizitart.no2.common.util.ValidationUtils.notNull; /** * @author Anindya Chatterjee */ @Slf4j -public class JacksonMapper extends MappableMapper { - private final List jacksonExtensions; - private final List> moduleTypes; - - @Getter(AccessLevel.PROTECTED) - private final ObjectMapper objectMapper; - - public JacksonMapper() { - this.jacksonExtensions = new ArrayList<>(); - this.moduleTypes = new ArrayList<>(); - this.objectMapper = createObjectMapper(); - } - - public JacksonMapper(JacksonExtension... jacksonExtensions) { - this.jacksonExtensions = new ArrayList<>(listOf(jacksonExtensions)); - this.moduleTypes = new ArrayList<>(); - this.objectMapper = createObjectMapper(); - } - - protected ObjectMapper createObjectMapper() { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.setVisibility( - objectMapper.getSerializationConfig().getDefaultVisibilityChecker() - .withFieldVisibility(JsonAutoDetect.Visibility.ANY) - .withGetterVisibility(JsonAutoDetect.Visibility.NONE) - .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)); - objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); - objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); - objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - - this.jacksonExtensions.add(new NitriteIdExtension()); - for (JacksonExtension jacksonExtension : jacksonExtensions) { - loadJacksonExtension(jacksonExtension, objectMapper); +public class JacksonMapper implements NitriteMapper { + private ObjectMapper objectMapper; + + protected ObjectMapper getObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + objectMapper.setVisibility( + objectMapper.getSerializationConfig().getDefaultVisibilityChecker() + .withFieldVisibility(JsonAutoDetect.Visibility.ANY) + .withGetterVisibility(JsonAutoDetect.Visibility.NONE) + .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)); + objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); + objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.registerModule(new NitriteIdModule()); } return objectMapper; } - @Override - protected void addValueType(Class valueType) { - super.addValueType(valueType); - this.moduleTypes.add(valueType); + public void registerJacksonModule(Module module) { + notNull(module, "module cannot be null"); + getObjectMapper().registerModule(module); } @Override @@ -92,103 +67,61 @@ public Target convert(Source source, Class type) { return null; } - if (isValue(source)) { - if (this.moduleTypes.contains(type)) { - return this.objectMapper.convertValue(source, type); + try { + JsonNode node = getObjectMapper().convertValue(source, JsonNode.class); + if (node == null) return null; + + if (node.isValueNode()) { + return getNodeValue(node); } else { - return (Target) convertValue(source); - } - } else { - if (Document.class.isAssignableFrom(type)) { - return (Target) convertToDocument(source); - } else if (source instanceof Document) { - return convertFromDocument((Document) source, type); + if (Document.class.isAssignableFrom(type)) { + return (Target) convertToDocument(source); + } else if (source instanceof Document) { + return convertFromDocument((Document) source, type); + } } + } catch (Exception e) { + throw new ObjectMappingException("Failed to convert object of type " + + source.getClass() + " to type " + type, e); } - throw new ObjectMappingException("Failed to convert using jackson"); + throw new ObjectMappingException("Can't convert object to type " + type + + ", try registering a jackson Module for it."); } - @Override - public boolean isValueType(Class type) { - if (super.isValueType(type)) return true; - if (moduleTypes.contains(type)) return true; - if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) return false; - Object item = ObjectUtils.newInstance(type, false); - return isValue(item); - } - - @Override - public boolean isValue(Object object) { - try { - JsonNode node = objectMapper.convertValue(object, JsonNode.class); - return node != null && node.isValueNode(); - } catch (Exception ex) { - throw new ObjectMappingException("Error while checking for value type", ex); - } - } @Override public void initialize(NitriteConfig nitriteConfig) { - } - @Override protected Target convertFromDocument(Document source, Class type) { try { - return super.convertFromDocument(source, type); - } catch (ObjectMappingException ome) { - try { - return objectMapper.convertValue(source, type); - } catch (IllegalArgumentException iae) { - log.error("Error while converting document to object ", iae); - if (iae.getCause() instanceof JsonMappingException) { - JsonMappingException jme = (JsonMappingException) iae.getCause(); - if (jme.getMessage().contains("Cannot construct instance")) { - throw new ObjectMappingException(jme.getMessage()); - } + return getObjectMapper().convertValue(source, type); + } catch (IllegalArgumentException iae) { + if (iae.getCause() instanceof JsonMappingException) { + JsonMappingException jme = (JsonMappingException) iae.getCause(); + if (jme.getMessage().contains("Cannot construct instance")) { + throw new ObjectMappingException(jme.getMessage()); } - throw iae; } + throw iae; } } - @Override protected Document convertToDocument(Source source) { - try { - return super.convertToDocument(source); - } catch (ObjectMappingException ome) { - JsonNode node = objectMapper.convertValue(source, JsonNode.class); - return readDocument(node); - } + JsonNode node = getObjectMapper().convertValue(source, JsonNode.class); + return readDocument(node); } - private void loadJacksonExtension(JacksonExtension jacksonExtension, ObjectMapper objectMapper) { - for (Class dataType : jacksonExtension.getSupportedTypes()) { - addValueType(dataType); - } - objectMapper.registerModule(jacksonExtension.getModule()); - } - - private Object convertValue(Object object) { - JsonNode node = objectMapper.convertValue(object, JsonNode.class); - if (node == null) { - return null; - } - + @SuppressWarnings("unchecked") + private T getNodeValue(JsonNode node) { switch (node.getNodeType()) { case NUMBER: - return node.numberValue(); + return (T) node.numberValue(); case STRING: - return node.textValue(); + return (T) node.textValue(); case BOOLEAN: - return node.booleanValue(); - case ARRAY: - case BINARY: - case MISSING: - case NULL: - case OBJECT: - case POJO: + return (T) Boolean.valueOf(node.booleanValue()); default: return null; } diff --git a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapperModule.java b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapperModule.java index 9fe5f54bd..7b7d6d55d 100644 --- a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapperModule.java +++ b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonMapperModule.java @@ -16,6 +16,7 @@ package org.dizitart.no2.common.mapper; +import com.fasterxml.jackson.databind.Module; import org.dizitart.no2.common.module.NitriteModule; import org.dizitart.no2.common.module.NitritePlugin; @@ -33,8 +34,11 @@ public JacksonMapperModule() { jacksonMapper = new JacksonMapper(); } - public JacksonMapperModule(JacksonExtension... jacksonExtensions) { - jacksonMapper = new JacksonMapper(jacksonExtensions); + public JacksonMapperModule(Module... jacksonModules) { + jacksonMapper = new JacksonMapper(); + for (Module jacksonModule : jacksonModules) { + jacksonMapper.registerJacksonModule(jacksonModule); + } } @Override diff --git a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdExtension.java b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdExtension.java deleted file mode 100644 index 6523a9991..000000000 --- a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdExtension.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2019-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.common.mapper.extensions; - -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.module.SimpleModule; -import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.common.mapper.JacksonExtension; - -import java.util.List; - -import static org.dizitart.no2.common.util.Iterables.listOf; - -/** - * Class that registers capability of serializing {@code NitriteId} with the Jackson core. - * - * @author Anindya Chatterjee - * @since 1.0.0 - */ -public class NitriteIdExtension implements JacksonExtension { - - @Override - public List> getSupportedTypes() { - return listOf(NitriteId.class); - } - - @Override - public Module getModule() { - return new SimpleModule() { - @Override - public void setupModule(SetupContext context) { - addSerializer(NitriteId.class, new NitriteIdSerializer()); - addDeserializer(NitriteId.class, new NitriteIdDeserializer()); - super.setupModule(context); - } - }; - } -} diff --git a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdDeserializer.java b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdDeserializer.java similarity index 96% rename from nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdDeserializer.java rename to nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdDeserializer.java index f69a97aac..ca07c4c6f 100644 --- a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdDeserializer.java +++ b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdDeserializer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.dizitart.no2.common.mapper.extensions; +package org.dizitart.no2.common.mapper.modules; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; diff --git a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdModule.java b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdModule.java new file mode 100644 index 000000000..2c0c451e7 --- /dev/null +++ b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdModule.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019-2020. Nitrite author or authors. + * + * 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 org.dizitart.no2.common.mapper.modules; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.dizitart.no2.collection.NitriteId; + +/** + * Class that registers capability of serializing {@link NitriteId} with the Jackson core. + * + * @author Anindya Chatterjee + * @since 1.0.0 + */ +public class NitriteIdModule extends SimpleModule { + + @Override + public void setupModule(SetupContext context) { + addSerializer(NitriteId.class, new NitriteIdSerializer()); + addDeserializer(NitriteId.class, new NitriteIdDeserializer()); + super.setupModule(context); + } +} diff --git a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdSerializer.java b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdSerializer.java similarity index 96% rename from nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdSerializer.java rename to nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdSerializer.java index 2262db37d..1b6b34e2b 100644 --- a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/extensions/NitriteIdSerializer.java +++ b/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/modules/NitriteIdSerializer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.dizitart.no2.common.mapper.extensions; +package org.dizitart.no2.common.mapper.modules; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/JacksonMapperTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/JacksonMapperTest.java index 72eec9f15..21ec94a0b 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/JacksonMapperTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/JacksonMapperTest.java @@ -18,8 +18,6 @@ package org.dizitart.no2.common.mapper; import com.fasterxml.jackson.databind.DeserializationConfig; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.cfg.ContextAttributes; import com.fasterxml.jackson.databind.introspect.VisibilityChecker; @@ -29,8 +27,7 @@ import com.fasterxml.jackson.databind.node.BinaryNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import org.dizitart.no2.NitriteConfig; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.extensions.NitriteIdExtension; +import org.dizitart.no2.common.mapper.modules.NitriteIdModule; import org.dizitart.no2.exceptions.ObjectMappingException; import org.junit.Test; @@ -80,10 +77,13 @@ public void testConstructor() { @Test public void testConstructor2() { - NitriteIdExtension nitriteIdExtension = new NitriteIdExtension(); - NitriteIdExtension nitriteIdExtension1 = new NitriteIdExtension(); - ObjectMapper objectMapper = (new JacksonMapper(nitriteIdExtension, nitriteIdExtension1, new NitriteIdExtension())) - .getObjectMapper(); + NitriteIdModule nitriteIdModule = new NitriteIdModule(); + NitriteIdModule nitriteIdModule1 = new NitriteIdModule(); + JacksonMapper jacksonMapper = new JacksonMapper(); + jacksonMapper.registerJacksonModule(nitriteIdModule); + jacksonMapper.registerJacksonModule(nitriteIdModule1); + jacksonMapper.registerJacksonModule(new NitriteIdModule()); + ObjectMapper objectMapper = jacksonMapper.getObjectMapper(); PolymorphicTypeValidator polymorphicTypeValidator = objectMapper.getPolymorphicTypeValidator(); assertTrue(polymorphicTypeValidator instanceof LaissezFaireSubTypeValidator); VisibilityChecker visibilityChecker = objectMapper.getVisibilityChecker(); @@ -120,7 +120,9 @@ public void testConstructor2() { @Test public void testConstructor3() { - ObjectMapper objectMapper = (new JacksonMapper(new NitriteIdExtension())).getObjectMapper(); + JacksonMapper jacksonMapper = new JacksonMapper(); + jacksonMapper.registerJacksonModule(new NitriteIdModule()); + ObjectMapper objectMapper = jacksonMapper.getObjectMapper(); PolymorphicTypeValidator polymorphicTypeValidator = objectMapper.getPolymorphicTypeValidator(); assertTrue(polymorphicTypeValidator instanceof LaissezFaireSubTypeValidator); VisibilityChecker visibilityChecker = objectMapper.getVisibilityChecker(); @@ -156,8 +158,8 @@ public void testConstructor3() { } @Test - public void testCreateObjectMapper() { - ObjectMapper actualCreateObjectMapperResult = (new JacksonMapper()).createObjectMapper(); + public void testGetObjectMapper() { + ObjectMapper actualCreateObjectMapperResult = (new JacksonMapper()).getObjectMapper(); PolymorphicTypeValidator polymorphicTypeValidator = actualCreateObjectMapperResult.getPolymorphicTypeValidator(); assertTrue(polymorphicTypeValidator instanceof LaissezFaireSubTypeValidator); VisibilityChecker visibilityChecker = actualCreateObjectMapperResult.getVisibilityChecker(); @@ -202,15 +204,6 @@ public void testConvert() { .getSerializerProviderInstance() instanceof com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.Impl); } - @Test - public void testConvert2() { - JacksonMapper jacksonMapper = new JacksonMapper(); - jacksonMapper.addValueType(Object.class); - assertEquals("Source", jacksonMapper.convert("Source", Object.class)); - assertTrue(jacksonMapper.getObjectMapper() - .getSerializerProviderInstance() instanceof com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.Impl); - } - @Test public void testConvert3() { JacksonMapper jacksonMapper = new JacksonMapper(); @@ -239,53 +232,6 @@ public void testConvert6() throws UnsupportedEncodingException { assertNull(jacksonMapper.convert(source, Object.class)); } - @Test - public void testIsValueType() { - JacksonMapper jacksonMapper = new JacksonMapper(); - assertThrows(ObjectMappingException.class, () -> jacksonMapper.isValueType(Object.class)); - } - - @Test - public void testIsValueType2() { - JacksonMapper jacksonMapper = new JacksonMapper(); - jacksonMapper.addValueType(Object.class); - assertTrue(jacksonMapper.isValueType(Object.class)); - } - - @Test - public void testIsValueType3() { - JacksonMapper jacksonMapper = new JacksonMapper(); - assertFalse(jacksonMapper.isValueType(JsonMappingException.class)); - assertTrue(jacksonMapper.getObjectMapper() - .getSerializerProviderInstance() instanceof com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.Impl); - } - - @Test - public void testIsValueType4() { - JacksonMapper jacksonMapper = new JacksonMapper(); - assertFalse(jacksonMapper.isValueType(JsonNode.class)); - } - - @Test - public void testIsValueType5() { - JacksonMapper jacksonMapper = new JacksonMapper(); - assertFalse(jacksonMapper.isValueType(Document.class)); - } - - @Test - public void testIsValue() { - JacksonMapper jacksonMapper = new JacksonMapper(); - assertTrue(jacksonMapper.isValue("Object")); - assertTrue(jacksonMapper.getObjectMapper() - .getSerializerProviderInstance() instanceof com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.Impl); - } - - @Test - public void testIsValue2() { - JacksonMapper jacksonMapper = new JacksonMapper(); - assertFalse(jacksonMapper.isValue(new ArrayNode(new JsonNodeFactory(true)))); - } - @Test public void testInitialize() { JacksonMapper jacksonMapper = new JacksonMapper(); diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdDeserializerTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/modules/NitriteIdDeserializerTest.java similarity index 95% rename from nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdDeserializerTest.java rename to nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/modules/NitriteIdDeserializerTest.java index 9aceea44e..7528e79a1 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdDeserializerTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/modules/NitriteIdDeserializerTest.java @@ -15,7 +15,7 @@ * */ -package org.dizitart.no2.common.mapper.extensions; +package org.dizitart.no2.common.mapper.modules; import org.dizitart.no2.collection.NitriteId; import org.junit.Test; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdSerializerTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/modules/NitriteIdSerializerTest.java similarity index 95% rename from nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdSerializerTest.java rename to nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/modules/NitriteIdSerializerTest.java index a1b7f61eb..54030b1ff 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdSerializerTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/modules/NitriteIdSerializerTest.java @@ -15,7 +15,7 @@ * */ -package org.dizitart.no2.common.mapper.extensions; +package org.dizitart.no2.common.mapper.modules; import org.junit.Test; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/JacksonModuleTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/JacksonModuleTest.java index 700f007af..b392f06e5 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/JacksonModuleTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/JacksonModuleTest.java @@ -1,11 +1,9 @@ package org.dizitart.no2.integration.repository; -import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import lombok.Data; import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; -import org.dizitart.no2.common.mapper.JacksonExtension; import org.dizitart.no2.common.mapper.JacksonMapperModule; import org.dizitart.no2.exceptions.ObjectMappingException; import org.dizitart.no2.mvstore.MVStoreModule; @@ -18,10 +16,8 @@ import java.nio.file.Paths; import java.time.Duration; import java.time.LocalDateTime; -import java.util.List; import java.util.UUID; -import static org.dizitart.no2.common.util.Iterables.listOf; import static org.dizitart.no2.integration.repository.BaseObjectRepositoryTest.getRandomTempDbFile; import static org.junit.Assert.assertEquals; @@ -75,7 +71,7 @@ public void testJavaTimeModule() { .build(); NitriteBuilder nitriteBuilder = Nitrite.builder() - .loadModule(new JacksonMapperModule(new JavaTimeExtension())) + .loadModule(new JacksonMapperModule(new JavaTimeModule())) .fieldSeparator(".") .loadModule(storeModule); @@ -102,17 +98,4 @@ private static class TestData { private LocalDateTime localDateTime; private Duration duration; } - - public static class JavaTimeExtension implements JacksonExtension { - - @Override - public List> getSupportedTypes() { - return listOf(LocalDateTime.class, Duration.class); - } - - @Override - public Module getModule() { - return new JavaTimeModule(); - } - } } diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectCursorTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectCursorTest.java index 132746f0e..4f9aa507c 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectCursorTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectCursorTest.java @@ -19,8 +19,8 @@ import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.exceptions.ValidationException; -import org.dizitart.no2.repository.Cursor; import org.dizitart.no2.integration.repository.data.Employee; +import org.dizitart.no2.repository.Cursor; import org.junit.Test; import java.util.AbstractCollection; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java index 48307a3bd..9e070c839 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java @@ -157,7 +157,7 @@ public void testRemoveNoId() { repository.remove(object); } - @Test(expected = ValidationException.class) + @Test(expected = ObjectMappingException.class) public void testProjectionFailedInstantiate() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index e3da6b307..dcf439b54 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -63,7 +63,7 @@ public void testNullType() { RepositoryFactory factory = new RepositoryFactory(new CollectionFactory(new LockService())); JacksonMapper mapper = new JacksonMapper(); db = TestUtil.createDb(fileName, NitriteModule.module(mapper)); - factory.getRepository(db.getConfig(), null, "dummy"); + factory.getRepository(db.getConfig(), (Class) null, "dummy"); } @Test diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Note.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Note.java index 647776b0f..68942f3c3 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Note.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Note.java @@ -20,9 +20,6 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; import java.io.Serializable; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java index d49a599cf..e6e077671 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java @@ -27,8 +27,9 @@ import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.concurrent.ThreadPoolManager; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexOptions; @@ -84,6 +85,10 @@ public class NitriteTest { public void setUp() throws ParseException { db = TestUtil.createDb(fileName, "test-user", "test-password"); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new CompatChild.Converter()); + documentMapper.registerEntityConverter(new Receipt.Converter()); + simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH); Document doc1 = createDocument("firstName", "fn1") @@ -280,7 +285,7 @@ public void testMultipleGetRepository() { @Test(expected = ValidationException.class) public void testGetRepositoryInvalid() { - db.getRepository(null); + db.getRepository((Class) null); } @Test(expected = NitriteIOException.class) @@ -464,10 +469,12 @@ public void testReadCompatibility() throws IOException { String oldDbFile = System.getProperty("java.io.tmpdir") + File.separator + "old.db"; Nitrite db = TestUtil.createDb(oldDbFile, "test-user", "test-password"); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new Receipt.Converter()); NitriteCollection collection = db.getCollection("test"); - // text filter has be the first filter in and clause + // text filter has been the first filter in and clause List cursor = collection.find( and(where("second_key").text("fox"), where("first_key").eq(1))).toList(); assertEquals(cursor.size(), 1); @@ -565,20 +572,30 @@ public void run() { @Data @AllArgsConstructor @NoArgsConstructor - public static class CompatChild implements Mappable { + public static class CompatChild { private Long childId; private String lastName; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("childId", childId) - .put("lastName", lastName); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return CompatChild.class; + } - @Override - public void read(NitriteMapper mapper, Document document) { - childId = document.get("childId", Long.class); - lastName = document.get("lastName", String.class); + @Override + public Document toDocument(CompatChild entity, NitriteMapper nitriteMapper) { + return Document.createDocument("childId", entity.childId) + .put("lastName", entity.lastName); + } + + @Override + public CompatChild fromDocument(Document document, NitriteMapper nitriteMapper) { + CompatChild entity = new CompatChild(); + entity.childId = document.get("childId", Long.class); + entity.lastName = document.get("lastName", String.class); + return entity; + } } } @@ -588,33 +605,43 @@ public void read(NitriteMapper mapper, Document document) { @Indices({ @Index(value = "synced", type = IndexType.NON_UNIQUE) }) - public static class Receipt implements Mappable { + public static class Receipt { @Id private String clientRef; private Boolean synced; private Status status; private Long createdTimestamp = System.currentTimeMillis(); - @Override - public Document write(NitriteMapper mapper) { - return createDocument("status", status) - .put("clientRef", clientRef) - .put("synced", synced) - .put("createdTimestamp", createdTimestamp); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - Object status = document.get("status"); - if (status instanceof Status) { - this.status = (Status) status; - } else { - this.status = Status.valueOf(status.toString()); + @Override + public Class getEntityType() { + return Receipt.class; + } + + @Override + public Document toDocument(Receipt entity, NitriteMapper nitriteMapper) { + return createDocument("status", entity.status) + .put("clientRef", entity.clientRef) + .put("synced", entity.synced) + .put("createdTimestamp", entity.createdTimestamp); + } + + @Override + public Receipt fromDocument(Document document, NitriteMapper nitriteMapper) { + Receipt receipt = new Receipt(); + if (document != null) { + Object status = document.get("status"); + if (status instanceof Status) { + receipt.status = (Status) status; + } else { + receipt.status = Status.valueOf(status.toString()); + } + receipt.clientRef = document.get("clientRef", String.class); + receipt.synced = document.get("synced", Boolean.class); + receipt.createdTimestamp = document.get("createdTimestamp", Long.class); } - this.clientRef = document.get("clientRef", String.class); - this.synced = document.get("synced", Boolean.class); - this.createdTimestamp = document.get("createdTimestamp", Long.class); + return receipt; } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java index fc5457ee5..7339b791b 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java @@ -26,8 +26,9 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.FieldValues; import org.dizitart.no2.common.Fields; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.NitriteSecurityException; @@ -193,6 +194,10 @@ public void testPopulateRepositories() { .loadModule(module) .openOrCreate(); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TestObject.Converter()); + documentMapper.registerEntityConverter(new TestObject2.Converter()); + NitriteCollection collection = db.getCollection("test"); collection.insert(createDocument("id1", "value")); @@ -368,16 +373,6 @@ public Target convert(Source source, Class type) { return null; } - @Override - public boolean isValueType(Class type) { - return false; - } - - @Override - public boolean isValue(Object object) { - return false; - } - @Override public void initialize(NitriteConfig nitriteConfig) { @@ -385,7 +380,7 @@ public void initialize(NitriteConfig nitriteConfig) { } @Index(value = "longValue") - private static class TestObject implements Mappable { + private static class TestObject { private String stringValue; private Long longValue; @@ -397,23 +392,29 @@ public TestObject(String stringValue, Long longValue) { this.stringValue = stringValue; } - @Override - public Document write(NitriteMapper mapper) { - return createDocument("stringValue", stringValue) - .put("longValue", longValue); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - this.stringValue = document.get("stringValue", String.class); - this.longValue = document.get("longValue", Long.class); + @Override + public Class getEntityType() { + return TestObject.class; + } + + @Override + public Document toDocument(TestObject entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("stringValue", entity.stringValue) + .put("longValue", entity.longValue); + } + + @Override + public TestObject fromDocument(Document document, NitriteMapper nitriteMapper) { + return null; } } } @Index(value = "longValue") - private static class TestObject2 implements Mappable { + private static class TestObject2 { private String stringValue; private Long longValue; @@ -425,17 +426,27 @@ public TestObject2(String stringValue, Long longValue) { this.stringValue = stringValue; } - @Override - public Document write(NitriteMapper mapper) { - return createDocument("stringValue", stringValue) - .put("longValue", longValue); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - this.stringValue = document.get("stringValue", String.class); - this.longValue = document.get("longValue", Long.class); + @Override + public Class getEntityType() { + return TestObject2.class; + } + + @Override + public Document toDocument(TestObject2 entity, NitriteMapper nitriteMapper) { + return createDocument("stringValue", entity.stringValue) + .put("longValue", entity.longValue); + } + + @Override + public TestObject2 fromDocument(Document document, NitriteMapper nitriteMapper) { + TestObject2 entity = new TestObject2(); + if (document != null) { + entity.stringValue = document.get("stringValue", String.class); + entity.longValue = document.get("longValue", Long.class); + } + return entity; } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java index 19c4a830a..887419d44 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java @@ -24,8 +24,9 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; @@ -66,6 +67,11 @@ public class NitriteStressTest { @Before public void before() { db = createDb(fileName); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TestDto.Converter()); + documentMapper.registerEntityConverter(new PerfTest.Converter()); + documentMapper.registerEntityConverter(new PerfTestIndexed.Converter()); + collection = db.getCollection("test"); System.out.println(fileName); } @@ -84,6 +90,8 @@ public void cleanUp() { @Test public void stressTest() { ObjectRepository testRepository = db.getRepository(TestDto.class); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TestDto.Converter()); testRepository.createIndex(IndexOptions.indexOptions(IndexType.FULL_TEXT), "lastName"); testRepository.createIndex(IndexOptions.indexOptions(IndexType.NON_UNIQUE), "birthDate"); @@ -220,7 +228,7 @@ private List getItems(Class type) { } @Data - public static class TestDto implements Mappable { + public static class TestDto { @XmlElement( name = "StudentNumber", @@ -268,53 +276,73 @@ public static class TestDto implements Mappable { public TestDto() { } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("studentNumber", studentNumber) - .put("lastName", lastName) - .put("prefixes", prefixes) - .put("initials", initials) - .put("firstNames", firstNames) - .put("nickName", nickName) - .put("birthDate", birthDate); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TestDto.class; + } + + @Override + public Document toDocument(TestDto entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("studentNumber", entity.studentNumber) + .put("lastName", entity.lastName) + .put("prefixes", entity.prefixes) + .put("initials", entity.initials) + .put("firstNames", entity.firstNames) + .put("nickName", entity.nickName) + .put("birthDate", entity.birthDate); + } - @Override - public void read(NitriteMapper mapper, Document document) { - studentNumber = document.get("studentNumber", String.class); - lastName = document.get("lastName", String.class); - prefixes = document.get("prefixes", String.class); - initials = document.get("initials", String.class); - firstNames = document.get("firstNames", String.class); - nickName = document.get("nickName", String.class); - birthDate = document.get("birthDate", String.class); + @Override + public TestDto fromDocument(Document document, NitriteMapper nitriteMapper) { + TestDto entity = new TestDto(); + entity.studentNumber = document.get("studentNumber", String.class); + entity.lastName = document.get("lastName", String.class); + entity.prefixes = document.get("prefixes", String.class); + entity.initials = document.get("initials", String.class); + entity.firstNames = document.get("firstNames", String.class); + entity.nickName = document.get("nickName", String.class); + entity.birthDate = document.get("birthDate", String.class); + return entity; + } } } @Data - public static class PerfTest implements Mappable { + public static class PerfTest { private String firstName; private String lastName; private Integer age; private String text; - @Override - public Document write(NitriteMapper mapper) { - Document document = Document.createDocument(); - document.put("firstName", firstName); - document.put("lastName", lastName); - document.put("age", age); - document.put("text", text); - return document; - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - this.firstName = (String) document.get("firstName"); - this.lastName = (String) document.get("lastName"); - this.age = (Integer) document.get("age"); - this.text = (String) document.get("text"); + @Override + public Class getEntityType() { + return PerfTest.class; + } + + @Override + public Document toDocument(PerfTest entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + document.put("firstName", entity.firstName); + document.put("lastName", entity.lastName); + document.put("age", entity.age); + document.put("text", entity.text); + return document; + } + + @Override + public PerfTest fromDocument(Document document, NitriteMapper nitriteMapper) { + PerfTest entity = new PerfTest(); + entity.firstName = (String) document.get("firstName"); + entity.lastName = (String) document.get("lastName"); + entity.age = (Integer) document.get("age"); + entity.text = (String) document.get("text"); + return entity; + } } } @@ -324,5 +352,33 @@ public void read(NitriteMapper mapper, Document document) { @Index(value = "text", type = IndexType.FULL_TEXT), }) private static class PerfTestIndexed extends PerfTest { + + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PerfTestIndexed.class; + } + + @Override + public Document toDocument(PerfTestIndexed entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + document.put("firstName", entity.getFirstName()); + document.put("lastName", entity.getLastName()); + document.put("age", entity.getAge()); + document.put("text", entity.getText()); + return document; + } + + @Override + public PerfTestIndexed fromDocument(Document document, NitriteMapper nitriteMapper) { + PerfTestIndexed entity = new PerfTestIndexed(); + entity.setFirstName((String) document.get("firstName")); + entity.setLastName((String) document.get("lastName")); + entity.setAge((Integer) document.get("age")); + entity.setText((String) document.get("text")); + return entity; + } + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java index 8d446cf0f..71a97a42d 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java @@ -21,6 +21,7 @@ import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.exceptions.FilterException; +import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; import org.junit.Test; @@ -61,7 +62,7 @@ public void testFindOptionsInvalidOffset() { assertEquals(collection.find(skipBy(10).limit(1)).size(), 0); } - @Test(expected = ValidationException.class) + @Test(expected = InvalidOperationException.class) public void testFindInvalidSort() { insert(); collection.find(orderBy("data", SortOrder.Descending)).toList(); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java index 247a1705a..4a45fa055 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java @@ -188,7 +188,7 @@ public void testRebuildIndex() { insert(); Collection indices = collection.listIndices(); for (IndexDescriptor idx : indices) { - collection.rebuildIndex(idx.getIndexFields().getFieldNames().toArray(new String[0])); + collection.rebuildIndex(idx.getFields().getFieldNames().toArray(new String[0])); } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java index fe37845e1..a59e7e459 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java @@ -134,7 +134,7 @@ public void testRebuildIndex() { insert(); Collection indices = collection.listIndices(); for (IndexDescriptor idx : indices) { - collection.rebuildIndex(idx.getIndexFields().getFieldNames().toArray(new String[0])); + collection.rebuildIndex(idx.getFields().getFieldNames().toArray(new String[0])); } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java index 2bfd51c5a..d9b4e5816 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java @@ -18,6 +18,7 @@ package org.dizitart.no2.integration.event; import org.dizitart.no2.collection.UpdateOptions; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.Employee; import org.dizitart.no2.Nitrite; @@ -119,6 +120,9 @@ public void setUp() { db = nitriteBuilder.openOrCreate(); } + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + employeeRepository = db.getRepository(Employee.class); listener = new SampleListenerCollection(); employeeRepository.subscribe(listener); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java index a0787f3a9..5c71e08d6 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java @@ -18,6 +18,7 @@ package org.dizitart.no2.integration.migration; import com.github.javafaker.Faker; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; @@ -57,6 +58,10 @@ public class MigrationTest { @Before public void setUp() { db = createDb(dbPath); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new OldClass.Converter()); + documentMapper.registerEntityConverter(new OldClass.Literature.Converter()); + faker = new Faker(); } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java index 3ef9090ea..1de57ca4f 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java @@ -19,9 +19,9 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Entity; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -35,7 +35,7 @@ @Index(value = "fullName", type = IndexType.NON_UNIQUE), @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), }) -public class NewClass implements Mappable { +public class NewClass { @Id private Long empId; private String firstName; @@ -43,42 +43,61 @@ public class NewClass implements Mappable { private String fullName; private Literature literature; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("empId", empId) - .put("firstName", firstName) - .put("familyName", familyName) - .put("fullName", fullName) - .put("literature", literature.write(mapper)); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return NewClass.class; + } + + @Override + public Document toDocument(NewClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument("empId", entity.empId) + .put("firstName", entity.firstName) + .put("familyName", entity.familyName) + .put("fullName", entity.fullName) + .put("literature", nitriteMapper.convert(entity.literature, Document.class)); + } - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - firstName = document.get("firstName", String.class); - familyName = document.get("familyName", String.class); - fullName = document.get("fullName", String.class); + @Override + public NewClass fromDocument(Document document, NitriteMapper nitriteMapper) { + NewClass entity = new NewClass(); + entity.empId = document.get("empId", Long.class); + entity.firstName = document.get("firstName", String.class); + entity.familyName = document.get("familyName", String.class); + entity.fullName = document.get("fullName", String.class); - Document doc = document.get("literature", Document.class); - literature = new Literature(); - literature.read(mapper, doc); + Document doc = document.get("literature", Document.class); + entity.literature = nitriteMapper.convert(doc, Literature.class); + return entity; + } } @Data - public static class Literature implements Mappable { + public static class Literature { private String text; private Integer ratings; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("text", text) - .put("ratings", ratings); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - text = document.get("text", String.class); - ratings = document.get("ratings", Integer.class); + @Override + public Class getEntityType() { + return Literature.class; + } + + @Override + public Document toDocument(Literature entity, NitriteMapper nitriteMapper) { + return Document.createDocument("text", entity.text) + .put("ratings", entity.ratings); + } + + @Override + public Literature fromDocument(Document document, NitriteMapper nitriteMapper) { + Literature entity = new Literature(); + entity.text = document.get("text", String.class); + entity.ratings = document.get("ratings", Integer.class); + return entity; + } } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java index 0f2e7669c..8c1142508 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java @@ -19,9 +19,9 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Entity; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -36,7 +36,7 @@ @Index(value = "literature.text", type = IndexType.FULL_TEXT), @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), }) -public class OldClass implements Mappable { +public class OldClass { @Id private String uuid; private String empId; @@ -44,42 +44,61 @@ public class OldClass implements Mappable { private String lastName; private Literature literature; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("empId", empId) - .put("uuid", uuid) - .put("firstName", firstName) - .put("lastName", lastName) - .put("literature", literature.write(mapper)); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return OldClass.class; + } + + @Override + public Document toDocument(OldClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument("empId", entity.empId) + .put("uuid", entity.uuid) + .put("firstName", entity.firstName) + .put("lastName", entity.lastName) + .put("literature", nitriteMapper.convert(entity.literature, Document.class)); + } - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", String.class); - uuid = document.get("uuid", String.class); - firstName = document.get("firstName", String.class); - lastName = document.get("lastName", String.class); + @Override + public OldClass fromDocument(Document document, NitriteMapper nitriteMapper) { + OldClass entity = new OldClass(); + entity.empId = document.get("empId", String.class); + entity.uuid = document.get("uuid", String.class); + entity.firstName = document.get("firstName", String.class); + entity.lastName = document.get("lastName", String.class); - Document doc = document.get("literature", Document.class); - literature = new Literature(); - literature.read(mapper, doc); + Document doc = document.get("literature", Document.class); + entity.literature = nitriteMapper.convert(doc, Literature.class); + return entity; + } } @Data - public static class Literature implements Mappable { + public static class Literature { private String text; private Float ratings; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("text", text) - .put("ratings", ratings); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - text = document.get("text", String.class); - ratings = document.get("ratings", Float.class); + @Override + public Class getEntityType() { + return Literature.class; + } + + @Override + public Document toDocument(Literature entity, NitriteMapper nitriteMapper) { + return Document.createDocument("text", entity.text) + .put("ratings", entity.ratings); + } + + @Override + public Literature fromDocument(Document document, NitriteMapper nitriteMapper) { + Literature entity = new Literature(); + entity.text = document.get("text", String.class); + entity.ratings = document.get("ratings", Float.class); + return entity; + } } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java index 2d33d0c43..f279620f0 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java @@ -17,10 +17,16 @@ package org.dizitart.no2.integration.repository; -import org.dizitart.no2.integration.Retry; -import org.dizitart.no2.integration.repository.data.*; import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; +import org.dizitart.no2.integration.Retry; +import org.dizitart.no2.integration.repository.data.*; +import org.dizitart.no2.integration.repository.decorator.ManufacturerConverter; +import org.dizitart.no2.integration.repository.decorator.MiniProduct; +import org.dizitart.no2.integration.repository.decorator.ProductConverter; +import org.dizitart.no2.integration.repository.decorator.ProductIdConverter; +import org.dizitart.no2.integration.transaction.TxData; import org.dizitart.no2.mvstore.MVStoreModule; import org.dizitart.no2.mvstore.MVStoreModuleBuilder; import org.dizitart.no2.repository.ObjectRepository; @@ -135,6 +141,33 @@ protected void openDb() { } else { db = nitriteBuilder.openOrCreate(); } + + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new RepositoryJoinTest.Person.Converter()); + documentMapper.registerEntityConverter(new RepositoryJoinTest.Address.Converter()); + documentMapper.registerEntityConverter(new RepositoryJoinTest.PersonDetails.Converter()); + documentMapper.registerEntityConverter(new Company.CompanyConverter()); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + documentMapper.registerEntityConverter(new Note.NoteConverter()); + documentMapper.registerEntityConverter(new Book.BookConverter()); + documentMapper.registerEntityConverter(new BookId.BookIdConverter()); + documentMapper.registerEntityConverter(new ClassA.ClassAConverter()); + documentMapper.registerEntityConverter(new ClassBConverter()); + documentMapper.registerEntityConverter(new ClassC.ClassCConverter()); + documentMapper.registerEntityConverter(new ElemMatch.Converter()); + documentMapper.registerEntityConverter(new InternalClass.Converter()); + documentMapper.registerEntityConverter(new UniversalTextTokenizerTest.TextData.Converter()); + documentMapper.registerEntityConverter(new SubEmployee.Converter()); + documentMapper.registerEntityConverter(new ProductScore.Converter()); + documentMapper.registerEntityConverter(new PersonEntity.Converter()); + documentMapper.registerEntityConverter(new RepeatableIndexTest.Converter()); + documentMapper.registerEntityConverter(new EncryptedPerson.Converter()); + documentMapper.registerEntityConverter(new TxData.Converter()); + documentMapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); + documentMapper.registerEntityConverter(new ProductConverter()); + documentMapper.registerEntityConverter(new ProductIdConverter()); + documentMapper.registerEntityConverter(new ManufacturerConverter()); + documentMapper.registerEntityConverter(new MiniProduct.Converter()); } @After diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java index 965a8d8ab..54fc77286 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java @@ -21,6 +21,8 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.Company; import org.dizitart.no2.integration.repository.data.Note; @@ -28,7 +30,6 @@ import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.Document; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.mvstore.MVStoreModule; import org.dizitart.no2.repository.ObjectRepository; @@ -71,6 +72,11 @@ public void setUp() { .fieldSeparator(":") .openOrCreate(); + SimpleDocumentMapper mapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + mapper.registerEntityConverter(new Company.CompanyConverter()); + mapper.registerEntityConverter(new EmployeeForCustomSeparator.EmployeeForCustomSeparatorConverter()); + mapper.registerEntityConverter(new Note.NoteConverter()); + repository = db.getRepository(EmployeeForCustomSeparator.class); } @@ -122,7 +128,7 @@ public void testFindByEmbeddedField() { @Index(value = "address", type = IndexType.FULL_TEXT), @Index(value = "employeeNote:text", type = IndexType.FULL_TEXT) }) - public static class EmployeeForCustomSeparator implements Serializable, Mappable { + public static class EmployeeForCustomSeparator implements Serializable { @Id @Getter @Setter @@ -160,28 +166,39 @@ public EmployeeForCustomSeparator(EmployeeForCustomSeparator copy) { employeeNote = copy.employeeNote; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("empId", empId) - .put("joinDate", joinDate) - .put("address", address) - .put("blob", blob) - .put("company", company.write(mapper)) - .put("employeeNote", employeeNote.write(mapper)); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); - blob = document.get("blob", byte[].class); - employeeNote = new Note(); - Document doc = document.get("employeeNote", Document.class); - employeeNote.read(mapper, doc); - company = new Company(); - doc = document.get("company", Document.class); - company.read(mapper, doc); + public static class EmployeeForCustomSeparatorConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmployeeForCustomSeparator.class; + } + + @Override + public Document toDocument(EmployeeForCustomSeparator entity, NitriteMapper nitriteMapper) { + return Document.createDocument().put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address) + .put("blob", entity.blob) + .put("company", nitriteMapper.convert(entity.company, Document.class)) + .put("employeeNote", nitriteMapper.convert(entity.employeeNote, Document.class)); + } + + @Override + public EmployeeForCustomSeparator fromDocument(Document document, NitriteMapper nitriteMapper) { + EmployeeForCustomSeparator entity = new EmployeeForCustomSeparator(); + + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + entity.blob = document.get("blob", byte[].class); + + Document doc = document.get("employeeNote", Document.class); + entity.employeeNote = nitriteMapper.convert(doc, Note.class); + + doc = document.get("company", Document.class); + entity.company = nitriteMapper.convert(doc, Company.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java index 1a52708ce..7096ddaaf 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -27,20 +27,29 @@ * @author Anindya Chatterjee. */ @Data -class InternalClass implements Mappable { +class InternalClass { @Id private long id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("name", name); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return InternalClass.class; + } + + @Override + public Document toDocument(InternalClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - name = document.get("name", String.class); + @Override + public InternalClass fromDocument(Document document, NitriteMapper nitriteMapper) { + InternalClass entity = new InternalClass(); + entity.id = document.get("id", Long.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java index dedd5e256..278977722 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java @@ -17,6 +17,7 @@ package org.dizitart.no2.integration.repository; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.WithNitriteId; import org.dizitart.no2.Nitrite; @@ -53,6 +54,9 @@ public class NitriteIdAsIdTest { @Before public void before() { db = TestUtil.createDb(fileName); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); + repo = db.getRepository(WithNitriteId.class); } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java index e070d485d..a34a91078 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java @@ -17,6 +17,7 @@ package org.dizitart.no2.integration.repository; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.*; import org.dizitart.no2.Nitrite; @@ -50,6 +51,18 @@ public class ObjectRepositoryNegativeTest { @Before public void setUp() { db = TestUtil.createDb(dbPath); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new WithPublicField.Converter()); + documentMapper.registerEntityConverter(new WithObjectId.Converter()); + documentMapper.registerEntityConverter(new WithOutId.Converter()); + documentMapper.registerEntityConverter(new WithoutEmbeddedId.Converter()); + documentMapper.registerEntityConverter(new WithoutEmbeddedId.NestedId.Converter()); + documentMapper.registerEntityConverter(new WithEmptyStringId.Converter()); + documentMapper.registerEntityConverter(new WithNullId.Converter()); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + documentMapper.registerEntityConverter(new Company.CompanyConverter()); + documentMapper.registerEntityConverter(new Note.NoteConverter()); + documentMapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); } @After @@ -162,7 +175,7 @@ public void testRemoveNoId() { repository.remove(object); } - @Test(expected = ValidationException.class) + @Test(expected = ObjectMappingException.class) public void testProjectionFailedInstantiate() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 0f9344987..50e8969ab 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -19,17 +19,21 @@ import com.github.javafaker.Faker; import lombok.Data; -import org.dizitart.no2.integration.Retry; -import org.dizitart.no2.integration.repository.data.*; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.common.meta.Attributes; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.MappableMapper; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.integration.Retry; +import org.dizitart.no2.integration.repository.data.*; +import org.dizitart.no2.integration.repository.decorator.ManufacturerConverter; +import org.dizitart.no2.integration.repository.decorator.MiniProduct; +import org.dizitart.no2.integration.repository.decorator.ProductConverter; +import org.dizitart.no2.integration.repository.decorator.ProductIdConverter; import org.dizitart.no2.mvstore.MVStoreModule; import org.dizitart.no2.repository.Cursor; import org.dizitart.no2.repository.ObjectRepository; @@ -66,7 +70,25 @@ public class ObjectRepositoryTest { @Before public void setUp() { - NitriteMapper mapper = new MappableMapper(); + SimpleDocumentMapper mapper = new SimpleDocumentMapper(); + mapper.registerEntityConverter(new InternalClass.Converter()); + mapper.registerEntityConverter(new EmployeeEntity.Converter()); + mapper.registerEntityConverter(new StressRecord.Converter()); + mapper.registerEntityConverter(new WithClassField.Converter()); + mapper.registerEntityConverter(new WithDateId.Converter()); + mapper.registerEntityConverter(new WithTransientField.Converter()); + mapper.registerEntityConverter(new WithOutId.Converter()); + mapper.registerEntityConverter(new ChildClass.Converter()); + mapper.registerEntityConverter(new WithOutGetterSetter.Converter()); + mapper.registerEntityConverter(new WithPrivateConstructor.Converter()); + mapper.registerEntityConverter(new WithPublicField.Converter()); + mapper.registerEntityConverter(new Employee.EmployeeConverter()); + mapper.registerEntityConverter(new Company.CompanyConverter()); + mapper.registerEntityConverter(new ProductConverter()); + mapper.registerEntityConverter(new ProductIdConverter()); + mapper.registerEntityConverter(new ManufacturerConverter()); + mapper.registerEntityConverter(new MiniProduct.Converter()); + MVStoreModule storeModule = MVStoreModule.withConfig() .filePath(dbPath) .build(); @@ -350,7 +372,7 @@ public void testIssue217() { @Index(value = "firstName", type = IndexType.NON_UNIQUE), @Index(value = "lastName", type = IndexType.NON_UNIQUE), }) - private static class EmployeeEntity implements Mappable { + private static class EmployeeEntity { private static final Faker faker = new Faker(); @Id @@ -364,18 +386,28 @@ public EmployeeEntity() { lastName = faker.name().lastName(); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("firstName", firstName) - .put("lastName", lastName); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - firstName = document.get("firstName", String.class); - lastName = document.get("lastName", String.class); + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmployeeEntity.class; + } + + @Override + public Document toDocument(EmployeeEntity entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("firstName", entity.firstName) + .put("lastName", entity.lastName); + } + + @Override + public EmployeeEntity fromDocument(Document document, NitriteMapper nitriteMapper) { + EmployeeEntity entity = new EmployeeEntity(); + entity.id = document.get("id", Long.class); + entity.firstName = document.get("firstName", String.class); + entity.lastName = document.get("lastName", String.class); + return entity; + } } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 695d62f53..85e1dcac1 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -66,7 +66,7 @@ public void testRepositoryFactory() { @Test(expected = ValidationException.class) public void testNullType() { RepositoryFactory factory = new RepositoryFactory(new CollectionFactory(new LockService())); - factory.getRepository(db.getConfig(), null, "dummy"); + factory.getRepository(db.getConfig(), (Class) null, "dummy"); } @Test diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java index 4dacdbff8..05634377f 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java @@ -22,7 +22,7 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.Lookup; import org.dizitart.no2.common.RecordStream; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.repository.ObjectRepository; @@ -31,10 +31,7 @@ import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Set; +import java.util.*; import static org.dizitart.no2.collection.Document.createDocument; import static org.dizitart.no2.collection.FindOptions.skipBy; @@ -138,80 +135,120 @@ public void testRemove() { } @Data - public static class Person implements Mappable { + public static class Person { @Id private NitriteId nitriteId; private String id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("id", id) - .put("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return Person.class; + } + + @Override + public Document toDocument(Person entity, NitriteMapper nitriteMapper) { + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("id", entity.id) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - id = document.get("id", String.class); - name = document.get("name", String.class); + @Override + public Person fromDocument(Document document, NitriteMapper nitriteMapper) { + Person entity = new Person(); + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.id = document.get("id", String.class); + entity.name = document.get("name", String.class); + return entity; + } } } @Data - public static class Address implements Mappable { + public static class Address { @Id private NitriteId nitriteId; private String personId; private String street; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("personId", personId) - .put("street", street); - } + public static class Converter implements EntityConverter
{ + + @Override + public Class
getEntityType() { + return Address.class; + } + + @Override + public Document toDocument(Address entity, NitriteMapper nitriteMapper) { + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("personId", entity.personId) + .put("street", entity.street); + } - @Override - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - personId = document.get("personId", String.class); - street = document.get("street", String.class); + @Override + public Address fromDocument(Document document, NitriteMapper nitriteMapper) { + Address entity = new Address(); + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.personId = document.get("personId", String.class); + entity.street = document.get("street", String.class); + return entity; + } } } @Data - public static class PersonDetails implements Mappable { + public static class PersonDetails { @Id private NitriteId nitriteId; private String id; private String name; private List
addresses; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("personId", id) - .put("street", name) - .put("addresses", addresses); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PersonDetails.class; + } + + @Override + public Document toDocument(PersonDetails entity, NitriteMapper nitriteMapper) { + List documents = new ArrayList<>(); + if (entity.addresses != null) { + for (Address address : entity.addresses) { + documents.add(nitriteMapper.convert(address, Document.class)); + } + } + + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("personId", entity.id) + .put("street", entity.name) + .put("addresses", documents); + } + + @Override + public PersonDetails fromDocument(Document document, NitriteMapper nitriteMapper) { + PersonDetails entity = new PersonDetails(); + + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.id = document.get("id", String.class); + entity.name = document.get("name", String.class); + + Collection documents = document.get("addresses", Collection.class); + if (documents != null) { + entity.addresses = new ArrayList<>(); + for (Document doc : documents) { + Address address = nitriteMapper.convert(doc, Address.class); + entity.addresses.add(address); + } + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - id = document.get("id", String.class); - name = document.get("name", String.class); - Set documents = document.get("addresses", Set.class); - this.addresses = new ArrayList<>(); - for (Document doc : documents) { - Address address = new Address(); - address.read(mapper, doc); - addresses.add(address); + return entity; } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java index 68015c9f3..fd1b959c7 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java @@ -18,10 +18,11 @@ package org.dizitart.no2.integration.repository; import lombok.Getter; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.repository.data.*; import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.SortOrder; -import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.FilterException; import org.dizitart.no2.exceptions.InvalidIdException; @@ -364,21 +365,20 @@ public void testElemMatchFilter() { final ProductScore score6 = new ProductScore("xyz", 8); ObjectRepository repository = db.getRepository(ElemMatch.class); - ElemMatch e1 = new ElemMatch() {{ - setId(1); - setStrArray(new String[]{"a", "b"}); - setProductScores(new ProductScore[]{score1, score4}); - }}; - ElemMatch e2 = new ElemMatch() {{ - setId(2); - setStrArray(new String[]{"d", "e"}); - setProductScores(new ProductScore[]{score2, score5}); - }}; - ElemMatch e3 = new ElemMatch() {{ - setId(3); - setStrArray(new String[]{"a", "f"}); - setProductScores(new ProductScore[]{score3, score6}); - }}; + ElemMatch e1 = new ElemMatch(); + e1.setId(1); + e1.setStrArray(new String[]{"a", "b"}); + e1.setProductScores(new ProductScore[]{score1, score4}); + + ElemMatch e2 = new ElemMatch(); + e2.setId(2); + e2.setStrArray(new String[]{"d", "e"}); + e2.setProductScores(new ProductScore[]{score2, score5}); + + ElemMatch e3 = new ElemMatch(); + e3.setId(3); + e3.setStrArray(new String[]{"a", "f"}); + e3.setProductScores(new ProductScore[]{score3, score6}); repository.insert(e1, e2, e3); @@ -563,24 +563,37 @@ public void testIdSet() { @Test public void testBetweenFilter() { @Getter - class TestData implements Mappable { + class TestData { private Date age; public TestData(Date age) { this.age = age; } + } + + class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TestData.class; + } @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("age", age); + public Document toDocument(TestData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("age", entity.age); } @Override - public void read(NitriteMapper mapper, Document document) { - age = document.get("age", Date.class); + public TestData fromDocument(Document document, NitriteMapper nitriteMapper) { + TestData entity = new TestData(new Date()); + entity.age = document.get("age", Date.class); + return entity; } } + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new Converter()); + TestData data1 = new TestData(new GregorianCalendar(2020, Calendar.JANUARY, 11).getTime()); TestData data2 = new TestData(new GregorianCalendar(2021, Calendar.FEBRUARY, 12).getTime()); TestData data3 = new TestData(new GregorianCalendar(2022, Calendar.MARCH, 13).getTime()); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index 057a0b78e..427d94b8d 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -20,8 +20,9 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.index.NitriteTextIndexer; import org.dizitart.no2.index.fulltext.Languages; @@ -57,6 +58,8 @@ public void setUp() { openDb(); textRepository = db.getRepository(TextData.class); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TextData.Converter()); for (int i = 0; i < 10; i++) { TextData data = new TextData(); @@ -183,20 +186,29 @@ public void testUniversalFullTextIndexing() { @Indices( @Index(value = "text", type = IndexType.FULL_TEXT) ) - public static class TextData implements Mappable { + public static class TextData { public int id; public String text; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("text", text); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return TextData.class; + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Integer.class); - text = document.get("text", String.class); + @Override + public Document toDocument(TextData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("text", entity.text); + } + + @Override + public TextData fromDocument(Document document, NitriteMapper nitriteMapper) { + TextData entity = new TextData(); + entity.id = document.get("id", Integer.class); + entity.text = document.get("text", String.class); + return entity; + } } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index c80c7b060..cb3c972ca 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Entity; @@ -39,7 +39,7 @@ @Index(value = "description", type = IndexType.FULL_TEXT), @Index(value = { "price", "publisher" }) }) -public class Book implements Mappable { +public class Book { @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) private BookId bookId; @@ -51,22 +51,32 @@ public class Book implements Mappable { private String description; - @Override - public Document write(NitriteMapper mapper) { - return createDocument("book_id", mapper.convert(bookId, Document.class)) - .put("publisher", publisher) - .put("price", price) - .put("tags", tags) - .put("description", description); - } + public static class BookConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Book.class; + } + + @Override + public Document toDocument(Book entity, NitriteMapper nitriteMapper) { + return createDocument("book_id", nitriteMapper.convert(entity.bookId, Document.class)) + .put("publisher", entity.publisher) + .put("price", entity.price) + .put("tags", entity.tags) + .put("description", entity.description); + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - bookId = mapper.convert(document.get("book_id"), BookId.class); - publisher = document.get("publisher", String.class); - price = document.get("price", Double.class); - tags = (List) document.get("tags", List.class); - description = document.get("description", String.class); + @Override + @SuppressWarnings("unchecked") + public Book fromDocument(Document document, NitriteMapper nitriteMapper) { + Book entity = new Book(); + entity.bookId = nitriteMapper.convert(document.get("book_id"), BookId.class); + entity.publisher = document.get("publisher", String.class); + entity.price = document.get("price", Double.class); + entity.tags = (List) document.get("tags", List.class); + entity.description = document.get("description", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java index 2afcc42f6..4aa11f1af 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import static org.dizitart.no2.collection.Document.createDocument; @@ -28,24 +28,34 @@ * @author Anindya Chatterjee */ @Data -public class BookId implements Mappable { +public class BookId { private String isbn; private String name; private String author; - @Override - public Document write(NitriteMapper mapper) { - return createDocument("isbn", isbn) - .put("book_name", name) - .put("author", author); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - isbn = document.get("isbn", String.class); - name = document.get("book_name", String.class); - author = document.get("author", String.class); + public static class BookIdConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return BookId.class; + } + + @Override + public Document toDocument(BookId entity, NitriteMapper nitriteMapper) { + return createDocument("isbn", entity.isbn) + .put("book_name", entity.name) + .put("author", entity.author); + } + + @Override + public BookId fromDocument(Document document, NitriteMapper nitriteMapper) { + BookId entity = new BookId(); + entity.isbn = document.get("isbn", String.class); + entity.name = document.get("book_name", String.class); + entity.author = document.get("author", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java index d3c2ba644..6f0470d26 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java @@ -20,9 +20,12 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.InheritIndices; +import java.util.Date; + /** * @author Anindya Chatterjee */ @@ -32,14 +35,30 @@ public class ChildClass extends ParentClass { private String name; - @Override - public Document write(NitriteMapper mapper) { - return super.write(mapper).put("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ChildClass.class; + } + + @Override + public Document toDocument(ChildClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.getName()) + .put("id", entity.getId()) + .put("date", entity.getDate()) + .put("text", entity.getText()); + } - @Override - public void read(NitriteMapper mapper, Document document) { - super.read(mapper, document); - name = document.get("name", String.class); + @Override + public ChildClass fromDocument(Document document, NitriteMapper nitriteMapper) { + ChildClass entity = new ChildClass(); + entity.setId(document.get("id", Long.class)); + entity.setDate(document.get("date", Date.class)); + entity.setText(document.get("text", String.class)); + entity.setName(document.get("name", String.class)); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java index a8161c4e2..4e736981d 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java @@ -22,14 +22,14 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.UUID; @EqualsAndHashCode @ToString -public class ClassA implements Mappable { +public class ClassA { @Getter @Setter private ClassB b; @@ -53,23 +53,33 @@ public static ClassA create(int seed) { return classA; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("b", b != null ? b.write(mapper) : null) - .put("uid", uid) - .put("string", string) - .put("blob", blob); - } + public static class ClassAConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassA.class; + } + + @Override + public Document toDocument(ClassA entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("b", nitriteMapper.convert(entity.b, Document.class)) + .put("uid", entity.uid) + .put("string", entity.string) + .put("blob", entity.blob); + } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document.get("b") != null) { - b = new ClassB(); - b.read(mapper, document.get("b", Document.class)); + @Override + public ClassA fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassA entity = new ClassA(); + if (document.get("b") != null) { + Document doc = document.get("b", Document.class); + entity.b = nitriteMapper.convert(doc, ClassB.class); + } + entity.uid = document.get("uid", UUID.class); + entity.string = document.get("string", String.class); + entity.blob = document.get("blob", byte[].class); + return entity; } - uid = document.get("uid", UUID.class); - string = document.get("string", String.class); - blob = document.get("blob", byte[].class); } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java index a546b91e2..51cc9b416 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java @@ -21,13 +21,10 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; @EqualsAndHashCode @ToString -class ClassB implements Comparable, Mappable { +class ClassB implements Comparable { @Getter @Setter private int number; @@ -47,16 +44,4 @@ public int compareTo(ClassB o) { return Integer.compare(number, o.number); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("number", number) - .put("text", text); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - number = document.get("number", Integer.class); - text = document.get("text", String.class); - } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java new file mode 100644 index 000000000..6d5f88bab --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.data; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ClassBConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassB.class; + } + + @Override + public Document toDocument(ClassB entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("number", entity.getNumber()) + .put("text", entity.getText()); + } + + @Override + public ClassB fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassB entity = new ClassB(); + if (document.get("number") != null) { + entity.setNumber(document.get("number", Integer.class)); + } + entity.setText(document.get("text", String.class)); + return entity; + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java index 860fe1e93..0dd8d58d6 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java @@ -22,12 +22,12 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; @EqualsAndHashCode @ToString -public class ClassC implements Mappable { +public class ClassC { @Getter @Setter private long id; @@ -46,21 +46,37 @@ public static ClassC create(int seed) { return classC; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("id", id) - .put("digit", digit) - .put("parent", parent != null ? parent.write(mapper) : null); - } + public static class ClassCConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassC.class; + } + + @Override + public Document toDocument(ClassC entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("id", entity.id) + .put("digit", entity.digit) + .put("parent", nitriteMapper.convert(entity.parent, Document.class)); + } + + @Override + public ClassC fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassC entity = new ClassC(); + if (document.get("id") != null) { + entity.id = document.get("id", Long.class); + } + + if (document.get("digit") != null) { + entity.digit = document.get("digit", Double.class); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - digit = document.get("digit", Double.class); - if (document.get("parent") != null) { - parent = new ClassA(); - parent.read(mapper, document.get("parent", Document.class)); + if (document.get("parent") != null) { + Document doc = document.get("parent", Document.class); + entity.parent = nitriteMapper.convert(doc, ClassA.class); + } + return entity; } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java index 944e17358..54c1acf94 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -39,7 +39,7 @@ @Indices({ @Index(value = "companyName") }) -public class Company implements Serializable, Mappable { +public class Company implements Serializable { @Id(fieldName = "company_id") @Getter @Setter @@ -61,25 +61,6 @@ public class Company implements Serializable, Mappable { @Setter private Map> employeeRecord; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("company_id", companyId) - .put("companyName", companyName) - .put("dateCreated", dateCreated) - .put("departments", departments) - .put("employeeRecord", employeeRecord); - } - - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - companyId = document.get("company_id", Long.class); - companyName = document.get("companyName", String.class); - dateCreated = document.get("dateCreated", Date.class); - departments = document.get("departments", List.class); - employeeRecord = document.get("employeeRecord", Map.class); - } - @Override public String toString() { return "Company{" + @@ -89,4 +70,32 @@ public String toString() { ", departments=" + departments + '}'; } + + public static class CompanyConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Company.class; + } + + @Override + public Document toDocument(Company entity, NitriteMapper nitriteMapper) { + return Document.createDocument("company_id", entity.companyId) + .put("companyName", entity.companyName) + .put("dateCreated", entity.dateCreated) + .put("departments", entity.departments) + .put("employeeRecord", entity.employeeRecord); + } + + @Override + public Company fromDocument(Document document, NitriteMapper nitriteMapper) { + Company entity = new Company(); + entity.companyId = document.get("company_id", Long.class); + entity.companyName = document.get("companyName", String.class); + entity.dateCreated = document.get("dateCreated", Date.class); + entity.departments = document.get("departments", List.class); + entity.employeeRecord = document.get("employeeRecord", Map.class); + return entity; + } + } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java index 26e0dcf4a..6d83c2940 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java @@ -19,6 +19,9 @@ import com.github.javafaker.Faker; import lombok.val; +import org.dizitart.no2.integration.repository.decorator.Manufacturer; +import org.dizitart.no2.integration.repository.decorator.Product; +import org.dizitart.no2.integration.repository.decorator.ProductId; import java.nio.charset.StandardCharsets; import java.util.*; @@ -102,6 +105,15 @@ public static Book randomBook() { return book; } + public static Product randomProduct() { + Product product = new Product(); + product.setProductName(faker.name().name()); + product.setProductId(randomProductId()); + product.setManufacturer(randomManufacturer()); + product.setPrice(Double.parseDouble(faker.commerce().price())); + return product; + } + private static List departments() { return new ArrayList() {{ add("dev"); @@ -114,4 +126,19 @@ private static List departments() { add("support"); }}; } + + private static ProductId randomProductId() { + ProductId productId = new ProductId(); + productId.setProductCode(faker.code().ean13()); + productId.setUniqueId(UUID.randomUUID().toString()); + return productId; + } + + private static Manufacturer randomManufacturer() { + Manufacturer manufacturer = new Manufacturer(); + manufacturer.setUniqueId(random.nextInt()); + manufacturer.setName(faker.name().name()); + manufacturer.setAddress(faker.address().fullAddress()); + return manufacturer; + } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java index cb5304e29..3ef376e44 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.ArrayList; @@ -29,38 +29,46 @@ * @author Anindya Chatterjee */ @Data -public class ElemMatch implements Mappable { +public class ElemMatch { private long id; private String[] strArray; private ProductScore[] productScores; - @Override - public Document write(NitriteMapper mapper) { - List list = new ArrayList<>(); - if (productScores != null) { - for (ProductScore productScore : productScores) { - Document document = productScore.write(mapper); - list.add(document); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ElemMatch.class; } - return Document.createDocument("id", id) - .put("strArray", strArray) - .put("productScores", list); - } + @Override + public Document toDocument(ElemMatch entity, NitriteMapper nitriteMapper) { + List list = new ArrayList<>(); + if (entity.productScores != null) { + for (ProductScore productScore : entity.productScores) { + Document document = nitriteMapper.convert(productScore, Document.class); + list.add(document); + } + } + + return Document.createDocument("id", entity.id) + .put("strArray", entity.strArray) + .put("productScores", list); + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - strArray = document.get("strArray", String[].class); - List list = document.get("productScores", List.class); - if (list != null) { - productScores = new ProductScore[list.size()]; - for (int i = 0; i < list.size(); i++) { - productScores[i] = new ProductScore(); - productScores[i].read(mapper, list.get(i)); + @Override + public ElemMatch fromDocument(Document document, NitriteMapper nitriteMapper) { + ElemMatch entity = new ElemMatch(); + entity.id = document.get("id", Long.class); + entity.strArray = document.get("strArray", String[].class); + List list = document.get("productScores", List.class); + if (list != null) { + entity.productScores = new ProductScore[list.size()]; + for (int i = 0; i < list.size(); i++) { + entity.productScores[i] = nitriteMapper.convert(list.get(i), ProductScore.class); + } } + return entity; } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java index 7204f5654..dbba83965 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java @@ -22,9 +22,9 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -39,7 +39,7 @@ @Index(value = "joinDate", type = IndexType.NON_UNIQUE) @Index(value = "address", type = IndexType.FULL_TEXT) @Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) -public class Employee implements Serializable, Mappable { +public class Employee implements Serializable { @Id @Getter @Setter @@ -82,28 +82,39 @@ public Employee(Employee copy) { emailAddress = copy.emailAddress; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("empId", empId) - .put("joinDate", joinDate) - .put("address", address) - .put("blob", blob) - .put("emailAddress", emailAddress) - .put("employeeNote", employeeNote != null ? employeeNote.write(mapper) : null); - } + public static class EmployeeConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Employee.class; + } + + @Override + public Document toDocument(Employee entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address) + .put("blob", entity.blob) + .put("emailAddress", entity.emailAddress) + .put("employeeNote", nitriteMapper.convert(entity.employeeNote, Document.class)); + } + + @Override + public Employee fromDocument(Document document, NitriteMapper nitriteMapper) { + Employee entity = new Employee(); + + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + entity.blob = document.get("blob", byte[].class); + entity.emailAddress = document.get("emailAddress", String.class); - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); - blob = document.get("blob", byte[].class); - emailAddress = document.get("emailAddress", String.class); - - if (document.get("employeeNote") != null) { - employeeNote = new Note(); - employeeNote.read(mapper, document.get("employeeNote", Document.class)); + if (document.get("employeeNote") != null) { + Document doc = document.get("employeeNote", Document.class); + entity.employeeNote = nitriteMapper.convert(doc, Note.class);; + } + return entity; } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java index 8b9d4b23b..7003db015 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Entity; @@ -30,25 +30,35 @@ */ @Data @Entity -public class EncryptedPerson implements Mappable { +public class EncryptedPerson { private String name; private String creditCardNumber; private String cvv; private Date expiryDate; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("creditCardNumber", creditCardNumber) - .put("cvv", cvv) - .put("expiryDate", expiryDate); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EncryptedPerson.class; + } + + @Override + public Document toDocument(EncryptedPerson entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("creditCardNumber", entity.creditCardNumber) + .put("cvv", entity.cvv) + .put("expiryDate", entity.expiryDate); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - creditCardNumber = document.get("creditCardNumber", String.class); - cvv = document.get("cvv", String.class); - expiryDate = document.get("expiryDate", Date.class); + @Override + public EncryptedPerson fromDocument(Document document, NitriteMapper nitriteMapper) { + EncryptedPerson entity = new EncryptedPerson(); + entity.name = document.get("name", String.class); + entity.creditCardNumber = document.get("creditCardNumber", String.class); + entity.cvv = document.get("cvv", String.class); + entity.expiryDate = document.get("expiryDate", Date.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Note.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Note.java index 67dc307d1..7bf7f1726 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Note.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Note.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.io.Serializable; @@ -30,7 +30,7 @@ * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class Note implements Serializable, Mappable { +public class Note implements Serializable { @Getter @Setter private Long noteId; @@ -38,14 +38,26 @@ public class Note implements Serializable, Mappable { @Setter private String text; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("noteId", noteId).put("text", text); - } + public static class NoteConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Note.class; + } + + @Override + public Document toDocument(Note entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("noteId", entity.noteId) + .put("text", entity.text); + } - @Override - public void read(NitriteMapper mapper, Document document) { - noteId = document.get("noteId", Long.class); - text = document.get("text", String.class); + @Override + public Note fromDocument(Document document, NitriteMapper nitriteMapper) { + Note entity = new Note(); + entity.noteId = document.get("noteId", Long.class); + entity.text = document.get("text", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java index bc2514543..844e39223 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java @@ -19,8 +19,6 @@ import lombok.Getter; import lombok.Setter; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -36,18 +34,4 @@ public class ParentClass extends SuperDuperClass { @Id protected Long id; private Date date; - - @Override - public Document write(NitriteMapper mapper) { - return super.write(mapper) - .put("id", id) - .put("date", date); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - super.read(mapper, document); - id = document.get("id", Long.class); - date = document.get("date", Date.class); - } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java index 2b90075fa..b036118c7 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java @@ -19,9 +19,9 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Entity; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -37,7 +37,7 @@ @Index(value = "name", type = IndexType.FULL_TEXT), @Index(value = "status", type = IndexType.NON_UNIQUE) }) -public class PersonEntity implements Mappable { +public class PersonEntity { @Id private String uuid; private String name; @@ -56,24 +56,34 @@ public PersonEntity(String name) { this.dateCreated = new Date(); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("uuid", uuid) - .put("name", name) - .put("status", status) - .put("friend", friend != null ? friend.write(mapper) : null) - .put("dateCreated", dateCreated); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PersonEntity.class; + } + + @Override + public Document toDocument(PersonEntity entity, NitriteMapper nitriteMapper) { + return Document.createDocument("uuid", entity.uuid) + .put("name", entity.name) + .put("status", entity.status) + .put("friend", entity.friend != null ? nitriteMapper.convert(entity.friend, Document.class) : null) + .put("dateCreated", entity.dateCreated); + } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - uuid = document.get("uuid", String.class); - name = document.get("name", String.class); - status = document.get("status", String.class); - dateCreated = document.get("dateCreated", Date.class); - friend = new PersonEntity(); - friend.read(mapper, document.get("friend", Document.class)); + @Override + public PersonEntity fromDocument(Document document, NitriteMapper nitriteMapper) { + if (document != null) { + PersonEntity entity = new PersonEntity(); + entity.uuid = document.get("uuid", String.class); + entity.name = document.get("name", String.class); + entity.status = document.get("status", String.class); + entity.dateCreated = document.get("dateCreated", Date.class); + entity.friend = nitriteMapper.convert(document.get("friend", Document.class), PersonEntity.class); + return entity; + } + return null; } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java index 65550d182..9ebe19057 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,7 +28,7 @@ */ @Getter @Setter -public class ProductScore implements Mappable { +public class ProductScore { private String product; private int score; @@ -40,15 +40,25 @@ public ProductScore(String product, int score) { this.score = score; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("product", product) - .put("score", score); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ProductScore.class; + } + + @Override + public Document toDocument(ProductScore entity, NitriteMapper nitriteMapper) { + return Document.createDocument("product", entity.product) + .put("score", entity.score); + } - @Override - public void read(NitriteMapper mapper, Document document) { - product = document.get("product", String.class); - score = document.get("score", Integer.class); + @Override + public ProductScore fromDocument(Document document, NitriteMapper nitriteMapper) { + ProductScore entity = new ProductScore(); + entity.product = document.get("product", String.class); + entity.score = document.get("score", Integer.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java index 1ecadb243..67f77060e 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java @@ -19,9 +19,9 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Index; /** @@ -31,22 +31,32 @@ @Index(value = "firstName") @Index(value = "age", type = IndexType.NON_UNIQUE) @Index(value = "lastName", type = IndexType.FULL_TEXT) -public class RepeatableIndexTest implements Mappable { +public class RepeatableIndexTest { private String firstName; private Integer age; private String lastName; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("firstName", firstName) - .put("age", age) - .put("lastName", lastName); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return RepeatableIndexTest.class; + } + + @Override + public Document toDocument(RepeatableIndexTest entity, NitriteMapper nitriteMapper) { + return Document.createDocument("firstName", entity.firstName) + .put("age", entity.age) + .put("lastName", entity.lastName); + } - @Override - public void read(NitriteMapper mapper, Document document) { - firstName = document.get("firstName", String.class); - age = document.get("age", Integer.class); - lastName = document.get("lastName", String.class); + @Override + public RepeatableIndexTest fromDocument(Document document, NitriteMapper nitriteMapper) { + RepeatableIndexTest entity = new RepeatableIndexTest(); + entity.firstName = document.get("firstName", String.class); + entity.age = document.get("age", Integer.class); + entity.lastName = document.get("lastName", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java index d1cb199c4..9e74e8fb0 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,28 +28,37 @@ */ @Getter @Setter -public class StressRecord implements Mappable { +public class StressRecord { private String firstName; private boolean processed; private String lastName; private boolean failed; private String notes; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("firstName", firstName) - .put("processed", processed) - .put("lastName", lastName) - .put("failed", failed) - .put("notes", notes); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return StressRecord.class; + } + + @Override + public Document toDocument(StressRecord entity, NitriteMapper nitriteMapper) { + return Document.createDocument().put("firstName", entity.firstName) + .put("processed", entity.processed) + .put("lastName", entity.lastName) + .put("failed", entity.failed) + .put("notes", entity.notes); + } - @Override - public void read(NitriteMapper mapper, Document document) { - firstName = document.get("firstName", String.class); - processed = document.get("processed", Boolean.class); - lastName = document.get("lastName", String.class); - failed = document.get("failed", Boolean.class); - notes = document.get("notes", String.class); + @Override + public StressRecord fromDocument(Document document, NitriteMapper nitriteMapper) { + StressRecord entity = new StressRecord(); + entity.firstName = document.get("firstName", String.class); + entity.processed = document.get("processed", Boolean.class); + entity.lastName = document.get("lastName", String.class); + entity.failed = document.get("failed", Boolean.class); + entity.notes = document.get("notes", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java index 8eaa3faf1..ddea32812 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.Date; @@ -30,7 +30,7 @@ * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class SubEmployee implements Mappable { +public class SubEmployee { @Getter @Setter private Long empId; @@ -43,18 +43,28 @@ public class SubEmployee implements Mappable { @Setter private String address; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("empId", empId) - .put("joinDate", joinDate) - .put("address", address); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return SubEmployee.class; + } + + @Override + public Document toDocument(SubEmployee entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address); + } - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); + @Override + public SubEmployee fromDocument(Document document, NitriteMapper nitriteMapper) { + SubEmployee entity = new SubEmployee(); + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java index 198f16d0c..d39ddf146 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java @@ -19,10 +19,7 @@ import lombok.Getter; import lombok.Setter; -import org.dizitart.no2.collection.Document; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Index; /** @@ -31,16 +28,6 @@ @Getter @Setter @Index(value = "text", type = IndexType.FULL_TEXT) -public class SuperDuperClass implements Mappable { +public class SuperDuperClass { private String text; - - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("text", text); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - text = document.get("text", String.class); - } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java index 79cafe0dc..bdeef8744 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,20 +29,29 @@ */ @Getter @Setter -public class WithClassField implements Mappable { +public class WithClassField { @Id private String name; private Class clazz; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("clazz", clazz); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return WithClassField.class; + } + + @Override + public Document toDocument(WithClassField entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("clazz", entity.clazz); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - clazz = document.get("clazz", Class.class); + @Override + public WithClassField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithClassField entity = new WithClassField(); + entity.name = document.get("name", String.class); + entity.clazz = document.get("clazz", Class.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java index 229f1aaf7..125e94b05 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.Date; @@ -32,19 +32,29 @@ @Getter @Setter @EqualsAndHashCode -public class WithDateId implements Mappable { +public class WithDateId { private Date id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("id", id); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithDateId.class; + } + + @Override + public Document toDocument(WithDateId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("id", entity.id); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - id = document.get("id", Date.class); + @Override + public WithDateId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithDateId entity = new WithDateId(); + entity.name = document.get("name", String.class); + entity.id = document.get("id", Date.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java index a0048a6bb..15792f12e 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,17 +29,27 @@ */ @Getter @Setter -public class WithEmptyStringId implements Mappable { +public class WithEmptyStringId { @Id private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithEmptyStringId.class; + } + + @Override + public Document toDocument(WithEmptyStringId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); + @Override + public WithEmptyStringId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithEmptyStringId entity = new WithEmptyStringId(); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java index 2364be76d..6748256c6 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java @@ -20,7 +20,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -28,21 +28,31 @@ * @author Anindya Chatterjee */ @Data -public class WithNitriteId implements Mappable { +public class WithNitriteId { @Id public NitriteId idField; public String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("idField", idField) - .put("name", name); - } + public static class WithNitriteIdConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithNitriteId.class; + } + + @Override + public Document toDocument(WithNitriteId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("idField", entity.idField) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - idField = document.get("idField", NitriteId.class); - name = document.get("name", String.class); + @Override + public WithNitriteId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithNitriteId entity = new WithNitriteId(); + entity.idField = document.get("idField", NitriteId.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java index 0dc3fb68b..e5ca44b78 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,21 +29,31 @@ */ @Getter @Setter -public class WithNullId implements Mappable { +public class WithNullId { @Id private String name; private long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithNullId.class; + } + + @Override + public Document toDocument(WithNullId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithNullId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithNullId entity = new WithNullId(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java index 5f00f97d4..634a90fb8 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,17 +29,27 @@ */ @Getter @Setter -public class WithObjectId implements Mappable { +public class WithObjectId { @Id private WithOutId withOutId; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("withOutId", withOutId); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithObjectId.class; + } + + @Override + public Document toDocument(WithObjectId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("withOutId", entity.withOutId); + } - @Override - public void read(NitriteMapper mapper, Document document) { - withOutId = document.get("withOutId", WithOutId.class); + @Override + public WithObjectId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithObjectId entity = new WithObjectId(); + entity.withOutId = document.get("withOutId", WithOutId.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java index a7a2c56c9..2ce03bf24 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java @@ -19,14 +19,14 @@ import lombok.EqualsAndHashCode; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class WithOutGetterSetter implements Mappable { +public class WithOutGetterSetter { private String name; private long number; @@ -35,16 +35,25 @@ public WithOutGetterSetter() { number = 2; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); + public static class Converter implements EntityConverter { - } + @Override + public Class getEntityType() { + return WithOutGetterSetter.class; + } + + @Override + public Document toDocument(WithOutGetterSetter entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithOutGetterSetter fromDocument(Document document, NitriteMapper nitriteMapper) { + WithOutGetterSetter entity = new WithOutGetterSetter(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java index 09b63c2c2..645dd0b45 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,7 +28,7 @@ */ @Getter @Setter -public class WithOutId implements Comparable, Mappable { +public class WithOutId implements Comparable { private String name; private long number; @@ -37,16 +37,26 @@ public int compareTo(WithOutId o) { return Long.compare(number, o.number); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public Class getEntityType() { + return WithOutId.class; + } + + @Override + public Document toDocument(WithOutId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.name) + .put("number", entity.number); + } + + @Override + public WithOutId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithOutId entity = new WithOutId(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java index a425209fb..28febec9e 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java @@ -19,14 +19,14 @@ import lombok.EqualsAndHashCode; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class WithPrivateConstructor implements Mappable { +public class WithPrivateConstructor { private String name; private long number; @@ -42,15 +42,24 @@ public static WithPrivateConstructor create(final String name, final long number return obj; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithPrivateConstructor.class; + } + + @Override + public Document toDocument(WithPrivateConstructor entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithPrivateConstructor fromDocument(Document document, NitriteMapper nitriteMapper) { + String name = document.get("name", String.class); + Long number = document.get("number", Long.class); + return WithPrivateConstructor.create(name, number); + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java index 793388231..eae21f1de 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java @@ -18,27 +18,37 @@ package org.dizitart.no2.integration.repository.data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; /** * @author Anindya Chatterjee. */ -public class WithPublicField implements Mappable { +public class WithPublicField { @Id public String name; public long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithPublicField.class; + } + + @Override + public Document toDocument(WithPublicField entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithPublicField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithPublicField entity = new WithPublicField(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java index 9c1ade657..35dde5a91 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,19 +29,29 @@ */ @Getter @Setter -public class WithTransientField implements Mappable { +public class WithTransientField { private transient String name; @Id private long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithTransientField.class; + } + + @Override + public Document toDocument(WithTransientField entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - number = document.get("number", Long.class); + @Override + public WithTransientField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithTransientField entity = new WithTransientField(); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java index 9ecb9a68b..9c721c80c 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -27,39 +27,60 @@ * @author Anindya Chatterjee */ @Data -public class WithoutEmbeddedId implements Mappable { +public class WithoutEmbeddedId { @Id private NestedId nestedId; private String data; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("nestedId", nestedId.write(mapper)) - .put("data", data); - } + @Data + public static class NestedId { + private Long id; + + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - Document nestedId = document.get("nestedId", Document.class); - this.nestedId = mapper.convert(nestedId, NestedId.class); - this.data = document.get("data", String.class); + @Override + public Class getEntityType() { + return NestedId.class; + } + + @Override + public Document toDocument(NestedId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("id", entity.id); + } + + @Override + public NestedId fromDocument(Document document, NitriteMapper nitriteMapper) { + NestedId entity = new NestedId(); + entity.id = document.get("id", Long.class); + return entity; + } + } } + public static class Converter implements EntityConverter { - @Data - public static class NestedId implements Mappable { - private Long id; + @Override + public Class getEntityType() { + return WithoutEmbeddedId.class; + } @Override - public Document write(NitriteMapper mapper) { + public Document toDocument(WithoutEmbeddedId entity, NitriteMapper nitriteMapper) { return Document.createDocument() - .put("id", id); + .put("nestedId", nitriteMapper.convert(entity.nestedId, Document.class)) + .put("data", entity.data); } @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); + public WithoutEmbeddedId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithoutEmbeddedId entity = new WithoutEmbeddedId(); + Document nestedId = document.get("nestedId", Document.class); + + entity.nestedId = nitriteMapper.convert(nestedId, NestedId.class); + entity.data = document.get("data", String.class); + + return entity; } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java similarity index 75% rename from nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java rename to nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java index 1b764da20..c6bfe8bb9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableFactory.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java @@ -15,14 +15,13 @@ * */ -package org.dizitart.no2.common.mapper; +package org.dizitart.no2.integration.repository.decorator; -/** - * Represents a factory for creating instances of a type. - * - * @since 4.0 - * @author Anindya Chatterjee - */ -public interface MappableFactory { - T create(); +import lombok.Data; + +@Data +public class Manufacturer { + private String name; + private String address; + private Integer uniqueId; } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java new file mode 100644 index 000000000..f31d7b0bb --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ManufacturerConverter implements EntityConverter { + @Override + public Class getEntityType() { + return Manufacturer.class; + } + + @Override + public Document toDocument(Manufacturer entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.getName()) + .put("address", entity.getAddress()) + .put("uniqueId", entity.getUniqueId()); + } + + @Override + public Manufacturer fromDocument(Document document, NitriteMapper nitriteMapper) { + Manufacturer manufacturer = new Manufacturer(); + manufacturer.setName(document.get("name", String.class)); + manufacturer.setAddress(document.get("address", String.class)); + manufacturer.setUniqueId(document.get("uniqueId", Integer.class)); + return manufacturer; + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java new file mode 100644 index 000000000..8d04904f9 --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityDecorator; +import org.dizitart.no2.repository.EntityId; + +import java.util.List; + +public class ManufacturerDecorator implements EntityDecorator { + @Override + public Class getEntityType() { + return Manufacturer.class; + } + + @Override + public EntityId getIdField() { + return null; + } + + @Override + public List getIndexFields() { + return null; + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java new file mode 100644 index 000000000..89b332c8a --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +@Data +public class MiniProduct { + private String uniqueId; + private String manufacturerName; + private Double price; + + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return MiniProduct.class; + } + + @Override + public Document toDocument(MiniProduct entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("productId.uniqueId", entity.getUniqueId()) + .put("manufacturer.name", entity.getManufacturerName()) + .put("price", entity.getPrice()); + } + + @Override + public MiniProduct fromDocument(Document document, NitriteMapper nitriteMapper) { + MiniProduct entity = new MiniProduct(); + entity.setUniqueId(document.get("productId.uniqueId", String.class)); + entity.setManufacturerName(document.get("manufacturer.name", String.class)); + entity.setPrice(document.get("price", Double.class)); + return entity; + } + } +} diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdExtensionTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java similarity index 63% rename from nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdExtensionTest.java rename to nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java index 2a02ad524..ff59219f9 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/mapper/extensions/NitriteIdExtensionTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2021 Nitrite author or authors. + * Copyright (c) 2017-2022 Nitrite author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,14 @@ * */ -package org.dizitart.no2.common.mapper.extensions; +package org.dizitart.no2.integration.repository.decorator; -import org.junit.Assert; -import org.junit.Test; +import lombok.Data; -public class NitriteIdExtensionTest { - @Test - public void testGetSupportedTypes() { - Assert.assertEquals(1, (new NitriteIdExtension()).getSupportedTypes().size()); - } +@Data +public class Product { + private ProductId productId; + private Manufacturer manufacturer; + private String productName; + private Double price; } - diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java new file mode 100644 index 000000000..0421c0cac --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ProductConverter implements EntityConverter { + @Override + public Class getEntityType() { + return Product.class; + } + + @Override + public Document toDocument(Product entity, NitriteMapper nitriteMapper) { + Document productId = nitriteMapper.convert(entity.getProductId(), Document.class); + Document manufacturer = nitriteMapper.convert(entity.getManufacturer(), Document.class); + + return Document.createDocument() + .put("productId", productId) + .put("manufacturer", manufacturer) + .put("productName", entity.getProductName()) + .put("price", entity.getPrice()); + } + + @Override + public Product fromDocument(Document document, NitriteMapper nitriteMapper) { + Product entity = new Product(); + ProductId productId = nitriteMapper.convert(document.get("productId", Document.class), ProductId.class); + Manufacturer manufacturer = nitriteMapper.convert(document.get("manufacturer", Document.class), + Manufacturer.class); + entity.setProductId(productId); + entity.setManufacturer(manufacturer); + entity.setProductName(document.get("productName", String.class)); + entity.setPrice(document.get("price", Double.class)); + return entity; + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java new file mode 100644 index 000000000..301dd8909 --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.repository.EntityDecorator; +import org.dizitart.no2.repository.EntityId; + +import java.util.Arrays; +import java.util.List; + +public class ProductDecorator implements EntityDecorator { + @Override + public Class getEntityType() { + return Product.class; + } + + @Override + public EntityId getIdField() { + return new EntityId("productId", "uniqueId", "productCode"); + } + + @Override + public List getIndexFields() { + return Arrays.asList( + IndexFields.create(IndexType.NON_UNIQUE, "manufacturer.name"), + IndexFields.create(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") + ); + } + + @Override + public String getEntityName() { + return "product"; + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java new file mode 100644 index 000000000..dd2f9f04e --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; + +@Data +public class ProductId { + private String uniqueId; + private String productCode; +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java new file mode 100644 index 000000000..2c02fddc0 --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ProductIdConverter implements EntityConverter { + @Override + public Class getEntityType() { + return ProductId.class; + } + + @Override + public Document toDocument(ProductId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("uniqueId", entity.getUniqueId()) + .put("productCode", entity.getProductCode()); + } + + @Override + public ProductId fromDocument(Document document, NitriteMapper nitriteMapper) { + ProductId entity = new ProductId(); + entity.setUniqueId(document.get("uniqueId", String.class)); + entity.setProductCode(document.get("productCode", String.class)); + return entity; + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TxData.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TxData.java index 1e5ed7ca6..104ccd4ad 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TxData.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/transaction/TxData.java @@ -21,7 +21,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -31,20 +31,30 @@ @Data @NoArgsConstructor @AllArgsConstructor -class TxData implements Mappable { +public class TxData { @Id private Long id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TxData.class; + } + + @Override + public Document toDocument(TxData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - name = document.get("name", String.class); + @Override + public TxData fromDocument(Document document, NitriteMapper nitriteMapper) { + TxData entity = new TxData(); + entity.id = document.get("id", Long.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java index f19431be6..7a8cba03e 100644 --- a/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java +++ b/nitrite-replication/src/test/java/org/dizitart/no2/integration/DataGateIntegrationTest.java @@ -28,11 +28,7 @@ import org.dizitart.no2.sync.Replica; import org.dizitart.no2.sync.event.ReplicationEvent; import org.dizitart.no2.sync.event.ReplicationEventType; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.Ignore; +import org.junit.*; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.MongoDBContainer; import org.testcontainers.containers.Network; diff --git a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java index 88da8c9e8..d570520a8 100644 --- a/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java +++ b/nitrite-rocksdb-adapter/src/main/java/org/dizitart/no2/rocksdb/formatter/NitriteSerializers.java @@ -115,7 +115,7 @@ private static class IndexDescriptorSerializer extends Serializer) null); } @Test(expected = NitriteIOException.class) @@ -331,11 +335,17 @@ public void testIssue193() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(10000); for (int i = 0; i < 10000; i++) { pool.submit(() -> { - int refIndex = random.nextInt(5); - Receipt receipt = factory.manufacturePojoWithFullData(Receipt.class); - receipt.setClientRef(refs[refIndex]); - repository.update(receipt, true); - latch.countDown(); + try { + int refIndex = random.nextInt(5); + Receipt receipt = factory.manufacturePojoWithFullData(Receipt.class); + receipt.setClientRef(refs[refIndex]); + repository.update(receipt, true); + } catch (Exception e) { + e.printStackTrace(); + fail("Unhandled exception in thread - " + e.getMessage()); + } finally { + latch.countDown(); + } }); } @@ -420,20 +430,30 @@ public void run() { @Data @AllArgsConstructor @NoArgsConstructor - public static class CompatChild implements Mappable { + public static class CompatChild { private Long childId; private String lastName; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("childId", childId) - .put("lastName", lastName); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return CompatChild.class; + } + + @Override + public Document toDocument(CompatChild entity, NitriteMapper nitriteMapper) { + return Document.createDocument("childId", entity.childId) + .put("lastName", entity.lastName); + } - @Override - public void read(NitriteMapper mapper, Document document) { - childId = document.get("childId", Long.class); - lastName = document.get("lastName", String.class); + @Override + public CompatChild fromDocument(Document document, NitriteMapper nitriteMapper) { + CompatChild entity = new CompatChild(); + entity.childId = document.get("childId", Long.class); + entity.lastName = document.get("lastName", String.class); + return entity; + } } } @@ -443,33 +463,43 @@ public void read(NitriteMapper mapper, Document document) { @Indices({ @Index(value = "synced", type = IndexType.NON_UNIQUE) }) - public static class Receipt implements Mappable { + public static class Receipt { @Id private String clientRef; private Boolean synced; private Status status; private Long createdTimestamp = System.currentTimeMillis(); - @Override - public Document write(NitriteMapper mapper) { - return createDocument("status", status) - .put("clientRef", clientRef) - .put("synced", synced) - .put("createdTimestamp", createdTimestamp); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return Receipt.class; + } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - Object status = document.get("status"); - if (status instanceof Status) { - this.status = (Status) status; - } else { - this.status = Status.valueOf(status.toString()); + @Override + public Document toDocument(Receipt entity, NitriteMapper nitriteMapper) { + return createDocument("status", entity.status) + .put("clientRef", entity.clientRef) + .put("synced", entity.synced) + .put("createdTimestamp", entity.createdTimestamp); + } + + @Override + public Receipt fromDocument(Document document, NitriteMapper nitriteMapper) { + Receipt receipt = new Receipt(); + if (document != null) { + Object status = document.get("status"); + if (status instanceof Status) { + receipt.status = (Status) status; + } else { + receipt.status = Status.valueOf(status.toString()); + } + receipt.clientRef = document.get("clientRef", String.class); + receipt.synced = document.get("synced", Boolean.class); + receipt.createdTimestamp = document.get("createdTimestamp", Long.class); } - this.clientRef = document.get("clientRef", String.class); - this.synced = document.get("synced", Boolean.class); - this.createdTimestamp = document.get("createdTimestamp", Long.class); + return receipt; } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java index f6436ee6d..46c08bde1 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java @@ -27,8 +27,9 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.FieldValues; import org.dizitart.no2.common.Fields; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.NitriteSecurityException; @@ -174,6 +175,10 @@ public void testPopulateRepositories() { .fieldSeparator(".") .openOrCreate(); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TestObject.Converter()); + documentMapper.registerEntityConverter(new TestObject2.Converter()); + NitriteCollection collection = db.getCollection("test"); collection.insert(createDocument("id1", "value")); @@ -304,16 +309,6 @@ public Target convert(Source source, Class type) { return null; } - @Override - public boolean isValueType(Class type) { - return false; - } - - @Override - public boolean isValue(Object object) { - return false; - } - @Override public void initialize(NitriteConfig nitriteConfig) { @@ -321,7 +316,7 @@ public void initialize(NitriteConfig nitriteConfig) { } @Index(value = "longValue") - private static class TestObject implements Mappable { + private static class TestObject { private String stringValue; private Long longValue; @@ -333,23 +328,29 @@ public TestObject(String stringValue, Long longValue) { this.stringValue = stringValue; } - @Override - public Document write(NitriteMapper mapper) { - return createDocument("stringValue", stringValue) - .put("longValue", longValue); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - this.stringValue = document.get("stringValue", String.class); - this.longValue = document.get("longValue", Long.class); + @Override + public Class getEntityType() { + return TestObject.class; + } + + @Override + public Document toDocument(TestObject entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("stringValue", entity.stringValue) + .put("longValue", entity.longValue); + } + + @Override + public TestObject fromDocument(Document document, NitriteMapper nitriteMapper) { + return null; } } } @Index(value = "longValue") - private static class TestObject2 implements Mappable { + private static class TestObject2 { private String stringValue; private Long longValue; @@ -361,17 +362,27 @@ public TestObject2(String stringValue, Long longValue) { this.stringValue = stringValue; } - @Override - public Document write(NitriteMapper mapper) { - return createDocument("stringValue", stringValue) - .put("longValue", longValue); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - this.stringValue = document.get("stringValue", String.class); - this.longValue = document.get("longValue", Long.class); + @Override + public Class getEntityType() { + return TestObject2.class; + } + + @Override + public Document toDocument(TestObject2 entity, NitriteMapper nitriteMapper) { + return createDocument("stringValue", entity.stringValue) + .put("longValue", entity.longValue); + } + + @Override + public TestObject2 fromDocument(Document document, NitriteMapper nitriteMapper) { + TestObject2 entity = new TestObject2(); + if (document != null) { + entity.stringValue = document.get("stringValue", String.class); + entity.longValue = document.get("longValue", Long.class); + } + return entity; } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java index 19c4a830a..5184d6f03 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java @@ -24,8 +24,9 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; @@ -66,6 +67,11 @@ public class NitriteStressTest { @Before public void before() { db = createDb(fileName); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TestDto.Converter()); + documentMapper.registerEntityConverter(new PerfTest.Converter()); + documentMapper.registerEntityConverter(new PerfTestIndexed.Converter()); + collection = db.getCollection("test"); System.out.println(fileName); } @@ -220,7 +226,7 @@ private List getItems(Class type) { } @Data - public static class TestDto implements Mappable { + public static class TestDto { @XmlElement( name = "StudentNumber", @@ -268,53 +274,73 @@ public static class TestDto implements Mappable { public TestDto() { } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("studentNumber", studentNumber) - .put("lastName", lastName) - .put("prefixes", prefixes) - .put("initials", initials) - .put("firstNames", firstNames) - .put("nickName", nickName) - .put("birthDate", birthDate); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TestDto.class; + } + + @Override + public Document toDocument(TestDto entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("studentNumber", entity.studentNumber) + .put("lastName", entity.lastName) + .put("prefixes", entity.prefixes) + .put("initials", entity.initials) + .put("firstNames", entity.firstNames) + .put("nickName", entity.nickName) + .put("birthDate", entity.birthDate); + } - @Override - public void read(NitriteMapper mapper, Document document) { - studentNumber = document.get("studentNumber", String.class); - lastName = document.get("lastName", String.class); - prefixes = document.get("prefixes", String.class); - initials = document.get("initials", String.class); - firstNames = document.get("firstNames", String.class); - nickName = document.get("nickName", String.class); - birthDate = document.get("birthDate", String.class); + @Override + public TestDto fromDocument(Document document, NitriteMapper nitriteMapper) { + TestDto entity = new TestDto(); + entity.studentNumber = document.get("studentNumber", String.class); + entity.lastName = document.get("lastName", String.class); + entity.prefixes = document.get("prefixes", String.class); + entity.initials = document.get("initials", String.class); + entity.firstNames = document.get("firstNames", String.class); + entity.nickName = document.get("nickName", String.class); + entity.birthDate = document.get("birthDate", String.class); + return entity; + } } } @Data - public static class PerfTest implements Mappable { + public static class PerfTest { private String firstName; private String lastName; private Integer age; private String text; - @Override - public Document write(NitriteMapper mapper) { - Document document = Document.createDocument(); - document.put("firstName", firstName); - document.put("lastName", lastName); - document.put("age", age); - document.put("text", text); - return document; - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PerfTest.class; + } - @Override - public void read(NitriteMapper mapper, Document document) { - this.firstName = (String) document.get("firstName"); - this.lastName = (String) document.get("lastName"); - this.age = (Integer) document.get("age"); - this.text = (String) document.get("text"); + @Override + public Document toDocument(PerfTest entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + document.put("firstName", entity.firstName); + document.put("lastName", entity.lastName); + document.put("age", entity.age); + document.put("text", entity.text); + return document; + } + + @Override + public PerfTest fromDocument(Document document, NitriteMapper nitriteMapper) { + PerfTest entity = new PerfTest(); + entity.firstName = (String) document.get("firstName"); + entity.lastName = (String) document.get("lastName"); + entity.age = (Integer) document.get("age"); + entity.text = (String) document.get("text"); + return entity; + } } } @@ -324,5 +350,32 @@ public void read(NitriteMapper mapper, Document document) { @Index(value = "text", type = IndexType.FULL_TEXT), }) private static class PerfTestIndexed extends PerfTest { + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PerfTestIndexed.class; + } + + @Override + public Document toDocument(PerfTestIndexed entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + document.put("firstName", entity.getFirstName()); + document.put("lastName", entity.getLastName()); + document.put("age", entity.getAge()); + document.put("text", entity.getText()); + return document; + } + + @Override + public PerfTestIndexed fromDocument(Document document, NitriteMapper nitriteMapper) { + PerfTestIndexed entity = new PerfTestIndexed(); + entity.setFirstName((String) document.get("firstName")); + entity.setLastName((String) document.get("lastName")); + entity.setAge((Integer) document.get("age")); + entity.setText((String) document.get("text")); + return entity; + } + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java index 8d446cf0f..71a97a42d 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java @@ -21,6 +21,7 @@ import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.exceptions.FilterException; +import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; import org.junit.Test; @@ -61,7 +62,7 @@ public void testFindOptionsInvalidOffset() { assertEquals(collection.find(skipBy(10).limit(1)).size(), 0); } - @Test(expected = ValidationException.class) + @Test(expected = InvalidOperationException.class) public void testFindInvalidSort() { insert(); collection.find(orderBy("data", SortOrder.Descending)).toList(); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java index 247a1705a..4a45fa055 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java @@ -188,7 +188,7 @@ public void testRebuildIndex() { insert(); Collection indices = collection.listIndices(); for (IndexDescriptor idx : indices) { - collection.rebuildIndex(idx.getIndexFields().getFieldNames().toArray(new String[0])); + collection.rebuildIndex(idx.getFields().getFieldNames().toArray(new String[0])); } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java index fe37845e1..a59e7e459 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java @@ -134,7 +134,7 @@ public void testRebuildIndex() { insert(); Collection indices = collection.listIndices(); for (IndexDescriptor idx : indices) { - collection.rebuildIndex(idx.getIndexFields().getFieldNames().toArray(new String[0])); + collection.rebuildIndex(idx.getFields().getFieldNames().toArray(new String[0])); } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java index 3ec7a6823..9bbbcbdb1 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/event/EventTest.java @@ -19,6 +19,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.events.EventType; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.Employee; import org.dizitart.no2.repository.ObjectRepository; @@ -87,6 +88,9 @@ public void setUp() { .openOrCreate(); } + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + employeeRepository = db.getRepository(Employee.class); listener = new SampleListenerCollection(); employeeRepository.subscribe(listener); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java index ffd30a92b..8d027bdbe 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java @@ -23,6 +23,7 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.common.Constants; import org.dizitart.no2.common.Fields; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.MigrationException; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; @@ -57,6 +58,10 @@ public class MigrationTest { @Before public void setUp() { db = createDb(dbPath); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new OldClass.Converter()); + documentMapper.registerEntityConverter(new OldClass.Literature.Converter()); + faker = new Faker(); } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java index 56ef25c22..daa662f3e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java @@ -19,8 +19,8 @@ import lombok.Data; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Entity; import org.dizitart.no2.repository.annotations.Id; @@ -35,7 +35,7 @@ @Index(value = "fullName", type = IndexType.NON_UNIQUE), @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), }) -public class NewClass implements Mappable { +public class NewClass { @Id private Long empId; private String firstName; @@ -43,42 +43,61 @@ public class NewClass implements Mappable { private String fullName; private Literature literature; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("empId", empId) - .put("firstName", firstName) - .put("familyName", familyName) - .put("fullName", fullName) - .put("literature", literature.write(mapper)); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return NewClass.class; + } + + @Override + public Document toDocument(NewClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument("empId", entity.empId) + .put("firstName", entity.firstName) + .put("familyName", entity.familyName) + .put("fullName", entity.fullName) + .put("literature", nitriteMapper.convert(entity.literature, Document.class)); + } - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - firstName = document.get("firstName", String.class); - familyName = document.get("familyName", String.class); - fullName = document.get("fullName", String.class); + @Override + public NewClass fromDocument(Document document, NitriteMapper nitriteMapper) { + NewClass entity = new NewClass(); + entity.empId = document.get("empId", Long.class); + entity.firstName = document.get("firstName", String.class); + entity.familyName = document.get("familyName", String.class); + entity.fullName = document.get("fullName", String.class); - Document doc = document.get("literature", Document.class); - literature = new Literature(); - literature.read(mapper, doc); + Document doc = document.get("literature", Document.class); + entity.literature = nitriteMapper.convert(doc, Literature.class); + return entity; + } } @Data - public static class Literature implements Mappable { + public static class Literature { private String text; private Integer ratings; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("text", text) - .put("ratings", ratings); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - text = document.get("text", String.class); - ratings = document.get("ratings", Integer.class); + @Override + public Class getEntityType() { + return Literature.class; + } + + @Override + public Document toDocument(Literature entity, NitriteMapper nitriteMapper) { + return Document.createDocument("text", entity.text) + .put("ratings", entity.ratings); + } + + @Override + public Literature fromDocument(Document document, NitriteMapper nitriteMapper) { + Literature entity = new Literature(); + entity.text = document.get("text", String.class); + entity.ratings = document.get("ratings", Integer.class); + return entity; + } } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java index 3bbb09b4e..bc3a078d9 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java @@ -19,8 +19,8 @@ import lombok.Data; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Entity; import org.dizitart.no2.repository.annotations.Id; @@ -36,7 +36,7 @@ @Index(value = "literature.text", type = IndexType.FULL_TEXT), @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), }) -public class OldClass implements Mappable { +public class OldClass { @Id private String uuid; private String empId; @@ -44,42 +44,61 @@ public class OldClass implements Mappable { private String lastName; private Literature literature; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("empId", empId) - .put("uuid", uuid) - .put("firstName", firstName) - .put("lastName", lastName) - .put("literature", literature.write(mapper)); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return OldClass.class; + } + + @Override + public Document toDocument(OldClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument("empId", entity.empId) + .put("uuid", entity.uuid) + .put("firstName", entity.firstName) + .put("lastName", entity.lastName) + .put("literature", nitriteMapper.convert(entity.literature, Document.class)); + } - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", String.class); - uuid = document.get("uuid", String.class); - firstName = document.get("firstName", String.class); - lastName = document.get("lastName", String.class); + @Override + public OldClass fromDocument(Document document, NitriteMapper nitriteMapper) { + OldClass entity = new OldClass(); + entity.empId = document.get("empId", String.class); + entity.uuid = document.get("uuid", String.class); + entity.firstName = document.get("firstName", String.class); + entity.lastName = document.get("lastName", String.class); - Document doc = document.get("literature", Document.class); - literature = new Literature(); - literature.read(mapper, doc); + Document doc = document.get("literature", Document.class); + entity.literature = nitriteMapper.convert(doc, Literature.class); + return entity; + } } @Data - public static class Literature implements Mappable { + public static class Literature { private String text; private Float ratings; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("text", text) - .put("ratings", ratings); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - text = document.get("text", String.class); - ratings = document.get("ratings", Float.class); + @Override + public Class getEntityType() { + return Literature.class; + } + + @Override + public Document toDocument(Literature entity, NitriteMapper nitriteMapper) { + return Document.createDocument("text", entity.text) + .put("ratings", entity.ratings); + } + + @Override + public Literature fromDocument(Document document, NitriteMapper nitriteMapper) { + Literature entity = new Literature(); + entity.text = document.get("text", String.class); + entity.ratings = document.get("ratings", Float.class); + return entity; + } } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java index 345d365b2..0f34b6136 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java @@ -19,8 +19,14 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.*; +import org.dizitart.no2.integration.repository.decorator.ManufacturerConverter; +import org.dizitart.no2.integration.repository.decorator.MiniProduct; +import org.dizitart.no2.integration.repository.decorator.ProductConverter; +import org.dizitart.no2.integration.repository.decorator.ProductIdConverter; +import org.dizitart.no2.integration.transaction.TxData; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.rocksdb.RocksDBModule; import org.junit.After; @@ -102,6 +108,33 @@ protected void openDb() { } else { db = nitriteBuilder.openOrCreate(); } + + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new RepositoryJoinTest.Person.Converter()); + documentMapper.registerEntityConverter(new RepositoryJoinTest.Address.Converter()); + documentMapper.registerEntityConverter(new RepositoryJoinTest.PersonDetails.Converter()); + documentMapper.registerEntityConverter(new Company.CompanyConverter()); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + documentMapper.registerEntityConverter(new Note.NoteConverter()); + documentMapper.registerEntityConverter(new Book.BookConverter()); + documentMapper.registerEntityConverter(new BookId.BookIdConverter()); + documentMapper.registerEntityConverter(new ClassA.ClassAConverter()); + documentMapper.registerEntityConverter(new ClassBConverter()); + documentMapper.registerEntityConverter(new ClassC.ClassCConverter()); + documentMapper.registerEntityConverter(new ElemMatch.Converter()); + documentMapper.registerEntityConverter(new InternalClass.Converter()); + documentMapper.registerEntityConverter(new UniversalTextTokenizerTest.TextData.Converter()); + documentMapper.registerEntityConverter(new SubEmployee.Converter()); + documentMapper.registerEntityConverter(new ProductScore.Converter()); + documentMapper.registerEntityConverter(new PersonEntity.Converter()); + documentMapper.registerEntityConverter(new RepeatableIndexTest.Converter()); + documentMapper.registerEntityConverter(new EncryptedPerson.Converter()); + documentMapper.registerEntityConverter(new TxData.Converter()); + documentMapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); + documentMapper.registerEntityConverter(new ProductConverter()); + documentMapper.registerEntityConverter(new ProductIdConverter()); + documentMapper.registerEntityConverter(new ManufacturerConverter()); + documentMapper.registerEntityConverter(new MiniProduct.Converter()); } @After diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java index a89c10612..be25597bf 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java @@ -24,8 +24,9 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.Company; @@ -70,6 +71,11 @@ public void setUp() { .fieldSeparator(":") .openOrCreate(); + SimpleDocumentMapper mapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + mapper.registerEntityConverter(new Company.CompanyConverter()); + mapper.registerEntityConverter(new EmployeeForCustomSeparator.EmployeeForCustomSeparatorConverter()); + mapper.registerEntityConverter(new Note.NoteConverter()); + repository = db.getRepository(EmployeeForCustomSeparator.class); } @@ -121,7 +127,7 @@ public void testFindByEmbeddedField() { @Index(value = "address", type = IndexType.FULL_TEXT), @Index(value = "employeeNote:text", type = IndexType.FULL_TEXT) }) - public static class EmployeeForCustomSeparator implements Serializable, Mappable { + public static class EmployeeForCustomSeparator implements Serializable { @Id @Getter @Setter @@ -159,28 +165,39 @@ public EmployeeForCustomSeparator(EmployeeForCustomSeparator copy) { employeeNote = copy.employeeNote; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("empId", empId) - .put("joinDate", joinDate) - .put("address", address) - .put("blob", blob) - .put("company", company.write(mapper)) - .put("employeeNote", employeeNote.write(mapper)); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); - blob = document.get("blob", byte[].class); - employeeNote = new Note(); - Document doc = document.get("employeeNote", Document.class); - employeeNote.read(mapper, doc); - company = new Company(); - doc = document.get("company", Document.class); - company.read(mapper, doc); + public static class EmployeeForCustomSeparatorConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmployeeForCustomSeparator.class; + } + + @Override + public Document toDocument(EmployeeForCustomSeparator entity, NitriteMapper nitriteMapper) { + return Document.createDocument().put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address) + .put("blob", entity.blob) + .put("company", nitriteMapper.convert(entity.company, Document.class)) + .put("employeeNote", nitriteMapper.convert(entity.employeeNote, Document.class)); + } + + @Override + public EmployeeForCustomSeparator fromDocument(Document document, NitriteMapper nitriteMapper) { + EmployeeForCustomSeparator entity = new EmployeeForCustomSeparator(); + + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + entity.blob = document.get("blob", byte[].class); + + Document doc = document.get("employeeNote", Document.class); + entity.employeeNote = nitriteMapper.convert(doc, Note.class); + + doc = document.get("company", Document.class); + entity.company = nitriteMapper.convert(doc, Company.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java index 1a52708ce..7096ddaaf 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -27,20 +27,29 @@ * @author Anindya Chatterjee. */ @Data -class InternalClass implements Mappable { +class InternalClass { @Id private long id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("name", name); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return InternalClass.class; + } + + @Override + public Document toDocument(InternalClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - name = document.get("name", String.class); + @Override + public InternalClass fromDocument(Document document, NitriteMapper nitriteMapper) { + InternalClass entity = new InternalClass(); + entity.id = document.get("id", Long.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java index dedd5e256..278977722 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java @@ -17,6 +17,7 @@ package org.dizitart.no2.integration.repository; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.WithNitriteId; import org.dizitart.no2.Nitrite; @@ -53,6 +54,9 @@ public class NitriteIdAsIdTest { @Before public void before() { db = TestUtil.createDb(fileName); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); + repo = db.getRepository(WithNitriteId.class); } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java index e070d485d..a34a91078 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java @@ -17,6 +17,7 @@ package org.dizitart.no2.integration.repository; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.*; import org.dizitart.no2.Nitrite; @@ -50,6 +51,18 @@ public class ObjectRepositoryNegativeTest { @Before public void setUp() { db = TestUtil.createDb(dbPath); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new WithPublicField.Converter()); + documentMapper.registerEntityConverter(new WithObjectId.Converter()); + documentMapper.registerEntityConverter(new WithOutId.Converter()); + documentMapper.registerEntityConverter(new WithoutEmbeddedId.Converter()); + documentMapper.registerEntityConverter(new WithoutEmbeddedId.NestedId.Converter()); + documentMapper.registerEntityConverter(new WithEmptyStringId.Converter()); + documentMapper.registerEntityConverter(new WithNullId.Converter()); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + documentMapper.registerEntityConverter(new Company.CompanyConverter()); + documentMapper.registerEntityConverter(new Note.NoteConverter()); + documentMapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); } @After @@ -162,7 +175,7 @@ public void testRemoveNoId() { repository.remove(object); } - @Test(expected = ValidationException.class) + @Test(expected = ObjectMappingException.class) public void testProjectionFailedInstantiate() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 546ae627a..87adaac15 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -22,14 +22,18 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.meta.Attributes; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.MappableMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.*; +import org.dizitart.no2.integration.repository.decorator.ManufacturerConverter; +import org.dizitart.no2.integration.repository.decorator.MiniProduct; +import org.dizitart.no2.integration.repository.decorator.ProductConverter; +import org.dizitart.no2.integration.repository.decorator.ProductIdConverter; import org.dizitart.no2.repository.Cursor; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Entity; @@ -66,7 +70,25 @@ public class ObjectRepositoryTest { @Before public void setUp() { - NitriteMapper mapper = new MappableMapper(); + SimpleDocumentMapper mapper = new SimpleDocumentMapper(); + mapper.registerEntityConverter(new InternalClass.Converter()); + mapper.registerEntityConverter(new EmployeeEntity.Converter()); + mapper.registerEntityConverter(new StressRecord.Converter()); + mapper.registerEntityConverter(new WithClassField.Converter()); + mapper.registerEntityConverter(new WithDateId.Converter()); + mapper.registerEntityConverter(new WithTransientField.Converter()); + mapper.registerEntityConverter(new WithOutId.Converter()); + mapper.registerEntityConverter(new ChildClass.Converter()); + mapper.registerEntityConverter(new WithOutGetterSetter.Converter()); + mapper.registerEntityConverter(new WithPrivateConstructor.Converter()); + mapper.registerEntityConverter(new WithPublicField.Converter()); + mapper.registerEntityConverter(new Employee.EmployeeConverter()); + mapper.registerEntityConverter(new Company.CompanyConverter()); + mapper.registerEntityConverter(new ProductConverter()); + mapper.registerEntityConverter(new ProductIdConverter()); + mapper.registerEntityConverter(new ManufacturerConverter()); + mapper.registerEntityConverter(new MiniProduct.Converter()); + RocksDBModule storeModule = RocksDBModule.withConfig() .filePath(dbPath) .build(); @@ -350,7 +372,7 @@ public void testIssue217() { @Index(value = "firstName", type = IndexType.NON_UNIQUE), @Index(value = "lastName", type = IndexType.NON_UNIQUE), }) - private static class EmployeeEntity implements Mappable { + private static class EmployeeEntity { private static final Faker faker = new Faker(); @Id @@ -364,18 +386,28 @@ public EmployeeEntity() { lastName = faker.name().lastName(); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("firstName", firstName) - .put("lastName", lastName); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - firstName = document.get("firstName", String.class); - lastName = document.get("lastName", String.class); + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmployeeEntity.class; + } + + @Override + public Document toDocument(EmployeeEntity entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("firstName", entity.firstName) + .put("lastName", entity.lastName); + } + + @Override + public EmployeeEntity fromDocument(Document document, NitriteMapper nitriteMapper) { + EmployeeEntity entity = new EmployeeEntity(); + entity.id = document.get("id", Long.class); + entity.firstName = document.get("firstName", String.class); + entity.lastName = document.get("lastName", String.class); + return entity; + } } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 5078a31e9..348ec112c 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -66,7 +66,7 @@ public void testRepositoryFactory() { @Test(expected = ValidationException.class) public void testNullType() { RepositoryFactory factory = new RepositoryFactory(new CollectionFactory(new LockService())); - factory.getRepository(db.getConfig(), null, "dummy"); + factory.getRepository(db.getConfig(), (Class) null, "dummy"); } @Test diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java index 4dacdbff8..05634377f 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java @@ -22,7 +22,7 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.Lookup; import org.dizitart.no2.common.RecordStream; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.repository.ObjectRepository; @@ -31,10 +31,7 @@ import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Set; +import java.util.*; import static org.dizitart.no2.collection.Document.createDocument; import static org.dizitart.no2.collection.FindOptions.skipBy; @@ -138,80 +135,120 @@ public void testRemove() { } @Data - public static class Person implements Mappable { + public static class Person { @Id private NitriteId nitriteId; private String id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("id", id) - .put("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return Person.class; + } + + @Override + public Document toDocument(Person entity, NitriteMapper nitriteMapper) { + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("id", entity.id) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - id = document.get("id", String.class); - name = document.get("name", String.class); + @Override + public Person fromDocument(Document document, NitriteMapper nitriteMapper) { + Person entity = new Person(); + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.id = document.get("id", String.class); + entity.name = document.get("name", String.class); + return entity; + } } } @Data - public static class Address implements Mappable { + public static class Address { @Id private NitriteId nitriteId; private String personId; private String street; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("personId", personId) - .put("street", street); - } + public static class Converter implements EntityConverter
{ + + @Override + public Class
getEntityType() { + return Address.class; + } + + @Override + public Document toDocument(Address entity, NitriteMapper nitriteMapper) { + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("personId", entity.personId) + .put("street", entity.street); + } - @Override - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - personId = document.get("personId", String.class); - street = document.get("street", String.class); + @Override + public Address fromDocument(Document document, NitriteMapper nitriteMapper) { + Address entity = new Address(); + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.personId = document.get("personId", String.class); + entity.street = document.get("street", String.class); + return entity; + } } } @Data - public static class PersonDetails implements Mappable { + public static class PersonDetails { @Id private NitriteId nitriteId; private String id; private String name; private List
addresses; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("personId", id) - .put("street", name) - .put("addresses", addresses); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PersonDetails.class; + } + + @Override + public Document toDocument(PersonDetails entity, NitriteMapper nitriteMapper) { + List documents = new ArrayList<>(); + if (entity.addresses != null) { + for (Address address : entity.addresses) { + documents.add(nitriteMapper.convert(address, Document.class)); + } + } + + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("personId", entity.id) + .put("street", entity.name) + .put("addresses", documents); + } + + @Override + public PersonDetails fromDocument(Document document, NitriteMapper nitriteMapper) { + PersonDetails entity = new PersonDetails(); + + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.id = document.get("id", String.class); + entity.name = document.get("name", String.class); + + Collection documents = document.get("addresses", Collection.class); + if (documents != null) { + entity.addresses = new ArrayList<>(); + for (Document doc : documents) { + Address address = nitriteMapper.convert(doc, Address.class); + entity.addresses.add(address); + } + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - id = document.get("id", String.class); - name = document.get("name", String.class); - Set documents = document.get("addresses", Set.class); - this.addresses = new ArrayList<>(); - for (Document doc : documents) { - Address address = new Address(); - address.read(mapper, doc); - addresses.add(address); + return entity; } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java index 68015c9f3..9be6cd740 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java @@ -18,15 +18,16 @@ package org.dizitart.no2.integration.repository; import lombok.Getter; -import org.dizitart.no2.integration.repository.data.*; import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.SortOrder; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.FilterException; import org.dizitart.no2.exceptions.InvalidIdException; import org.dizitart.no2.exceptions.NotIdentifiableException; import org.dizitart.no2.filters.Filter; +import org.dizitart.no2.integration.repository.data.*; import org.dizitart.no2.repository.Cursor; import org.dizitart.no2.repository.ObjectRepository; import org.junit.Test; @@ -364,21 +365,20 @@ public void testElemMatchFilter() { final ProductScore score6 = new ProductScore("xyz", 8); ObjectRepository repository = db.getRepository(ElemMatch.class); - ElemMatch e1 = new ElemMatch() {{ - setId(1); - setStrArray(new String[]{"a", "b"}); - setProductScores(new ProductScore[]{score1, score4}); - }}; - ElemMatch e2 = new ElemMatch() {{ - setId(2); - setStrArray(new String[]{"d", "e"}); - setProductScores(new ProductScore[]{score2, score5}); - }}; - ElemMatch e3 = new ElemMatch() {{ - setId(3); - setStrArray(new String[]{"a", "f"}); - setProductScores(new ProductScore[]{score3, score6}); - }}; + ElemMatch e1 = new ElemMatch(); + e1.setId(1); + e1.setStrArray(new String[]{"a", "b"}); + e1.setProductScores(new ProductScore[]{score1, score4}); + + ElemMatch e2 = new ElemMatch(); + e2.setId(2); + e2.setStrArray(new String[]{"d", "e"}); + e2.setProductScores(new ProductScore[]{score2, score5}); + + ElemMatch e3 = new ElemMatch(); + e3.setId(3); + e3.setStrArray(new String[]{"a", "f"}); + e3.setProductScores(new ProductScore[]{score3, score6}); repository.insert(e1, e2, e3); @@ -563,24 +563,37 @@ public void testIdSet() { @Test public void testBetweenFilter() { @Getter - class TestData implements Mappable { + class TestData { private Date age; public TestData(Date age) { this.age = age; } + } + + class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TestData.class; + } @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("age", age); + public Document toDocument(TestData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("age", entity.age); } @Override - public void read(NitriteMapper mapper, Document document) { - age = document.get("age", Date.class); + public TestData fromDocument(Document document, NitriteMapper nitriteMapper) { + TestData entity = new TestData(new Date()); + entity.age = document.get("age", Date.class); + return entity; } } + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new Converter()); + TestData data1 = new TestData(new GregorianCalendar(2020, Calendar.JANUARY, 11).getTime()); TestData data2 = new TestData(new GregorianCalendar(2021, Calendar.FEBRUARY, 12).getTime()); TestData data3 = new TestData(new GregorianCalendar(2022, Calendar.MARCH, 13).getTime()); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index 8b2237f0b..669da19f5 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -20,8 +20,9 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.index.NitriteTextIndexer; import org.dizitart.no2.index.fulltext.Languages; @@ -55,6 +56,8 @@ public class UniversalTextTokenizerTest extends BaseObjectRepositoryTest { public void setUp() { openDb(); textRepository = db.getRepository(TextData.class); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TextData.Converter()); for (int i = 0; i < 10; i++) { TextData data = new TextData(); @@ -168,20 +171,29 @@ public void testUniversalFullTextIndexing() { @Indices( @Index(value = "text", type = IndexType.FULL_TEXT) ) - public static class TextData implements Mappable { + public static class TextData { public int id; public String text; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("text", text); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return TextData.class; + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Integer.class); - text = document.get("text", String.class); + @Override + public Document toDocument(TextData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("text", entity.text); + } + + @Override + public TextData fromDocument(Document document, NitriteMapper nitriteMapper) { + TextData entity = new TextData(); + entity.id = document.get("id", Integer.class); + entity.text = document.get("text", String.class); + return entity; + } } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index c80c7b060..cb3c972ca 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Entity; @@ -39,7 +39,7 @@ @Index(value = "description", type = IndexType.FULL_TEXT), @Index(value = { "price", "publisher" }) }) -public class Book implements Mappable { +public class Book { @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) private BookId bookId; @@ -51,22 +51,32 @@ public class Book implements Mappable { private String description; - @Override - public Document write(NitriteMapper mapper) { - return createDocument("book_id", mapper.convert(bookId, Document.class)) - .put("publisher", publisher) - .put("price", price) - .put("tags", tags) - .put("description", description); - } + public static class BookConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Book.class; + } + + @Override + public Document toDocument(Book entity, NitriteMapper nitriteMapper) { + return createDocument("book_id", nitriteMapper.convert(entity.bookId, Document.class)) + .put("publisher", entity.publisher) + .put("price", entity.price) + .put("tags", entity.tags) + .put("description", entity.description); + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - bookId = mapper.convert(document.get("book_id"), BookId.class); - publisher = document.get("publisher", String.class); - price = document.get("price", Double.class); - tags = (List) document.get("tags", List.class); - description = document.get("description", String.class); + @Override + @SuppressWarnings("unchecked") + public Book fromDocument(Document document, NitriteMapper nitriteMapper) { + Book entity = new Book(); + entity.bookId = nitriteMapper.convert(document.get("book_id"), BookId.class); + entity.publisher = document.get("publisher", String.class); + entity.price = document.get("price", Double.class); + entity.tags = (List) document.get("tags", List.class); + entity.description = document.get("description", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java index 2afcc42f6..4aa11f1af 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import static org.dizitart.no2.collection.Document.createDocument; @@ -28,24 +28,34 @@ * @author Anindya Chatterjee */ @Data -public class BookId implements Mappable { +public class BookId { private String isbn; private String name; private String author; - @Override - public Document write(NitriteMapper mapper) { - return createDocument("isbn", isbn) - .put("book_name", name) - .put("author", author); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - isbn = document.get("isbn", String.class); - name = document.get("book_name", String.class); - author = document.get("author", String.class); + public static class BookIdConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return BookId.class; + } + + @Override + public Document toDocument(BookId entity, NitriteMapper nitriteMapper) { + return createDocument("isbn", entity.isbn) + .put("book_name", entity.name) + .put("author", entity.author); + } + + @Override + public BookId fromDocument(Document document, NitriteMapper nitriteMapper) { + BookId entity = new BookId(); + entity.isbn = document.get("isbn", String.class); + entity.name = document.get("book_name", String.class); + entity.author = document.get("author", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java index d3c2ba644..6f0470d26 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java @@ -20,9 +20,12 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.InheritIndices; +import java.util.Date; + /** * @author Anindya Chatterjee */ @@ -32,14 +35,30 @@ public class ChildClass extends ParentClass { private String name; - @Override - public Document write(NitriteMapper mapper) { - return super.write(mapper).put("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ChildClass.class; + } + + @Override + public Document toDocument(ChildClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.getName()) + .put("id", entity.getId()) + .put("date", entity.getDate()) + .put("text", entity.getText()); + } - @Override - public void read(NitriteMapper mapper, Document document) { - super.read(mapper, document); - name = document.get("name", String.class); + @Override + public ChildClass fromDocument(Document document, NitriteMapper nitriteMapper) { + ChildClass entity = new ChildClass(); + entity.setId(document.get("id", Long.class)); + entity.setDate(document.get("date", Date.class)); + entity.setText(document.get("text", String.class)); + entity.setName(document.get("name", String.class)); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java index a8161c4e2..4e736981d 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java @@ -22,14 +22,14 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.UUID; @EqualsAndHashCode @ToString -public class ClassA implements Mappable { +public class ClassA { @Getter @Setter private ClassB b; @@ -53,23 +53,33 @@ public static ClassA create(int seed) { return classA; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("b", b != null ? b.write(mapper) : null) - .put("uid", uid) - .put("string", string) - .put("blob", blob); - } + public static class ClassAConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassA.class; + } + + @Override + public Document toDocument(ClassA entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("b", nitriteMapper.convert(entity.b, Document.class)) + .put("uid", entity.uid) + .put("string", entity.string) + .put("blob", entity.blob); + } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document.get("b") != null) { - b = new ClassB(); - b.read(mapper, document.get("b", Document.class)); + @Override + public ClassA fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassA entity = new ClassA(); + if (document.get("b") != null) { + Document doc = document.get("b", Document.class); + entity.b = nitriteMapper.convert(doc, ClassB.class); + } + entity.uid = document.get("uid", UUID.class); + entity.string = document.get("string", String.class); + entity.blob = document.get("blob", byte[].class); + return entity; } - uid = document.get("uid", UUID.class); - string = document.get("string", String.class); - blob = document.get("blob", byte[].class); } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java index a546b91e2..51cc9b416 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java @@ -21,13 +21,10 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; @EqualsAndHashCode @ToString -class ClassB implements Comparable, Mappable { +class ClassB implements Comparable { @Getter @Setter private int number; @@ -47,16 +44,4 @@ public int compareTo(ClassB o) { return Integer.compare(number, o.number); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("number", number) - .put("text", text); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - number = document.get("number", Integer.class); - text = document.get("text", String.class); - } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java new file mode 100644 index 000000000..6d5f88bab --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.data; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ClassBConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassB.class; + } + + @Override + public Document toDocument(ClassB entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("number", entity.getNumber()) + .put("text", entity.getText()); + } + + @Override + public ClassB fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassB entity = new ClassB(); + if (document.get("number") != null) { + entity.setNumber(document.get("number", Integer.class)); + } + entity.setText(document.get("text", String.class)); + return entity; + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java index 860fe1e93..0dd8d58d6 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java @@ -22,12 +22,12 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; @EqualsAndHashCode @ToString -public class ClassC implements Mappable { +public class ClassC { @Getter @Setter private long id; @@ -46,21 +46,37 @@ public static ClassC create(int seed) { return classC; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("id", id) - .put("digit", digit) - .put("parent", parent != null ? parent.write(mapper) : null); - } + public static class ClassCConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassC.class; + } + + @Override + public Document toDocument(ClassC entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("id", entity.id) + .put("digit", entity.digit) + .put("parent", nitriteMapper.convert(entity.parent, Document.class)); + } + + @Override + public ClassC fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassC entity = new ClassC(); + if (document.get("id") != null) { + entity.id = document.get("id", Long.class); + } + + if (document.get("digit") != null) { + entity.digit = document.get("digit", Double.class); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - digit = document.get("digit", Double.class); - if (document.get("parent") != null) { - parent = new ClassA(); - parent.read(mapper, document.get("parent", Document.class)); + if (document.get("parent") != null) { + Document doc = document.get("parent", Document.class); + entity.parent = nitriteMapper.convert(doc, ClassA.class); + } + return entity; } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java index 944e17358..54c1acf94 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -39,7 +39,7 @@ @Indices({ @Index(value = "companyName") }) -public class Company implements Serializable, Mappable { +public class Company implements Serializable { @Id(fieldName = "company_id") @Getter @Setter @@ -61,25 +61,6 @@ public class Company implements Serializable, Mappable { @Setter private Map> employeeRecord; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("company_id", companyId) - .put("companyName", companyName) - .put("dateCreated", dateCreated) - .put("departments", departments) - .put("employeeRecord", employeeRecord); - } - - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - companyId = document.get("company_id", Long.class); - companyName = document.get("companyName", String.class); - dateCreated = document.get("dateCreated", Date.class); - departments = document.get("departments", List.class); - employeeRecord = document.get("employeeRecord", Map.class); - } - @Override public String toString() { return "Company{" + @@ -89,4 +70,32 @@ public String toString() { ", departments=" + departments + '}'; } + + public static class CompanyConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Company.class; + } + + @Override + public Document toDocument(Company entity, NitriteMapper nitriteMapper) { + return Document.createDocument("company_id", entity.companyId) + .put("companyName", entity.companyName) + .put("dateCreated", entity.dateCreated) + .put("departments", entity.departments) + .put("employeeRecord", entity.employeeRecord); + } + + @Override + public Company fromDocument(Document document, NitriteMapper nitriteMapper) { + Company entity = new Company(); + entity.companyId = document.get("company_id", Long.class); + entity.companyName = document.get("companyName", String.class); + entity.dateCreated = document.get("dateCreated", Date.class); + entity.departments = document.get("departments", List.class); + entity.employeeRecord = document.get("employeeRecord", Map.class); + return entity; + } + } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java index 26e0dcf4a..6d83c2940 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java @@ -19,6 +19,9 @@ import com.github.javafaker.Faker; import lombok.val; +import org.dizitart.no2.integration.repository.decorator.Manufacturer; +import org.dizitart.no2.integration.repository.decorator.Product; +import org.dizitart.no2.integration.repository.decorator.ProductId; import java.nio.charset.StandardCharsets; import java.util.*; @@ -102,6 +105,15 @@ public static Book randomBook() { return book; } + public static Product randomProduct() { + Product product = new Product(); + product.setProductName(faker.name().name()); + product.setProductId(randomProductId()); + product.setManufacturer(randomManufacturer()); + product.setPrice(Double.parseDouble(faker.commerce().price())); + return product; + } + private static List departments() { return new ArrayList() {{ add("dev"); @@ -114,4 +126,19 @@ private static List departments() { add("support"); }}; } + + private static ProductId randomProductId() { + ProductId productId = new ProductId(); + productId.setProductCode(faker.code().ean13()); + productId.setUniqueId(UUID.randomUUID().toString()); + return productId; + } + + private static Manufacturer randomManufacturer() { + Manufacturer manufacturer = new Manufacturer(); + manufacturer.setUniqueId(random.nextInt()); + manufacturer.setName(faker.name().name()); + manufacturer.setAddress(faker.address().fullAddress()); + return manufacturer; + } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java index cb5304e29..3ef376e44 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.ArrayList; @@ -29,38 +29,46 @@ * @author Anindya Chatterjee */ @Data -public class ElemMatch implements Mappable { +public class ElemMatch { private long id; private String[] strArray; private ProductScore[] productScores; - @Override - public Document write(NitriteMapper mapper) { - List list = new ArrayList<>(); - if (productScores != null) { - for (ProductScore productScore : productScores) { - Document document = productScore.write(mapper); - list.add(document); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ElemMatch.class; } - return Document.createDocument("id", id) - .put("strArray", strArray) - .put("productScores", list); - } + @Override + public Document toDocument(ElemMatch entity, NitriteMapper nitriteMapper) { + List list = new ArrayList<>(); + if (entity.productScores != null) { + for (ProductScore productScore : entity.productScores) { + Document document = nitriteMapper.convert(productScore, Document.class); + list.add(document); + } + } + + return Document.createDocument("id", entity.id) + .put("strArray", entity.strArray) + .put("productScores", list); + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - strArray = document.get("strArray", String[].class); - List list = document.get("productScores", List.class); - if (list != null) { - productScores = new ProductScore[list.size()]; - for (int i = 0; i < list.size(); i++) { - productScores[i] = new ProductScore(); - productScores[i].read(mapper, list.get(i)); + @Override + public ElemMatch fromDocument(Document document, NitriteMapper nitriteMapper) { + ElemMatch entity = new ElemMatch(); + entity.id = document.get("id", Long.class); + entity.strArray = document.get("strArray", String[].class); + List list = document.get("productScores", List.class); + if (list != null) { + entity.productScores = new ProductScore[list.size()]; + for (int i = 0; i < list.size(); i++) { + entity.productScores[i] = nitriteMapper.convert(list.get(i), ProductScore.class); + } } + return entity; } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java index 7204f5654..dbba83965 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java @@ -22,9 +22,9 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -39,7 +39,7 @@ @Index(value = "joinDate", type = IndexType.NON_UNIQUE) @Index(value = "address", type = IndexType.FULL_TEXT) @Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) -public class Employee implements Serializable, Mappable { +public class Employee implements Serializable { @Id @Getter @Setter @@ -82,28 +82,39 @@ public Employee(Employee copy) { emailAddress = copy.emailAddress; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("empId", empId) - .put("joinDate", joinDate) - .put("address", address) - .put("blob", blob) - .put("emailAddress", emailAddress) - .put("employeeNote", employeeNote != null ? employeeNote.write(mapper) : null); - } + public static class EmployeeConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Employee.class; + } + + @Override + public Document toDocument(Employee entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address) + .put("blob", entity.blob) + .put("emailAddress", entity.emailAddress) + .put("employeeNote", nitriteMapper.convert(entity.employeeNote, Document.class)); + } + + @Override + public Employee fromDocument(Document document, NitriteMapper nitriteMapper) { + Employee entity = new Employee(); + + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + entity.blob = document.get("blob", byte[].class); + entity.emailAddress = document.get("emailAddress", String.class); - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); - blob = document.get("blob", byte[].class); - emailAddress = document.get("emailAddress", String.class); - - if (document.get("employeeNote") != null) { - employeeNote = new Note(); - employeeNote.read(mapper, document.get("employeeNote", Document.class)); + if (document.get("employeeNote") != null) { + Document doc = document.get("employeeNote", Document.class); + entity.employeeNote = nitriteMapper.convert(doc, Note.class);; + } + return entity; } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java index 8b9d4b23b..7003db015 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Entity; @@ -30,25 +30,35 @@ */ @Data @Entity -public class EncryptedPerson implements Mappable { +public class EncryptedPerson { private String name; private String creditCardNumber; private String cvv; private Date expiryDate; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("creditCardNumber", creditCardNumber) - .put("cvv", cvv) - .put("expiryDate", expiryDate); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EncryptedPerson.class; + } + + @Override + public Document toDocument(EncryptedPerson entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("creditCardNumber", entity.creditCardNumber) + .put("cvv", entity.cvv) + .put("expiryDate", entity.expiryDate); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - creditCardNumber = document.get("creditCardNumber", String.class); - cvv = document.get("cvv", String.class); - expiryDate = document.get("expiryDate", Date.class); + @Override + public EncryptedPerson fromDocument(Document document, NitriteMapper nitriteMapper) { + EncryptedPerson entity = new EncryptedPerson(); + entity.name = document.get("name", String.class); + entity.creditCardNumber = document.get("creditCardNumber", String.class); + entity.cvv = document.get("cvv", String.class); + entity.expiryDate = document.get("expiryDate", Date.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Note.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Note.java index 67dc307d1..7bf7f1726 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Note.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Note.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.io.Serializable; @@ -30,7 +30,7 @@ * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class Note implements Serializable, Mappable { +public class Note implements Serializable { @Getter @Setter private Long noteId; @@ -38,14 +38,26 @@ public class Note implements Serializable, Mappable { @Setter private String text; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("noteId", noteId).put("text", text); - } + public static class NoteConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Note.class; + } + + @Override + public Document toDocument(Note entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("noteId", entity.noteId) + .put("text", entity.text); + } - @Override - public void read(NitriteMapper mapper, Document document) { - noteId = document.get("noteId", Long.class); - text = document.get("text", String.class); + @Override + public Note fromDocument(Document document, NitriteMapper nitriteMapper) { + Note entity = new Note(); + entity.noteId = document.get("noteId", Long.class); + entity.text = document.get("text", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java index bc2514543..844e39223 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java @@ -19,8 +19,6 @@ import lombok.Getter; import lombok.Setter; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -36,18 +34,4 @@ public class ParentClass extends SuperDuperClass { @Id protected Long id; private Date date; - - @Override - public Document write(NitriteMapper mapper) { - return super.write(mapper) - .put("id", id) - .put("date", date); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - super.read(mapper, document); - id = document.get("id", Long.class); - date = document.get("date", Date.class); - } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java index 2b90075fa..b036118c7 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java @@ -19,9 +19,9 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Entity; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -37,7 +37,7 @@ @Index(value = "name", type = IndexType.FULL_TEXT), @Index(value = "status", type = IndexType.NON_UNIQUE) }) -public class PersonEntity implements Mappable { +public class PersonEntity { @Id private String uuid; private String name; @@ -56,24 +56,34 @@ public PersonEntity(String name) { this.dateCreated = new Date(); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("uuid", uuid) - .put("name", name) - .put("status", status) - .put("friend", friend != null ? friend.write(mapper) : null) - .put("dateCreated", dateCreated); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PersonEntity.class; + } + + @Override + public Document toDocument(PersonEntity entity, NitriteMapper nitriteMapper) { + return Document.createDocument("uuid", entity.uuid) + .put("name", entity.name) + .put("status", entity.status) + .put("friend", entity.friend != null ? nitriteMapper.convert(entity.friend, Document.class) : null) + .put("dateCreated", entity.dateCreated); + } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - uuid = document.get("uuid", String.class); - name = document.get("name", String.class); - status = document.get("status", String.class); - dateCreated = document.get("dateCreated", Date.class); - friend = new PersonEntity(); - friend.read(mapper, document.get("friend", Document.class)); + @Override + public PersonEntity fromDocument(Document document, NitriteMapper nitriteMapper) { + if (document != null) { + PersonEntity entity = new PersonEntity(); + entity.uuid = document.get("uuid", String.class); + entity.name = document.get("name", String.class); + entity.status = document.get("status", String.class); + entity.dateCreated = document.get("dateCreated", Date.class); + entity.friend = nitriteMapper.convert(document.get("friend", Document.class), PersonEntity.class); + return entity; + } + return null; } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java index 65550d182..9ebe19057 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,7 +28,7 @@ */ @Getter @Setter -public class ProductScore implements Mappable { +public class ProductScore { private String product; private int score; @@ -40,15 +40,25 @@ public ProductScore(String product, int score) { this.score = score; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("product", product) - .put("score", score); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ProductScore.class; + } + + @Override + public Document toDocument(ProductScore entity, NitriteMapper nitriteMapper) { + return Document.createDocument("product", entity.product) + .put("score", entity.score); + } - @Override - public void read(NitriteMapper mapper, Document document) { - product = document.get("product", String.class); - score = document.get("score", Integer.class); + @Override + public ProductScore fromDocument(Document document, NitriteMapper nitriteMapper) { + ProductScore entity = new ProductScore(); + entity.product = document.get("product", String.class); + entity.score = document.get("score", Integer.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java index 1ecadb243..67f77060e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java @@ -19,9 +19,9 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Index; /** @@ -31,22 +31,32 @@ @Index(value = "firstName") @Index(value = "age", type = IndexType.NON_UNIQUE) @Index(value = "lastName", type = IndexType.FULL_TEXT) -public class RepeatableIndexTest implements Mappable { +public class RepeatableIndexTest { private String firstName; private Integer age; private String lastName; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("firstName", firstName) - .put("age", age) - .put("lastName", lastName); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return RepeatableIndexTest.class; + } + + @Override + public Document toDocument(RepeatableIndexTest entity, NitriteMapper nitriteMapper) { + return Document.createDocument("firstName", entity.firstName) + .put("age", entity.age) + .put("lastName", entity.lastName); + } - @Override - public void read(NitriteMapper mapper, Document document) { - firstName = document.get("firstName", String.class); - age = document.get("age", Integer.class); - lastName = document.get("lastName", String.class); + @Override + public RepeatableIndexTest fromDocument(Document document, NitriteMapper nitriteMapper) { + RepeatableIndexTest entity = new RepeatableIndexTest(); + entity.firstName = document.get("firstName", String.class); + entity.age = document.get("age", Integer.class); + entity.lastName = document.get("lastName", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java index d1cb199c4..9e74e8fb0 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,28 +28,37 @@ */ @Getter @Setter -public class StressRecord implements Mappable { +public class StressRecord { private String firstName; private boolean processed; private String lastName; private boolean failed; private String notes; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("firstName", firstName) - .put("processed", processed) - .put("lastName", lastName) - .put("failed", failed) - .put("notes", notes); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return StressRecord.class; + } + + @Override + public Document toDocument(StressRecord entity, NitriteMapper nitriteMapper) { + return Document.createDocument().put("firstName", entity.firstName) + .put("processed", entity.processed) + .put("lastName", entity.lastName) + .put("failed", entity.failed) + .put("notes", entity.notes); + } - @Override - public void read(NitriteMapper mapper, Document document) { - firstName = document.get("firstName", String.class); - processed = document.get("processed", Boolean.class); - lastName = document.get("lastName", String.class); - failed = document.get("failed", Boolean.class); - notes = document.get("notes", String.class); + @Override + public StressRecord fromDocument(Document document, NitriteMapper nitriteMapper) { + StressRecord entity = new StressRecord(); + entity.firstName = document.get("firstName", String.class); + entity.processed = document.get("processed", Boolean.class); + entity.lastName = document.get("lastName", String.class); + entity.failed = document.get("failed", Boolean.class); + entity.notes = document.get("notes", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java index 8eaa3faf1..ddea32812 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.Date; @@ -30,7 +30,7 @@ * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class SubEmployee implements Mappable { +public class SubEmployee { @Getter @Setter private Long empId; @@ -43,18 +43,28 @@ public class SubEmployee implements Mappable { @Setter private String address; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("empId", empId) - .put("joinDate", joinDate) - .put("address", address); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return SubEmployee.class; + } + + @Override + public Document toDocument(SubEmployee entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address); + } - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); + @Override + public SubEmployee fromDocument(Document document, NitriteMapper nitriteMapper) { + SubEmployee entity = new SubEmployee(); + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java index 198f16d0c..d39ddf146 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java @@ -19,10 +19,7 @@ import lombok.Getter; import lombok.Setter; -import org.dizitart.no2.collection.Document; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Index; /** @@ -31,16 +28,6 @@ @Getter @Setter @Index(value = "text", type = IndexType.FULL_TEXT) -public class SuperDuperClass implements Mappable { +public class SuperDuperClass { private String text; - - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("text", text); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - text = document.get("text", String.class); - } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java index 79cafe0dc..bdeef8744 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,20 +29,29 @@ */ @Getter @Setter -public class WithClassField implements Mappable { +public class WithClassField { @Id private String name; private Class clazz; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("clazz", clazz); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return WithClassField.class; + } + + @Override + public Document toDocument(WithClassField entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("clazz", entity.clazz); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - clazz = document.get("clazz", Class.class); + @Override + public WithClassField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithClassField entity = new WithClassField(); + entity.name = document.get("name", String.class); + entity.clazz = document.get("clazz", Class.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java index 229f1aaf7..125e94b05 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.Date; @@ -32,19 +32,29 @@ @Getter @Setter @EqualsAndHashCode -public class WithDateId implements Mappable { +public class WithDateId { private Date id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("id", id); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithDateId.class; + } + + @Override + public Document toDocument(WithDateId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("id", entity.id); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - id = document.get("id", Date.class); + @Override + public WithDateId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithDateId entity = new WithDateId(); + entity.name = document.get("name", String.class); + entity.id = document.get("id", Date.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java index a0048a6bb..15792f12e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,17 +29,27 @@ */ @Getter @Setter -public class WithEmptyStringId implements Mappable { +public class WithEmptyStringId { @Id private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithEmptyStringId.class; + } + + @Override + public Document toDocument(WithEmptyStringId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); + @Override + public WithEmptyStringId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithEmptyStringId entity = new WithEmptyStringId(); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java index 2364be76d..6748256c6 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java @@ -20,7 +20,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -28,21 +28,31 @@ * @author Anindya Chatterjee */ @Data -public class WithNitriteId implements Mappable { +public class WithNitriteId { @Id public NitriteId idField; public String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("idField", idField) - .put("name", name); - } + public static class WithNitriteIdConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithNitriteId.class; + } + + @Override + public Document toDocument(WithNitriteId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("idField", entity.idField) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - idField = document.get("idField", NitriteId.class); - name = document.get("name", String.class); + @Override + public WithNitriteId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithNitriteId entity = new WithNitriteId(); + entity.idField = document.get("idField", NitriteId.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java index 0dc3fb68b..e5ca44b78 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,21 +29,31 @@ */ @Getter @Setter -public class WithNullId implements Mappable { +public class WithNullId { @Id private String name; private long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithNullId.class; + } + + @Override + public Document toDocument(WithNullId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithNullId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithNullId entity = new WithNullId(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java index 5f00f97d4..634a90fb8 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,17 +29,27 @@ */ @Getter @Setter -public class WithObjectId implements Mappable { +public class WithObjectId { @Id private WithOutId withOutId; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("withOutId", withOutId); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithObjectId.class; + } + + @Override + public Document toDocument(WithObjectId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("withOutId", entity.withOutId); + } - @Override - public void read(NitriteMapper mapper, Document document) { - withOutId = document.get("withOutId", WithOutId.class); + @Override + public WithObjectId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithObjectId entity = new WithObjectId(); + entity.withOutId = document.get("withOutId", WithOutId.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java index a7a2c56c9..2ce03bf24 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java @@ -19,14 +19,14 @@ import lombok.EqualsAndHashCode; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class WithOutGetterSetter implements Mappable { +public class WithOutGetterSetter { private String name; private long number; @@ -35,16 +35,25 @@ public WithOutGetterSetter() { number = 2; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); + public static class Converter implements EntityConverter { - } + @Override + public Class getEntityType() { + return WithOutGetterSetter.class; + } + + @Override + public Document toDocument(WithOutGetterSetter entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithOutGetterSetter fromDocument(Document document, NitriteMapper nitriteMapper) { + WithOutGetterSetter entity = new WithOutGetterSetter(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java index 09b63c2c2..645dd0b45 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,7 +28,7 @@ */ @Getter @Setter -public class WithOutId implements Comparable, Mappable { +public class WithOutId implements Comparable { private String name; private long number; @@ -37,16 +37,26 @@ public int compareTo(WithOutId o) { return Long.compare(number, o.number); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public Class getEntityType() { + return WithOutId.class; + } + + @Override + public Document toDocument(WithOutId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.name) + .put("number", entity.number); + } + + @Override + public WithOutId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithOutId entity = new WithOutId(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java index a425209fb..28febec9e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java @@ -19,14 +19,14 @@ import lombok.EqualsAndHashCode; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class WithPrivateConstructor implements Mappable { +public class WithPrivateConstructor { private String name; private long number; @@ -42,15 +42,24 @@ public static WithPrivateConstructor create(final String name, final long number return obj; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithPrivateConstructor.class; + } + + @Override + public Document toDocument(WithPrivateConstructor entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithPrivateConstructor fromDocument(Document document, NitriteMapper nitriteMapper) { + String name = document.get("name", String.class); + Long number = document.get("number", Long.class); + return WithPrivateConstructor.create(name, number); + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java index 793388231..eae21f1de 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java @@ -18,27 +18,37 @@ package org.dizitart.no2.integration.repository.data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; /** * @author Anindya Chatterjee. */ -public class WithPublicField implements Mappable { +public class WithPublicField { @Id public String name; public long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithPublicField.class; + } + + @Override + public Document toDocument(WithPublicField entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithPublicField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithPublicField entity = new WithPublicField(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java index 9c1ade657..35dde5a91 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,19 +29,29 @@ */ @Getter @Setter -public class WithTransientField implements Mappable { +public class WithTransientField { private transient String name; @Id private long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithTransientField.class; + } + + @Override + public Document toDocument(WithTransientField entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - number = document.get("number", Long.class); + @Override + public WithTransientField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithTransientField entity = new WithTransientField(); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java index 9ecb9a68b..9c721c80c 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -27,39 +27,60 @@ * @author Anindya Chatterjee */ @Data -public class WithoutEmbeddedId implements Mappable { +public class WithoutEmbeddedId { @Id private NestedId nestedId; private String data; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("nestedId", nestedId.write(mapper)) - .put("data", data); - } + @Data + public static class NestedId { + private Long id; + + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - Document nestedId = document.get("nestedId", Document.class); - this.nestedId = mapper.convert(nestedId, NestedId.class); - this.data = document.get("data", String.class); + @Override + public Class getEntityType() { + return NestedId.class; + } + + @Override + public Document toDocument(NestedId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("id", entity.id); + } + + @Override + public NestedId fromDocument(Document document, NitriteMapper nitriteMapper) { + NestedId entity = new NestedId(); + entity.id = document.get("id", Long.class); + return entity; + } + } } + public static class Converter implements EntityConverter { - @Data - public static class NestedId implements Mappable { - private Long id; + @Override + public Class getEntityType() { + return WithoutEmbeddedId.class; + } @Override - public Document write(NitriteMapper mapper) { + public Document toDocument(WithoutEmbeddedId entity, NitriteMapper nitriteMapper) { return Document.createDocument() - .put("id", id); + .put("nestedId", nitriteMapper.convert(entity.nestedId, Document.class)) + .put("data", entity.data); } @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); + public WithoutEmbeddedId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithoutEmbeddedId entity = new WithoutEmbeddedId(); + Document nestedId = document.get("nestedId", Document.class); + + entity.nestedId = nitriteMapper.convert(nestedId, NestedId.class); + entity.data = document.get("data", String.class); + + return entity; } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java new file mode 100644 index 000000000..c6bfe8bb9 --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; + +@Data +public class Manufacturer { + private String name; + private String address; + private Integer uniqueId; +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java new file mode 100644 index 000000000..f31d7b0bb --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ManufacturerConverter implements EntityConverter { + @Override + public Class getEntityType() { + return Manufacturer.class; + } + + @Override + public Document toDocument(Manufacturer entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.getName()) + .put("address", entity.getAddress()) + .put("uniqueId", entity.getUniqueId()); + } + + @Override + public Manufacturer fromDocument(Document document, NitriteMapper nitriteMapper) { + Manufacturer manufacturer = new Manufacturer(); + manufacturer.setName(document.get("name", String.class)); + manufacturer.setAddress(document.get("address", String.class)); + manufacturer.setUniqueId(document.get("uniqueId", Integer.class)); + return manufacturer; + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java new file mode 100644 index 000000000..8d04904f9 --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityDecorator; +import org.dizitart.no2.repository.EntityId; + +import java.util.List; + +public class ManufacturerDecorator implements EntityDecorator { + @Override + public Class getEntityType() { + return Manufacturer.class; + } + + @Override + public EntityId getIdField() { + return null; + } + + @Override + public List getIndexFields() { + return null; + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java new file mode 100644 index 000000000..89b332c8a --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +@Data +public class MiniProduct { + private String uniqueId; + private String manufacturerName; + private Double price; + + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return MiniProduct.class; + } + + @Override + public Document toDocument(MiniProduct entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("productId.uniqueId", entity.getUniqueId()) + .put("manufacturer.name", entity.getManufacturerName()) + .put("price", entity.getPrice()); + } + + @Override + public MiniProduct fromDocument(Document document, NitriteMapper nitriteMapper) { + MiniProduct entity = new MiniProduct(); + entity.setUniqueId(document.get("productId.uniqueId", String.class)); + entity.setManufacturerName(document.get("manufacturer.name", String.class)); + entity.setPrice(document.get("price", Double.class)); + return entity; + } + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java new file mode 100644 index 000000000..ff59219f9 --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; + +@Data +public class Product { + private ProductId productId; + private Manufacturer manufacturer; + private String productName; + private Double price; +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java new file mode 100644 index 000000000..0421c0cac --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ProductConverter implements EntityConverter { + @Override + public Class getEntityType() { + return Product.class; + } + + @Override + public Document toDocument(Product entity, NitriteMapper nitriteMapper) { + Document productId = nitriteMapper.convert(entity.getProductId(), Document.class); + Document manufacturer = nitriteMapper.convert(entity.getManufacturer(), Document.class); + + return Document.createDocument() + .put("productId", productId) + .put("manufacturer", manufacturer) + .put("productName", entity.getProductName()) + .put("price", entity.getPrice()); + } + + @Override + public Product fromDocument(Document document, NitriteMapper nitriteMapper) { + Product entity = new Product(); + ProductId productId = nitriteMapper.convert(document.get("productId", Document.class), ProductId.class); + Manufacturer manufacturer = nitriteMapper.convert(document.get("manufacturer", Document.class), + Manufacturer.class); + entity.setProductId(productId); + entity.setManufacturer(manufacturer); + entity.setProductName(document.get("productName", String.class)); + entity.setPrice(document.get("price", Double.class)); + return entity; + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java new file mode 100644 index 000000000..301dd8909 --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.repository.EntityDecorator; +import org.dizitart.no2.repository.EntityId; + +import java.util.Arrays; +import java.util.List; + +public class ProductDecorator implements EntityDecorator { + @Override + public Class getEntityType() { + return Product.class; + } + + @Override + public EntityId getIdField() { + return new EntityId("productId", "uniqueId", "productCode"); + } + + @Override + public List getIndexFields() { + return Arrays.asList( + IndexFields.create(IndexType.NON_UNIQUE, "manufacturer.name"), + IndexFields.create(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") + ); + } + + @Override + public String getEntityName() { + return "product"; + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java new file mode 100644 index 000000000..dd2f9f04e --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; + +@Data +public class ProductId { + private String uniqueId; + private String productCode; +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java new file mode 100644 index 000000000..2c02fddc0 --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ProductIdConverter implements EntityConverter { + @Override + public Class getEntityType() { + return ProductId.class; + } + + @Override + public Document toDocument(ProductId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("uniqueId", entity.getUniqueId()) + .put("productCode", entity.getProductCode()); + } + + @Override + public ProductId fromDocument(Document document, NitriteMapper nitriteMapper) { + ProductId entity = new ProductId(); + entity.setUniqueId(document.get("uniqueId", String.class)); + entity.setProductCode(document.get("productCode", String.class)); + return entity; + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TxData.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TxData.java index 1e5ed7ca6..104ccd4ad 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TxData.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/transaction/TxData.java @@ -21,7 +21,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -31,20 +31,30 @@ @Data @NoArgsConstructor @AllArgsConstructor -class TxData implements Mappable { +public class TxData { @Id private Long id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TxData.class; + } + + @Override + public Document toDocument(TxData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - name = document.get("name", String.class); + @Override + public TxData fromDocument(Document document, NitriteMapper nitriteMapper) { + TxData entity = new TxData(); + entity.id = document.get("id", Long.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/rocksdb/GithubIssues.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/rocksdb/GithubIssues.java index d5e187d63..d2127df26 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/rocksdb/GithubIssues.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/rocksdb/GithubIssues.java @@ -20,8 +20,9 @@ import lombok.Data; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.repository.ObjectRepository; import org.junit.Test; @@ -34,10 +35,14 @@ public void testIssue412() { RocksDBModule dbModule = RocksDBModule.withConfig() .filePath("/tmp/rocks-demo") .build(); + Nitrite db = Nitrite.builder() .loadModule(dbModule) .openOrCreate(); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TestData.Converter()); + // Step 1 // NitriteCollection collection = db.getCollection("test"); // Document document = Document.createDocument("a", 1).put("b", 2); @@ -54,19 +59,30 @@ public void testIssue412() { } @Data - public static class TestData implements Mappable { + public static class TestData { private Integer id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id).put("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TestData.class; + } + + @Override + public Document toDocument(TestData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Integer.class); - name = document.get("name", String.class); + @Override + public TestData fromDocument(Document document, NitriteMapper nitriteMapper) { + TestData entity = new TestData(); + entity.id = document.get("id", Integer.class); + entity.name = document.get("name", String.class); + return entity; + } } } } diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/GeometryUtils.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/GeometryUtils.java new file mode 100644 index 000000000..0ec577d71 --- /dev/null +++ b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/GeometryUtils.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.spatial; + +import org.dizitart.no2.exceptions.NitriteIOException; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; +import org.locationtech.jts.io.WKTWriter; + +public class GeometryUtils { + private static final String GEOMETRY_ID = "geometry:"; + private static WKTWriter writer; + private static WKTReader reader; + + static { + writer = new WKTWriter(); + reader = new WKTReader(); + } + + private GeometryUtils() { + } + + public static String toString(Geometry geometry) { + return GEOMETRY_ID + writer.write(geometry); + } + + public static Geometry fromString(String geometryValue) { + try { + if (geometryValue.contains(GEOMETRY_ID)) { + String geometry = geometryValue.replace(GEOMETRY_ID, ""); + return reader.read(geometry); + } else { + throw new NitriteIOException("Not a valid WKT geometry string " + geometryValue); + } + } catch (ParseException pe) { + throw new NitriteIOException("Failed to parse WKT geometry string", pe); + } + } +} diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndex.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndex.java index 32ab1be97..303155ae8 100644 --- a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndex.java +++ b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/SpatialIndex.java @@ -154,7 +154,7 @@ private NitriteRTree findIndexMap() { private Geometry parseGeometry(String field, Object fieldValue) { if (fieldValue == null) return null; if (fieldValue instanceof String) { - return nitriteConfig.nitriteMapper().convert(fieldValue, Geometry.class); + return GeometryUtils.fromString((String) fieldValue); } else if (fieldValue instanceof Geometry) { return (Geometry) fieldValue; } diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryDeserializer.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryDeserializer.java index bb58df1fd..e9288f867 100644 --- a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryDeserializer.java +++ b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryDeserializer.java @@ -20,14 +20,11 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.spatial.GeometryUtils; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.io.ParseException; -import org.locationtech.jts.io.WKTReader; import java.io.IOException; -import static org.dizitart.no2.spatial.mapper.GeometryExtension.GEOMETRY_ID; - /** * @author Anindya Chatterjee */ @@ -41,17 +38,6 @@ protected GeometryDeserializer() { @Override public Geometry deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String value = p.getValueAsString(); - WKTReader reader = new WKTReader(); - try { - if (value.contains(GEOMETRY_ID)) { - String geometry = value.replace(GEOMETRY_ID, ""); - return reader.read(geometry); - } else { - throw new ParseException("Not a valid geometry value " + value); - } - } catch (ParseException e) { - log.error("Error while parsing WKT geometry string", e); - throw new IOException(e); - } + return GeometryUtils.fromString(value); } } diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryExtension.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryExtension.java deleted file mode 100644 index 23a7b7c51..000000000 --- a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryExtension.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.spatial.mapper; - -import com.fasterxml.jackson.databind.Module; -import com.fasterxml.jackson.databind.module.SimpleModule; -import org.dizitart.no2.common.mapper.JacksonExtension; -import org.locationtech.jts.geom.Geometry; - -import java.util.List; - -import static org.dizitart.no2.common.util.Iterables.listOf; - -/** - * Class that registers capability of serializing {@code Geometry} objects with the Jackson core. - * - * @author Anindya Chatterjee - * @since 4.0.0 - */ -public class GeometryExtension implements JacksonExtension { - /** - * The constant GEOMETRY_ID - */ - public static final String GEOMETRY_ID = "geometry:"; - - @Override - public List> getSupportedTypes() { - return listOf(Geometry.class); - } - - @Override - public Module getModule() { - return new SimpleModule() { - @Override - public void setupModule(SetupContext context) { - addSerializer(Geometry.class, new GeometrySerializer()); - addDeserializer(Geometry.class, new GeometryDeserializer()); - super.setupModule(context); - } - }; - } -} diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryModule.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryModule.java new file mode 100644 index 000000000..74ffac9be --- /dev/null +++ b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometryModule.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017-2020. Nitrite author or authors. + * + * 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 org.dizitart.no2.spatial.mapper; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.locationtech.jts.geom.Geometry; + +/** + * Class that registers capability of serializing {@link Geometry} objects with the Jackson core. + * + * @author Anindya Chatterjee + * @since 4.0.0 + */ +public class GeometryModule extends SimpleModule { + + @Override + public void setupModule(SetupContext context) { + addSerializer(Geometry.class, new GeometrySerializer()); + addDeserializer(Geometry.class, new GeometryDeserializer()); + super.setupModule(context); + } +} diff --git a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometrySerializer.java b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometrySerializer.java index 62585bdf8..dbb9c7b44 100644 --- a/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometrySerializer.java +++ b/nitrite-spatial/src/main/java/org/dizitart/no2/spatial/mapper/GeometrySerializer.java @@ -19,13 +19,11 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; +import org.dizitart.no2.spatial.GeometryUtils; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.io.WKTWriter; import java.io.IOException; -import static org.dizitart.no2.spatial.mapper.GeometryExtension.GEOMETRY_ID; - /** * @author Anindya Chatterjee */ @@ -38,9 +36,7 @@ protected GeometrySerializer() { @Override public void serialize(Geometry value, JsonGenerator gen, SerializerProvider provider) throws IOException { if (value != null) { - WKTWriter writer = new WKTWriter(); - String wktString = writer.write(value); - gen.writeString(GEOMETRY_ID + wktString); + gen.writeString(GeometryUtils.toString(value)); } } } diff --git a/nitrite-spatial/src/test/java/org/dizitart/no2/spatial/TestUtil.java b/nitrite-spatial/src/test/java/org/dizitart/no2/spatial/TestUtil.java index 99b0526f5..b131e2f47 100644 --- a/nitrite-spatial/src/test/java/org/dizitart/no2/spatial/TestUtil.java +++ b/nitrite-spatial/src/test/java/org/dizitart/no2/spatial/TestUtil.java @@ -21,7 +21,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.common.mapper.JacksonMapperModule; import org.dizitart.no2.mvstore.MVStoreModule; -import org.dizitart.no2.spatial.mapper.GeometryExtension; +import org.dizitart.no2.spatial.mapper.GeometryModule; import java.io.File; import java.nio.file.Files; @@ -50,7 +50,7 @@ public static Nitrite createDb(String fileName) { return Nitrite.builder() .loadModule(module) - .loadModule(new JacksonMapperModule(new GeometryExtension())) + .loadModule(new JacksonMapperModule(new GeometryModule())) .loadModule(new SpatialModule()) .fieldSeparator(".") .openOrCreate(); diff --git a/nitrite-support/build.gradle b/nitrite-support/build.gradle index b2761db7e..f1948e232 100644 --- a/nitrite-support/build.gradle +++ b/nitrite-support/build.gradle @@ -52,6 +52,7 @@ dependencies { testAnnotationProcessor "org.projectlombok:lombok:1.18.24" testImplementation project(path: ':nitrite-mvstore-adapter', configuration: 'default') testImplementation "junit:junit:4.13.2" + testImplementation "com.github.javafaker:javafaker:1.0.2" } test { diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java b/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java index 4ebb7de8e..e8c35e301 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/Exporter.java @@ -113,7 +113,7 @@ public void exportTo(File file) { } File parent = file.getParentFile(); - // if parent dir does not exists, try to create it + // if parent dir does not exist, try to create it if (!parent.exists()) { boolean result = parent.mkdirs(); if (!result) { diff --git a/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonImporter.java b/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonImporter.java index d049fc08c..059b21c5c 100644 --- a/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonImporter.java +++ b/nitrite-support/src/main/java/org/dizitart/no2/support/NitriteJsonImporter.java @@ -181,8 +181,8 @@ private void readIndices(PersistentCollection collection) throws IOException String data = parser.readValueAs(String.class); IndexDescriptor index = (IndexDescriptor) readEncodedObject(data); if (index != null) { - String[] fieldNames = index.getIndexFields().getFieldNames().toArray(new String[0]); - if (collection != null && index.getIndexFields() != null && !collection.hasIndex(fieldNames)) { + String[] fieldNames = index.getFields().getFieldNames().toArray(new String[0]); + if (collection != null && index.getFields() != null && !collection.hasIndex(fieldNames)) { collection.createIndex(indexOptions(index.getIndexType()), fieldNames); } } diff --git a/nitrite-support/src/test/java/org/dizitart/no2/support/BaseExternalTest.java b/nitrite-support/src/test/java/org/dizitart/no2/support/BaseExternalTest.java index 49055ade6..98a84d3a5 100644 --- a/nitrite-support/src/test/java/org/dizitart/no2/support/BaseExternalTest.java +++ b/nitrite-support/src/test/java/org/dizitart/no2/support/BaseExternalTest.java @@ -19,6 +19,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.mvstore.MVStoreModule; import org.dizitart.no2.repository.ObjectRepository; import org.junit.After; @@ -33,6 +34,7 @@ import java.util.UUID; import static org.dizitart.no2.common.Constants.*; +import static org.dizitart.no2.common.util.Iterables.setOf; import static org.junit.Assert.assertTrue; /** @@ -59,7 +61,7 @@ public static String getRandomTempDbFile() { if (!file.exists()) { assertTrue(file.mkdirs()); } - return file.getPath() + File.separator + UUID.randomUUID().toString() + ".db"; + return file.getPath() + File.separator + UUID.randomUUID() + ".db"; } @Before @@ -108,8 +110,14 @@ private Nitrite createDb(String filePath) { .filePath(filePath) .build(); + SimpleDocumentMapper documentMapper = new SimpleDocumentMapper(); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + documentMapper.registerEntityConverter(new Company.CompanyConverter()); + documentMapper.registerEntityConverter(new Note.NoteConverter()); + return Nitrite.builder() .loadModule(storeModule) + .loadModule(() -> setOf(documentMapper)) .fieldSeparator(".") .openOrCreate(); } diff --git a/nitrite-support/src/test/java/org/dizitart/no2/support/Company.java b/nitrite-support/src/test/java/org/dizitart/no2/support/Company.java index b92a9d6fe..2f855757b 100644 --- a/nitrite-support/src/test/java/org/dizitart/no2/support/Company.java +++ b/nitrite-support/src/test/java/org/dizitart/no2/support/Company.java @@ -16,16 +16,13 @@ package org.dizitart.no2.support; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; +import lombok.Data; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; import org.dizitart.no2.repository.annotations.Indices; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; import java.io.Serializable; import java.util.Date; @@ -35,49 +32,57 @@ /** * @author Anindya Chatterjee. */ -@ToString -@EqualsAndHashCode +@Data @Indices({ @Index(value = "companyName") }) -public class Company implements Serializable, Mappable { - @Id - @Getter - @Setter +public class Company implements Serializable { + @Id(fieldName = "company_id") private Long companyId; - @Getter - @Setter private String companyName; - @Getter - @Setter private Date dateCreated; - @Getter - @Setter private List departments; - @Getter - @Setter private Map> employeeRecord; @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("companyId", companyId) - .put("companyName", companyName) - .put("dateCreated", dateCreated) - .put("departments", departments) - .put("employeeRecord", employeeRecord); + public String toString() { + return "Company{" + + "companyId=" + companyId + + ", companyName='" + companyName + '\'' + + ", dateCreated=" + dateCreated + + ", departments=" + departments + + '}'; } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - companyId = document.get("companyId", Long.class); - companyName = document.get("companyName", String.class); - dateCreated = document.get("dateCreated", Date.class); - departments = document.get("departments", List.class); - employeeRecord = document.get("employeeRecord", Map.class); + public static class CompanyConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Company.class; + } + + @Override + public Document toDocument(Company entity, NitriteMapper nitriteMapper) { + return Document.createDocument("company_id", entity.companyId) + .put("companyName", entity.companyName) + .put("dateCreated", entity.dateCreated) + .put("departments", entity.departments) + .put("employeeRecord", entity.employeeRecord); + } + + @Override + public Company fromDocument(Document document, NitriteMapper nitriteMapper) { + Company entity = new Company(); + entity.companyId = document.get("company_id", Long.class); + entity.companyName = document.get("companyName", String.class); + entity.dateCreated = document.get("dateCreated", Date.class); + entity.departments = document.get("departments", List.class); + entity.employeeRecord = document.get("employeeRecord", Map.class); + return entity; + } } } diff --git a/nitrite-support/src/test/java/org/dizitart/no2/support/DataGenerator.java b/nitrite-support/src/test/java/org/dizitart/no2/support/DataGenerator.java index 53e52d1db..acea54ef5 100644 --- a/nitrite-support/src/test/java/org/dizitart/no2/support/DataGenerator.java +++ b/nitrite-support/src/test/java/org/dizitart/no2/support/DataGenerator.java @@ -16,13 +16,11 @@ package org.dizitart.no2.support; +import com.github.javafaker.Faker; import lombok.experimental.UtilityClass; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** @@ -32,12 +30,13 @@ public class DataGenerator { private static Random random = new Random(System.currentTimeMillis()); private static AtomicInteger counter = new AtomicInteger(random.nextInt()); + private static Faker faker = new Faker(); public static Company generateCompanyRecord() { Company company = new Company(); company.setCompanyId(System.nanoTime() + counter.incrementAndGet()); - company.setCompanyName(randomCompanyName()); - company.setDateCreated(randomDate()); + company.setCompanyName(faker.company().name()); + company.setDateCreated(faker.date().past(20 * 365, TimeUnit.DAYS)); List departments = departments(); company.setDepartments(departments); @@ -63,8 +62,8 @@ private static List generateEmployeeRecords(Company company, int count public static Employee generateEmployee() { Employee employee = new Employee(); employee.setEmpId(System.nanoTime() + counter.incrementAndGet()); - employee.setJoinDate(randomDate()); - employee.setAddress(UUID.randomUUID().toString().replace('-', ' ')); + employee.setJoinDate(faker.date().birthday()); + employee.setAddress(faker.address().fullAddress()); byte[] blob = new byte[random.nextInt(8000)]; random.nextBytes(blob); @@ -74,65 +73,18 @@ public static Employee generateEmployee() { return employee; } - private static Date randomDate() { - return new Date(-946771200000L + - (Math.abs(random.nextLong()) % (70L * 365 * 24 * 60 * 60 * 1000))); - } - public static Note randomNote() { - InputStream inputStream = ClassLoader.getSystemResourceAsStream("test.text"); - - try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { - String strLine; - long line = random.nextInt(49); - int count = 0; - while ((strLine = br.readLine()) != null) { - if (count == line) { - Note note = new Note(); - note.setNoteId(line); - note.setText(strLine); - return note; - } - count++; - } - } catch (IOException e) { - // ignore - } - // ignore - return null; - } - - private static String randomCompanyName() { - InputStream inputStream = ClassLoader.getSystemResourceAsStream("english.stop"); - - assert inputStream != null; - try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { - String strLine; - int line = random.nextInt(570); - int count = 0; - while ((strLine = br.readLine()) != null) { - if (count == line) { - return strLine + System.nanoTime() + " inc."; - } - count++; - } - } catch (IOException e) { - // ignore - } - // ignore - return null; + Note note = new Note(); + note.setNoteId((long) counter.incrementAndGet()); + note.setText(faker.lorem().paragraph()); + return note; } private static List departments() { - return new ArrayList() {{ - add("dev"); - add("hr"); - add("qa"); - add("dev-ops"); - add("sales"); - add("marketing"); - add("design"); - add("support"); - }}; + List departments = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + departments.add(faker.job().title()); + } + return departments; } } diff --git a/nitrite-support/src/test/java/org/dizitart/no2/support/Employee.java b/nitrite-support/src/test/java/org/dizitart/no2/support/Employee.java index 6e97fa636..9ad3af137 100644 --- a/nitrite-support/src/test/java/org/dizitart/no2/support/Employee.java +++ b/nitrite-support/src/test/java/org/dizitart/no2/support/Employee.java @@ -21,11 +21,11 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; import org.dizitart.no2.repository.annotations.Indices; -import org.dizitart.no2.common.mapper.Mappable; import org.dizitart.no2.common.mapper.NitriteMapper; import java.io.Serializable; @@ -41,7 +41,7 @@ @Index(value = "address", type = IndexType.FULL_TEXT), @Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) }) -public class Employee implements Serializable, Mappable { +public class Employee implements Serializable { @Id @Getter @Setter @@ -79,26 +79,37 @@ public Employee(Employee copy) { employeeNote = copy.employeeNote; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("empId", empId) - .put("joinDate", joinDate) - .put("address", address) - .put("blob", blob) - .put("employeeNote", employeeNote != null ? employeeNote.write(mapper) : null); - } + public static class EmployeeConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Employee.class; + } + + @Override + public Document toDocument(Employee entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address) + .put("blob", entity.blob) + .put("employeeNote", nitriteMapper.convert(entity.employeeNote, Document.class)); + } + + @Override + public Employee fromDocument(Document document, NitriteMapper nitriteMapper) { + Employee entity = new Employee(); - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); - blob = document.get("blob", byte[].class); + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + entity.blob = document.get("blob", byte[].class); - if (document.get("employeeNote") != null) { - employeeNote = new Note(); - employeeNote.read(mapper, document.get("employeeNote", Document.class)); + if (document.get("employeeNote") != null) { + Document doc = document.get("employeeNote", Document.class); + entity.employeeNote = nitriteMapper.convert(doc, Note.class);; + } + return entity; } } } diff --git a/nitrite-support/src/test/java/org/dizitart/no2/support/Note.java b/nitrite-support/src/test/java/org/dizitart/no2/support/Note.java index 4f78a970b..ee3435060 100644 --- a/nitrite-support/src/test/java/org/dizitart/no2/support/Note.java +++ b/nitrite-support/src/test/java/org/dizitart/no2/support/Note.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.io.Serializable; @@ -29,7 +29,7 @@ * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class Note implements Serializable, Mappable { +public class Note implements Serializable { @Getter @Setter private Long noteId; @@ -37,14 +37,26 @@ public class Note implements Serializable, Mappable { @Setter private String text; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("noteId", noteId).put("text", text); - } + public static class NoteConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Note.class; + } + + @Override + public Document toDocument(Note entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("noteId", entity.noteId) + .put("text", entity.text); + } - @Override - public void read(NitriteMapper mapper, Document document) { - noteId = document.get("noteId", Long.class); - text = document.get("text", String.class); + @Override + public Note fromDocument(Document document, NitriteMapper nitriteMapper) { + Note entity = new Note(); + entity.noteId = document.get("noteId", Long.class); + entity.text = document.get("text", String.class); + return entity; + } } } diff --git a/nitrite-support/src/test/resources/english.stop b/nitrite-support/src/test/resources/english.stop deleted file mode 100644 index 70572ebaa..000000000 --- a/nitrite-support/src/test/resources/english.stop +++ /dev/null @@ -1,571 +0,0 @@ -a -a's -able -about -above -according -accordingly -across -actually -after -afterwards -again -against -ain't -all -allow -allows -almost -alone -along -already -also -although -always -am -among -amongst -an -and -another -any -anybody -anyhow -anyone -anything -anyway -anyways -anywhere -apart -appear -appreciate -appropriate -are -aren't -around -as -aside -ask -asking -associated -at -available -away -awfully -b -be -became -because -become -becomes -becoming -been -before -beforehand -behind -being -believe -below -beside -besides -best -better -between -beyond -both -brief -but -by -c -c'mon -c's -came -can -can't -cannot -cant -cause -causes -certain -certainly -changes -clearly -co -com -come -comes -concerning -consequently -consider -considering -contain -containing -contains -corresponding -could -couldn't -course -currently -d -definitely -described -despite -did -didn't -different -do -does -doesn't -doing -don't -done -down -downwards -during -e -each -edu -eg -eight -either -else -elsewhere -enough -entirely -especially -et -etc -even -ever -every -everybody -everyone -everything -everywhere -ex -exactly -example -except -f -far -few -fifth -first -five -followed -following -follows -for -former -formerly -forth -four -from -further -furthermore -g -get -gets -getting -given -gives -go -goes -going -gone -got -gotten -greetings -h -had -hadn't -happens -hardly -has -hasn't -have -haven't -having -he -he's -hello -help -hence -her -here -here's -hereafter -hereby -herein -hereupon -hers -herself -hi -him -himself -his -hither -hopefully -how -howbeit -however -i -i'd -i'll -i'm -i've -ie -if -ignored -immediate -in -inasmuch -inc -indeed -indicate -indicated -indicates -inner -insofar -instead -into -inward -is -isn't -it -it'd -it'll -it's -its -itself -j -just -k -keep -keeps -kept -know -knows -known -l -last -lately -later -latter -latterly -least -less -lest -let -let's -like -liked -likely -little -look -looking -looks -ltd -m -mainly -many -may -maybe -me -mean -meanwhile -merely -might -more -moreover -most -mostly -much -must -my -myself -n -name -namely -nd -near -nearly -necessary -need -needs -neither -never -nevertheless -new -next -nine -no -nobody -non -none -noone -nor -normally -not -nothing -novel -now -nowhere -o -obviously -of -off -often -oh -ok -okay -old -on -once -one -ones -only -onto -or -other -others -otherwise -ought -our -ours -ourselves -out -outside -over -overall -own -p -particular -particularly -per -perhaps -placed -please -plus -possible -presumably -probably -provides -q -que -quite -qv -r -rather -rd -re -really -reasonably -regarding -regardless -regards -relatively -respectively -right -s -said -same -saw -say -saying -says -second -secondly -see -seeing -seem -seemed -seeming -seems -seen -self -selves -sensible -sent -serious -seriously -seven -several -shall -she -should -shouldn't -since -six -so -some -somebody -somehow -someone -something -sometime -sometimes -somewhat -somewhere -soon -sorry -specified -specify -specifying -still -sub -such -sup -sure -t -t's -take -taken -tell -tends -th -than -thank -thanks -thanx -that -that's -thats -the -their -theirs -them -themselves -then -thence -there -there's -thereafter -thereby -therefore -therein -theres -thereupon -these -they -they'd -they'll -they're -they've -think -third -this -thorough -thoroughly -those -though -three -through -throughout -thru -thus -to -together -too -took -toward -towards -tried -tries -truly -try -trying -twice -two -u -un -under -unfortunately -unless -unlikely -until -unto -up -upon -us -use -used -useful -uses -using -usually -uucp -v -value -various -very -via -viz -vs -w -want -wants -was -wasn't -way -we -we'd -we'll -we're -we've -welcome -well -went -were -weren't -what -what's -whatever -when -whence -whenever -where -where's -whereafter -whereas -whereby -wherein -whereupon -wherever -whether -which -while -whither -who -who's -whoever -whole -whom -whose -why -will -willing -wish -with -within -without -won't -wonder -would -would -wouldn't -x -y -yes -yet -you -you'd -you'll -you're -you've -your -yours -yourself -yourselves -z -zero \ No newline at end of file diff --git a/nitrite-support/src/test/resources/test.text b/nitrite-support/src/test/resources/test.text deleted file mode 100644 index c940fb50c..000000000 --- a/nitrite-support/src/test/resources/test.text +++ /dev/null @@ -1,50 +0,0 @@ -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean massa nulla, elementum vitae velit nec, suscipit porttitor urna. Integer volutpat nibh et nisi congue ullamcorper. Cras laoreet consectetur massa a luctus. Etiam non sapien vitae enim semper elementum. Donec ullamcorper nibh quis magna vestibulum, eget faucibus sem posuere. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut sed nulla id risus ultricies facilisis. Curabitur scelerisque eros metus, in ultrices libero efficitur sit amet. Vivamus aliquet nec sapien sed vehicula. Praesent a mauris eget tortor consectetur euismod. Suspendisse tempus felis et ante dictum, nec eleifend tellus finibus. -Vivamus felis turpis, pretium id gravida ut, pharetra et massa. Duis lobortis velit in nibh tristique, sit amet tristique neque eleifend. Quisque et faucibus magna. Donec in enim odio. Vestibulum fringilla odio quis libero convallis, sed imperdiet ante scelerisque. Suspendisse rutrum, turpis eget mollis ultricies, purus ante pellentesque arcu, ut gravida libero elit eget odio. Nam quis congue purus. Sed commodo enim lacus, eu tristique nisi pretium id. Nulla diam odio, cursus a eleifend eu, tristique tempus dui. -Suspendisse dictum mollis justo. Aliquam tempus consectetur elit, in euismod erat tempor non. Nullam lacinia eget sapien quis luctus. Praesent odio nisl, dapibus sit amet pharetra ut, consequat et neque. Ut consequat, ligula at lacinia ultrices, erat nisl vehicula urna, at ullamcorper magna urna ut erat. Integer at orci fringilla, congue nibh et, lacinia lorem. Sed aliquet quis felis nec fringilla. Integer egestas vitae neque in consequat. In laoreet nisi risus, a pharetra mi placerat a. Etiam ultricies arcu non est volutpat fringilla. Ut et molestie augue, ac consequat urna. Nulla et egestas mi. Sed tempor tortor et velit lacinia volutpat. Etiam volutpat porta interdum. Aliquam vitae rutrum sem. -Phasellus venenatis nibh mauris, id lobortis lorem dictum quis. Cras at sagittis felis. Proin viverra neque et metus rhoncus suscipit. Nam aliquet aliquet est, eget convallis turpis hendrerit sit amet. Nam eget commodo justo. Morbi pharetra commodo sapien vitae luctus. Integer eu eleifend massa. Integer consectetur sapien nec ligula pellentesque, sit amet tempus lacus venenatis. Aenean efficitur risus sed ligula dapibus pellentesque. Sed consectetur est eget egestas ornare. -Integer sit amet iaculis orci. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec non dignissim nibh, sit amet ornare ipsum. Proin quis felis ut orci condimentum mattis a non urna. Aenean a metus nec lacus fringilla scelerisque sit amet id nisi. Nulla et velit in libero laoreet mollis. Nam malesuada leo non enim hendrerit facilisis a quis enim. -Nullam in diam ut ex mattis aliquet sed et ipsum. Morbi ut nunc et ex mollis convallis. Donec dui velit, iaculis at sapien ullamcorper, dictum maximus nibh. Maecenas porttitor arcu quis ex molestie elementum. Donec scelerisque ut erat at finibus. Nunc nec tempor mauris, sed posuere magna. In hac habitasse platea dictumst. Phasellus elit nisl, pellentesque vestibulum arcu quis, accumsan cursus nisi. Nunc eget orci non felis pretium vestibulum nec in mi. Nulla tincidunt, tortor id bibendum scelerisque, nibh libero lacinia felis, at pretium arcu lectus eu justo. Aenean ac elit id leo finibus vehicula. -Donec sed orci lobortis, posuere purus nec, molestie odio. Mauris elementum libero vel nibh ultrices vehicula. Quisque dignissim ipsum libero, eu elementum quam dignissim a. Mauris magna felis, tincidunt eu dolor vel, sollicitudin porta mi. Integer tempor volutpat leo. Nunc a neque lectus. Nullam pretium tempus congue. -Sed vehicula ipsum ipsum, et maximus augue gravida ut. Nunc augue lorem, volutpat ac volutpat nec, commodo ut ex. Duis dignissim est enim, non dapibus urna pharetra a. Aliquam vitae viverra nibh, ut ullamcorper nulla. Pellentesque ultricies aliquet magna, eget fermentum turpis. Etiam ut urna elementum, laoreet est ut, commodo quam. In quis libero vitae lacus scelerisque dignissim. Phasellus libero ligula, porttitor ac enim sed, ultrices accumsan erat. Quisque cursus tortor sit amet tellus laoreet, eu finibus velit laoreet. Duis luctus massa non nulla luctus, at imperdiet lacus condimentum. Ut nulla metus, elementum sed porta id, fringilla non enim. Fusce id rutrum ipsum. Etiam rhoncus finibus faucibus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. -Etiam placerat aliquet nunc, id facilisis metus auctor at. Ut mi lacus, pharetra in egestas in, accumsan nec urna. Donec placerat erat quis enim vehicula, eget sollicitudin elit auctor. Curabitur ac auctor mauris. Nulla facilisi. Nam suscipit commodo mi, ut consequat neque finibus et. Fusce vestibulum porttitor odio, at tincidunt urna consectetur eu. Nullam lobortis risus sed convallis commodo. Cras non nibh vehicula, venenatis eros non, ultricies neque. Pellentesque enim nisl, elementum et libero id, laoreet dapibus odio. Aenean quis erat suscipit, mattis lectus quis, consectetur nibh. Vivamus vel sem egestas, ultricies mauris at, bibendum velit. Phasellus cursus elit erat, ut molestie risus condimentum vitae. Nam sapien mauris, ullamcorper sit amet dui id, accumsan lobortis dolor. Curabitur ultrices metus felis, ac sollicitudin eros vulputate at. Nunc eget tristique eros. -Vestibulum in lobortis nulla. Morbi mattis turpis ut tellus rutrum, at consectetur lacus mollis. Etiam maximus lectus ante, scelerisque aliquet turpis pharetra et. Fusce vulputate efficitur enim, at varius augue sagittis nec. Aenean volutpat volutpat metus, ut faucibus ex posuere dictum. Vivamus est enim, volutpat aliquam mi vel, dictum lobortis leo. Cras luctus fermentum tellus, sodales pellentesque lectus rutrum id. Sed in condimentum leo. Etiam ultricies porttitor ligula, et sodales purus imperdiet ac. Praesent non semper mi. Nullam bibendum auctor mi sit amet malesuada. Maecenas luctus accumsan ante feugiat egestas. Mauris tellus felis, venenatis sit amet risus nec, auctor auctor libero. Suspendisse purus risus, suscipit at lectus ut, maximus convallis nunc. Nulla facilisi. -Quisque commodo diam ut dui tristique luctus. Nam dignissim semper sollicitudin. Aenean condimentum orci ut augue suscipit, vitae ullamcorper urna ultrices. Nunc ac arcu eu massa placerat maximus. Cras ultrices elit id ipsum finibus, sit amet lacinia dui dignissim. Nulla suscipit suscipit libero non posuere. Aenean lobortis cursus metus, eu dignissim ligula pellentesque a. Donec viverra varius luctus. Mauris quis neque erat. Quisque pellentesque volutpat felis ultricies feugiat. -Fusce eu ante bibendum, tincidunt mauris bibendum, vulputate diam. Integer sit amet massa maximus, finibus ipsum in, scelerisque nunc. Quisque sodales neque quis tristique porttitor. Suspendisse molestie sed arcu nec commodo. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec gravida nunc at fringilla accumsan. Praesent id elit sem. Sed sagittis sed nunc sit amet volutpat. Suspendisse potenti. Etiam malesuada neque enim, a consectetur justo ultricies ac. -Aenean vel enim a diam aliquet tempor. Phasellus et blandit dui. Nulla facilisi. Vestibulum rhoncus dolor at risus vehicula, ut tincidunt arcu sodales. Duis at massa ut leo suscipit mollis hendrerit nec justo. Nullam ex erat, accumsan eu odio sit amet, ullamcorper hendrerit ligula. Nullam scelerisque ut turpis vitae imperdiet. Pellentesque fermentum urna id ex mollis, vel mattis leo interdum. -Aenean nec est ac est varius elementum. Vestibulum vulputate accumsan pellentesque. Sed pellentesque mi nec ipsum tincidunt, fringilla faucibus urna semper. Pellentesque quis consequat nisl. Sed viverra nulla sagittis ante blandit, nec vulputate purus dapibus. Nullam molestie nisi diam, vitae elementum massa pellentesque eu. Vestibulum lacinia tortor in nisi auctor, ut pharetra dui interdum. Mauris ac ante elit. -Praesent pharetra egestas sapien ut convallis. Pellentesque gravida maximus maximus. Aliquam tristique venenatis arcu, ut bibendum augue lacinia vel. Nulla vulputate sit amet nibh non vestibulum. Pellentesque et imperdiet ex, vel cursus dui. Integer facilisis, est et tincidunt cursus, ligula arcu faucibus tortor, id commodo orci orci in neque. Nam lacinia lectus tempus, finibus libero pretium, tempor nulla. Lorem ipsum dolor sit amet, consectetur adipiscing elit. -Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed massa ipsum, rhoncus pellentesque dui vestibulum, accumsan lacinia risus. Suspendisse ullamcorper enim vel sem blandit aliquet. Pellentesque bibendum erat eget quam pretium, sit amet cursus velit volutpat. Sed eget nunc at odio tristique venenatis. Mauris id sagittis quam, non aliquam magna. Curabitur sit amet neque a odio blandit consequat eu at est. Fusce in bibendum ipsum, quis fringilla justo. Nunc ut arcu non dui pretium vestibulum nec eu ipsum. Morbi ac dictum nulla. Vivamus vitae velit dolor. Duis quis aliquet felis. Praesent eu nulla vitae ex mattis imperdiet. -Aliquam finibus mauris arcu, non pellentesque diam interdum id. Praesent ornare lectus augue, eget lacinia odio venenatis a. Proin vulputate, nisl at feugiat euismod, massa arcu feugiat massa, a vehicula lorem nisi at diam. Aenean mattis, tellus sed hendrerit efficitur, purus purus consectetur nulla, a suscipit ipsum tortor vel magna. Nam sed posuere sem. Aliquam tincidunt in tortor ac cursus. Ut pellentesque urna in purus scelerisque, et imperdiet orci mollis. Fusce ultrices massa nisl, nec facilisis purus scelerisque ut. Pellentesque malesuada scelerisque diam, et dignissim orci ultricies vitae. Proin molestie ac augue at aliquet. Pellentesque fermentum dapibus purus ac porta. Nunc eu augue dui. Proin lacinia, enim at pretium vulputate, arcu arcu egestas ipsum, vel vehicula metus neque sed ex. Suspendisse vitae odio eu sapien pellentesque fermentum in nec leo. -Maecenas accumsan urna eros, id consequat felis maximus ac. Quisque id dictum sapien, et maximus justo. Sed blandit sit amet arcu ac vulputate. Sed bibendum maximus volutpat. Mauris dignissim purus vel leo ultrices aliquet. Aenean vitae sem rhoncus, vehicula sem quis, cursus metus. Duis hendrerit metus a fringilla sollicitudin. Morbi pellentesque velit eget posuere rhoncus. -Maecenas ullamcorper congue egestas. Aliquam maximus ullamcorper velit, vel ultrices ex pulvinar a. Sed convallis, metus eu facilisis lacinia, ex ante sollicitudin ipsum, vitae malesuada nibh nulla quis nulla. Aenean molestie, nulla commodo mattis viverra, purus mi ultrices ligula, ac accumsan libero velit sed est. Praesent cursus tincidunt ipsum nec sodales. Sed aliquet risus et odio lacinia, ac molestie libero posuere. Donec ultrices quam sapien, vel maximus eros malesuada eu. Integer tempor scelerisque laoreet. Vestibulum placerat rhoncus consequat. Nullam hendrerit, eros faucibus dictum faucibus, urna odio lobortis orci, ac suscipit ipsum lorem condimentum odio. Sed sollicitudin at odio sed viverra. Nulla aliquet facilisis ultrices. -Etiam vitae tempor tellus. Suspendisse potenti. Mauris sit amet odio diam. Vivamus at dignissim dui. Cras at nisl ut lorem condimentum vehicula. Cras vel nunc lectus. Morbi consequat dictum aliquet. Sed fermentum elementum ipsum, tempor fringilla libero varius et. -Mauris sed dictum purus, et ullamcorper tortor. Duis quis gravida urna. Integer pellentesque, ipsum at laoreet commodo, arcu dui semper lectus, sit amet consectetur diam nibh ut justo. Nam condimentum tincidunt arcu nec congue. Sed vulputate ligula at quam ultricies, et pretium justo consequat. Aenean nunc nisi, vehicula sed velit ut, pharetra egestas orci. Mauris pharetra erat tincidunt, gravida ipsum vitae, fermentum purus. Pellentesque sodales libero non purus ornare fermentum. Aliquam laoreet dignissim sem, et placerat turpis tincidunt non. -Cras bibendum ornare consequat. Donec non enim blandit, varius lorem sit amet, aliquam nibh. Morbi orci libero, egestas vitae arcu a, mollis interdum ante. Nullam vulputate risus enim, sit amet bibendum risus semper ut. Donec at orci sit amet ante hendrerit iaculis a non massa. Vestibulum nisi velit, dictum quis ullamcorper in, rhoncus sed ipsum. Curabitur sollicitudin diam dictum volutpat suscipit. Curabitur a elit et orci pellentesque venenatis ut rhoncus tellus. Proin nec metus non leo pharetra aliquet. Sed dolor augue, vehicula in odio vitae, cursus tincidunt eros. Praesent tempus luctus metus ac maximus. Phasellus non nibh quis eros rhoncus eleifend quis vitae nisi. -In ante enim, convallis in mollis nec, rutrum a nulla. Sed a urna ac nibh pharetra volutpat sit amet vel tortor. Quisque sodales tincidunt felis, et sollicitudin ligula venenatis vel. Pellentesque non justo eu diam sodales venenatis sed nec lacus. Nulla dignissim, sapien non pharetra scelerisque, risus massa dictum purus, congue convallis ante diam nec erat. Maecenas malesuada efficitur neque, a condimentum erat finibus vitae. Mauris quis dolor cursus nisi eleifend convallis sed id diam. Sed non pellentesque nisi. Praesent ut velit et tellus eleifend lacinia. Sed suscipit rutrum viverra. Pellentesque eget malesuada diam. Ut sed leo egestas, gravida odio aliquam, sodales nulla. In in lobortis nunc. Mauris et ex volutpat, elementum risus a, maximus magna. -Donec at odio sit amet nulla maximus semper at in elit. Vestibulum fringilla a tortor ut tristique. Proin a tristique mi. Cras libero ipsum, blandit ut volutpat non, convallis ac erat. Vivamus condimentum nunc dignissim, vestibulum tellus ut, tempus ante. Suspendisse fringilla nisi id arcu blandit pellentesque. Etiam vel elit velit. Donec ac sem consectetur, pharetra nisi a, facilisis turpis. Integer nec aliquam risus. Nullam posuere ligula eu purus rhoncus, nec fringilla nulla blandit. -Fusce condimentum sapien in elit dictum, ullamcorper vulputate ligula lacinia. Donec placerat elit dignissim mauris pellentesque malesuada. Maecenas semper lectus quis orci varius porta. Cras nec magna vitae ligula gravida bibendum. Pellentesque gravida purus sapien, eu egestas orci cursus sodales. Fusce varius ultrices enim quis faucibus. Aenean vel lobortis ex. Integer scelerisque nulla vel ligula pretium eleifend. Morbi lacus ligula, semper nec aliquet a, pulvinar nec ligula. Duis est orci, varius at ultricies ut, molestie id arcu. Nulla molestie lectus pharetra tortor cursus, eget gravida justo tempor. Fusce non ornare ante. In eu tortor venenatis, aliquet augue a, euismod purus. Duis et nibh venenatis, gravida nisi ut, sodales mi. Ut at ligula pretium, laoreet velit rutrum, sodales neque. Nulla quam nulla, mattis quis erat in, bibendum laoreet sem. -Donec posuere diam et est fringilla, sit amet malesuada lectus auctor. Nulla diam ante, tincidunt eu elementum in, congue vitae ante. Curabitur vulputate, nunc at posuere consectetur, nunc sem sagittis felis, non iaculis elit arcu eu metus. Pellentesque a eros dui. Phasellus commodo risus ante, at placerat augue ultricies a. Mauris in elementum velit. Curabitur quis odio vel tellus lacinia porttitor. Mauris nec mollis est, eget efficitur tellus. -In porttitor vel lectus vel finibus. Nunc quis quam ac nunc fringilla tempor. Pellentesque vel malesuada nunc. Suspendisse interdum pretium turpis, eget pretium tellus facilisis at. Etiam id ipsum eu velit elementum vehicula. Mauris vulputate lacus ac elit semper, a sodales metus varius. In semper ligula non erat vulputate, nec sollicitudin lectus gravida. Suspendisse porttitor sapien quis consectetur euismod. Maecenas consectetur scelerisque augue, eu iaculis dolor vulputate eget. In lobortis tristique mauris eu elementum. Proin lacus libero, suscipit id eleifend tincidunt, tincidunt vel felis. Aenean vulputate leo nec odio vehicula, venenatis tempus purus rutrum. -Curabitur non mauris vitae velit tempor dictum. Morbi id congue nunc. Aliquam imperdiet nec eros vitae pulvinar. Donec ut lobortis mauris. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vivamus pellentesque ante sem, vitae feugiat ante lobortis ut. Aliquam felis est, accumsan eget lacus at, gravida gravida ipsum. Etiam congue est justo, sed elementum erat aliquet quis. Aenean porttitor massa pellentesque, efficitur lacus non, scelerisque justo. Integer commodo turpis nibh, eget tincidunt mauris consequat id. Sed convallis egestas rhoncus. In faucibus mi sit amet dolor tempor, non ullamcorper erat mollis. -Quisque tincidunt feugiat volutpat. Nunc consectetur sapien finibus ipsum tincidunt, sit amet facilisis justo suscipit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque scelerisque massa eros, eget tempor lorem rutrum sit amet. Mauris faucibus libero at est sodales elementum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum dapibus ipsum ac quam placerat, eget lacinia lorem lobortis. Pellentesque eget velit nunc. Integer elementum justo in erat lobortis scelerisque. Sed ultrices dapibus quam quis ullamcorper. Duis auctor commodo placerat. Vivamus sed gravida turpis. Cras et nisl dignissim, pellentesque quam sed, eleifend nibh. Pellentesque et metus vel ipsum interdum bibendum. -Nam porttitor tristique metus eget elementum. Nullam eget risus commodo, malesuada dolor sit amet, tempus leo. Etiam eu iaculis neque, in tristique enim. Sed fringilla convallis leo id eleifend. Donec id purus suscipit, iaculis elit a, luctus felis. Praesent laoreet vulputate mi, eget cursus odio venenatis vitae. Curabitur egestas sem enim, non luctus erat tincidunt consectetur. Sed non viverra tellus. -Donec sodales ex eu neque condimentum feugiat. Integer elementum efficitur ligula quis aliquam. Quisque gravida tincidunt mauris. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque ut vehicula tellus. Maecenas ultricies est ac quam aliquam, eget tincidunt eros bibendum. Curabitur a viverra risus, ut dignissim lacus. Praesent pharetra vulputate porttitor. Suspendisse cursus egestas nisl, in mattis risus imperdiet non. Ut tempus tortor malesuada dapibus tristique. Sed blandit ligula non lacus tincidunt pellentesque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Fusce semper mattis ante, in efficitur ligula egestas sit amet. -Nullam eu dapibus nisi, nec fermentum enim. Duis orci odio, lobortis nec ullamcorper a, faucibus fringilla est. Aenean convallis, lorem non euismod venenatis, sapien ipsum pulvinar ipsum, non auctor arcu massa in nisi. Maecenas tincidunt sapien ante, in consequat dui accumsan eget. Phasellus id tellus ac libero iaculis fermentum. Vestibulum sagittis sapien quis porttitor facilisis. Cras euismod sollicitudin nisi, et pretium sapien hendrerit a. Ut auctor vitae neque id pellentesque. Donec at dui blandit, ultricies orci et, aliquet mauris. Praesent porttitor massa vel diam ultrices, ac convallis tellus tincidunt. Duis molestie condimentum nisl non venenatis. Pellentesque scelerisque odio ut lobortis porttitor. Donec congue interdum dolor, non sagittis tellus mattis vel. Fusce commodo augue fringilla semper gravida. -Sed placerat erat nulla, at commodo enim dapibus id. Integer augue dui, aliquet a eros at, euismod luctus erat. Donec vitae tempus enim. In luctus posuere sollicitudin. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus ultrices a felis tincidunt condimentum. Integer nulla augue, pellentesque eu facilisis id, congue dignissim ligula. Phasellus vestibulum blandit vulputate. Morbi a tempus eros. Maecenas ac metus vehicula massa egestas rhoncus. Nam accumsan porttitor rutrum. Morbi sollicitudin eros vestibulum tortor finibus, nec porta orci eleifend. Curabitur id suscipit quam. -Donec vehicula metus massa, accumsan vulputate dolor bibendum vel. Curabitur tincidunt magna sit amet massa commodo, quis porttitor libero sollicitudin. Aenean sollicitudin vehicula dignissim. Duis rhoncus varius leo, vitae eleifend eros tristique quis. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque fermentum nulla purus, nec aliquam nunc ultrices ut. Praesent vestibulum risus eu dolor ultricies eleifend. Nulla vitae hendrerit lorem, a pellentesque eros. Praesent pellentesque sit amet arcu ac hendrerit. -Ut scelerisque, velit eu rhoncus eleifend, dolor dui ultricies dolor, id fermentum lectus nunc nec sem. Donec eget nisi eget augue consequat pretium. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sollicitudin eget magna sed varius. Nam ultricies, eros non dapibus varius, eros elit finibus metus, quis semper lacus lectus in turpis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent ornare, sapien vel gravida semper, risus risus luctus ligula, in vestibulum nibh arcu ac tortor. In cursus tempus justo eget mollis. Phasellus eget mauris hendrerit, venenatis dolor quis, euismod neque. Suspendisse potenti. -Suspendisse lorem lectus, accumsan eget ipsum ac, commodo luctus velit. Vivamus non ligula enim. In hendrerit turpis ut mauris commodo sagittis. Sed dapibus dolor vel turpis malesuada, id tincidunt ipsum convallis. Nulla in convallis enim, at dignissim mi. Etiam at mauris eget eros viverra malesuada. Etiam malesuada ligula eu libero condimentum pretium. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed eget quam non purus malesuada tristique. -Aenean at arcu felis. Donec consectetur ante a tellus pellentesque sollicitudin. Duis ut arcu sapien. Aenean quis lacus a turpis porttitor egestas ut nec nisi. Aenean viverra nec nunc quis lacinia. Nulla dapibus eros ac placerat blandit. Morbi tincidunt porttitor sapien, eu aliquet ipsum ullamcorper at. Phasellus eleifend lacus et mauris mollis gravida. Nullam vitae luctus mauris. Integer malesuada nec ligula non gravida. Nullam at facilisis neque. Vestibulum ac est a libero sagittis tempor a et urna. Suspendisse ac sollicitudin leo, in aliquet augue. Curabitur non vulputate mi. Ut eget euismod ex, ut placerat leo. Praesent suscipit lectus magna, ac bibendum risus dapibus id. -Cras consequat vel nunc id porttitor. Suspendisse tristique nunc leo, quis fermentum diam ultrices ac. Donec scelerisque mollis porttitor. Maecenas interdum dui orci, ut volutpat purus iaculis ac. Proin sed odio est. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vestibulum malesuada turpis nunc, nec tincidunt risus ultricies eu. In pellentesque a leo quis aliquam. Suspendisse potenti. Cras volutpat nibh arcu, sit amet molestie purus rhoncus et. -Phasellus mi neque, placerat sed justo laoreet, aliquet sodales sem. Mauris vulputate aliquam sem, ac consectetur dolor iaculis eget. Nunc lobortis sapien lectus, ac consectetur nisi porta ac. In in sem nec massa consequat viverra nec aliquam urna. Proin maximus posuere tellus quis imperdiet. Suspendisse lobortis viverra aliquet. Duis laoreet, sem at ullamcorper consequat, eros orci maximus urna, non laoreet sapien orci ac ante. Quisque vulputate, magna ac venenatis tincidunt, nunc nibh convallis elit, sit amet mattis quam ipsum condimentum sem. Nunc maximus, nibh in lobortis dapibus, est erat lobortis tortor, eu euismod mi leo eu tellus. Praesent sit amet orci at quam porttitor cursus nec vel mauris. Nullam at sagittis tortor. Vivamus fermentum, quam nec egestas lobortis, velit velit molestie quam, in finibus libero mauris a lacus. -Sed nec dolor a lorem semper rutrum nec sit amet est. Vestibulum blandit bibendum nibh at commodo. Aenean sit amet purus dolor. Curabitur consequat volutpat aliquam. Nullam erat lorem, condimentum quis ultrices at, imperdiet at est. Nullam placerat mi sed facilisis rutrum. Proin metus felis, vulputate lacinia urna sit amet, rutrum fringilla felis. Donec quam lectus, porta sed elit et, dignissim luctus libero. -Suspendisse a augue ante. Phasellus hendrerit elit ut maximus consectetur. Aliquam id nisl ac lectus sodales rutrum quis vehicula purus. Vivamus sollicitudin turpis ac felis ullamcorper, id dictum ante iaculis. Fusce a libero est. In bibendum varius enim, gravida ultricies leo placerat et. Nunc ante erat, hendrerit sed sapien ac, molestie semper libero. Sed sagittis, quam non congue imperdiet, tellus leo ullamcorper metus, eget vestibulum tortor dui at dolor. Mauris est sem, tincidunt vel luctus non, tincidunt eu ipsum. Nullam a feugiat dui. Nunc vel velit vel ipsum sollicitudin dictum. Ut cursus imperdiet nibh, et aliquam ligula lacinia ac. -Cras erat felis, commodo ut posuere id, vehicula id neque. Vestibulum urna dolor, gravida vestibulum libero sed, pretium molestie mauris. Nullam in efficitur ipsum. Donec et ipsum finibus, bibendum diam vel, placerat est. Donec vitae gravida felis. Etiam augue justo, finibus id eros nec, consectetur lobortis risus. Aenean varius accumsan quam, ac pulvinar nunc pretium at. Mauris vitae risus maximus, eleifend leo ac, maximus nunc. Cras nibh ante, rhoncus eu eleifend eget, accumsan eget ex. -Vivamus tincidunt fermentum aliquet. Etiam et venenatis diam. Sed scelerisque convallis eros non pulvinar. Ut dignissim justo eros, id rhoncus magna mollis quis. Nullam lobortis odio at placerat feugiat. Morbi feugiat est eu mauris pellentesque efficitur. Praesent posuere arcu urna, eu cursus leo fermentum sit amet. Curabitur sit amet aliquam odio. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nunc malesuada nulla est, et varius nulla condimentum sed. Sed sit amet eros felis. Nullam vitae pretium lectus. -Etiam at massa vel urna varius accumsan. Proin non porttitor purus, nec facilisis libero. Quisque tristique, mi ac tristique vulputate, ipsum nibh porta urna, et tempus ex orci eu libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin non pellentesque elit, at vehicula justo. Ut facilisis mi sit amet est faucibus, id lobortis est varius. Nulla placerat justo lorem, quis condimentum nulla dapibus quis. Curabitur blandit non lorem at ultrices. Duis nunc mauris, cursus porttitor mauris mattis, consequat luctus neque. -Duis congue non quam eget dignissim. Etiam molestie metus et arcu sollicitudin cursus. Suspendisse cursus luctus rhoncus. Proin lorem metus, bibendum pharetra auctor vitae, malesuada eget risus. Aenean ac libero quis metus lacinia dignissim nec vitae tellus. Nulla consectetur semper ipsum, sed imperdiet elit lobortis sit amet. Vivamus iaculis arcu ut mattis iaculis. In metus tortor, euismod sit amet ligula eget, tincidunt euismod dolor. Aliquam eget elit at est faucibus aliquet sit amet quis leo. -Praesent lectus dolor, tincidunt et urna eget, consectetur volutpat libero. Quisque placerat vel sem ac accumsan. Nulla facilisi. Praesent facilisis condimentum quam. Vivamus malesuada nibh ut dignissim commodo. Morbi a egestas leo. Sed viverra, purus eu mattis varius, nibh nunc convallis est, et tristique nulla eros ut augue. Curabitur a dui tortor. Pellentesque viverra nisi ut risus laoreet vehicula. -Suspendisse potenti. Quisque et condimentum tellus. Vestibulum mollis sollicitudin risus, ac suscipit ex interdum ac. Vivamus mattis velit non lectus consectetur aliquam. Maecenas commodo iaculis eros ac suscipit. Quisque tempor libero in ultricies luctus. Phasellus dictum, orci in suscipit fringilla, est dui imperdiet purus, vitae tempor tellus odio et est. Vestibulum iaculis id felis in facilisis. Morbi luctus tortor sit amet dui varius iaculis. Sed gravida ipsum lectus, id tincidunt dui dapibus eget. Proin at vestibulum libero, nec ornare sapien. Quisque odio mi, maximus in pretium et, hendrerit at odio. -Nunc ullamcorper mattis laoreet. In dolor justo, imperdiet eget libero id, imperdiet laoreet erat. Donec molestie dictum leo, nec luctus ipsum viverra nec. Maecenas scelerisque metus sed vulputate ullamcorper. Maecenas varius eget felis vel porttitor. Sed sem diam, pellentesque vitae egestas ut, varius at urna. Donec eu metus commodo augue porta eleifend. Maecenas id porta erat. Aenean sed nunc orci. Ut nec tristique ipsum, ut egestas purus. Nullam egestas dictum tristique. Sed lacinia mauris quis cursus tristique. Quisque ac urna id diam elementum vulputate eu eu purus. -Aliquam sit amet purus bibendum, placerat massa quis, malesuada nisl. Nullam orci justo, egestas eget neque maximus, cursus porttitor velit. Donec vel sodales risus. Curabitur lorem dui, finibus id est a, hendrerit luctus velit. Praesent aliquet molestie felis, nec dictum leo tincidunt vel. Cras rutrum tempus egestas. Sed in ex interdum, lobortis nunc a, pharetra diam. Ut sed risus scelerisque purus rutrum rhoncus ut eget urna. Praesent mollis arcu vel auctor luctus. Nullam risus magna, iaculis non condimentum id, maximus sed diam. Integer porttitor ornare metus sed mattis. Nulla facilisi. Proin facilisis vestibulum dolor, sed hendrerit metus convallis a. Cras rutrum est eget mauris laoreet, nec blandit sapien euismod. -Nulla egestas auctor arcu in porttitor. Integer tincidunt ex non laoreet tincidunt. Suspendisse vitae urna ut nibh auctor viverra nec in lacus. In finibus sagittis velit, non rhoncus sapien tristique non. Suspendisse eu urna cursus, varius turpis vel, semper est. Donec orci augue, mollis sed auctor in, eleifend vitae justo. Cras commodo orci interdum turpis suscipit laoreet. Quisque venenatis lacus ut leo molestie pellentesque. Nunc id congue risus. Donec sagittis erat nunc, non consectetur erat euismod eu. Nam at velit nisl. \ No newline at end of file diff --git a/nitrite/build.gradle b/nitrite/build.gradle index 8a01b0e5a..a99e9ba56 100644 --- a/nitrite/build.gradle +++ b/nitrite/build.gradle @@ -44,7 +44,6 @@ repositories { dependencies { api platform(project(':nitrite-bom')) api "org.slf4j:slf4j-api" - api "org.objenesis:objenesis" api "org.jasypt:jasypt" annotationProcessor "org.projectlombok:lombok:1.18.24" diff --git a/nitrite/src/main/java/org/dizitart/no2/Nitrite.java b/nitrite/src/main/java/org/dizitart/no2/Nitrite.java index a42c458a9..cd5a39fa1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/Nitrite.java +++ b/nitrite/src/main/java/org/dizitart/no2/Nitrite.java @@ -18,17 +18,21 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.common.Constants; +import org.dizitart.no2.common.util.ObjectUtils; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.repository.ObjectRepository; -import org.dizitart.no2.store.StoreMetaData; +import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.store.NitriteStore; +import org.dizitart.no2.store.StoreMetaData; import org.dizitart.no2.transaction.Session; import java.util.Map; import java.util.Set; import static org.dizitart.no2.common.Constants.RESERVED_NAMES; +import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryName; +import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryNameByDecorator; import static org.dizitart.no2.common.util.ValidationUtils.notEmpty; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -112,6 +116,33 @@ static NitriteBuilder builder() { */ ObjectRepository getRepository(Class type, String key); + /** + * Opens a type-safe object repository using a {@link EntityDecorator}. If the repository + * does not exist it will be created automatically and returned. If a + * repository is already opened, it is returned as is. + *

+ * The returned repository is thread-safe for concurrent use. + * + * @param the type parameter + * @param entityDecorator the entityDecorator + * @return the repository + */ + ObjectRepository getRepository(EntityDecorator entityDecorator); + + /** + * Opens a type-safe object repository using a {@link EntityDecorator} and a key identifier + * from the store. If the repository does not exist it will be created automatically and + * returned. If a repository is already opened, it is returned as is. + *

+ * The returned repository is thread-safe for concurrent use. + * + * @param the type parameter + * @param entityDecorator the entityDecorator + * @param key the key + * @return the repository + */ + ObjectRepository getRepository(EntityDecorator entityDecorator, String key); + /** * Destroys a {@link NitriteCollection} without opening it first. * @@ -128,7 +159,7 @@ static NitriteBuilder builder() { void destroyRepository(Class type); /** - * Destroys an keyed-{@link ObjectRepository} without opening it first. + * Destroys a keyed-{@link ObjectRepository} without opening it first. * * @param the type parameter * @param type the type @@ -136,6 +167,23 @@ static NitriteBuilder builder() { */ void destroyRepository(Class type, String key); + /** + * Destroys an {@link ObjectRepository} without opening it first. + * + * @param the type parameter + * @param type the type + */ + void destroyRepository(EntityDecorator type); + + /** + * Destroys a keyed-{@link ObjectRepository} without opening it first. + * + * @param the type parameter + * @param type the type + * @param key the key + */ + void destroyRepository(EntityDecorator type, String key); + /** * Gets the set of all {@link NitriteCollection}s' names saved in the store. * @@ -226,7 +274,8 @@ default boolean hasCollection(String name) { */ default boolean hasRepository(Class type) { checkOpened(); - return listRepositories().contains(type.getName()); + String name = findRepositoryName(type, null); + return listRepositories().contains(name); } /** @@ -238,9 +287,37 @@ default boolean hasRepository(Class type) { * @return true if the repository exists; otherwise false. */ default boolean hasRepository(Class type, String key) { + checkOpened(); + String entityName = ObjectUtils.getEntityName(type); + return listKeyedRepositories().containsKey(key) + && listKeyedRepositories().get(key).contains(entityName); + } + + /** + * Checks whether a particular {@link ObjectRepository} exists in the store. + * + * @param the type parameter + * @param entityDecorator entityDecorator + * @return true if the repository exists; otherwise false. + */ + default boolean hasRepository(EntityDecorator entityDecorator) { + checkOpened(); + String name = findRepositoryNameByDecorator(entityDecorator, null); + return listRepositories().contains(name); + } + + /** + * Checks whether a particular keyed-{@link ObjectRepository} exists in the store. + * + * @param the type parameter. + * @param entityDecorator entityDecorator. + * @param key the key, which will be appended to the repositories name. + * @return true if the repository exists; otherwise false. + */ + default boolean hasRepository(EntityDecorator entityDecorator, String key) { checkOpened(); return listKeyedRepositories().containsKey(key) - && listKeyedRepositories().get(key).contains(type.getName()); + && listKeyedRepositories().get(key).contains(entityDecorator.getEntityName()); } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java index 1a78b88cf..e0f6acfdb 100644 --- a/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java +++ b/nitrite/src/main/java/org/dizitart/no2/NitriteDatabase.java @@ -27,6 +27,7 @@ import org.dizitart.no2.migration.MigrationManager; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.RepositoryFactory; +import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; import org.dizitart.no2.store.StoreMetaData; @@ -39,6 +40,7 @@ import static org.dizitart.no2.common.Constants.NITRITE_VERSION; import static org.dizitart.no2.common.Constants.STORE_INFO; import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryName; +import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryNameByDecorator; import static org.dizitart.no2.common.util.StringUtils.isNullOrEmpty; /** @@ -80,6 +82,18 @@ public ObjectRepository getRepository(Class type, String key) { return repositoryFactory.getRepository(nitriteConfig, type, key); } + @Override + public ObjectRepository getRepository(EntityDecorator entityDecorator) { + checkOpened(); + return repositoryFactory.getRepository(nitriteConfig, entityDecorator); + } + + @Override + public ObjectRepository getRepository(EntityDecorator entityDecorator, String key) { + checkOpened(); + return repositoryFactory.getRepository(nitriteConfig, entityDecorator, key); + } + @Override public void destroyCollection(String name) { checkOpened(); @@ -100,6 +114,20 @@ public void destroyRepository(Class type, String key) { store.removeMap(mapName); } + @Override + public void destroyRepository(EntityDecorator type) { + checkOpened(); + String mapName = findRepositoryNameByDecorator(type, null); + store.removeMap(mapName); + } + + @Override + public void destroyRepository(EntityDecorator type, String key) { + checkOpened(); + String mapName = findRepositoryNameByDecorator(type, key); + store.removeMap(mapName); + } + @Override public Set listCollectionNames() { checkOpened(); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java index b9c021f19..aedb15e49 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/DefaultNitriteCollection.java @@ -16,17 +16,17 @@ package org.dizitart.no2.collection; -import lombok.Getter; import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.events.CollectionEventInfo; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.collection.operation.CollectionOperations; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.concurrent.LockService; import org.dizitart.no2.common.event.EventBus; import org.dizitart.no2.common.event.NitriteEventBus; +import org.dizitart.no2.common.meta.Attributes; +import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.exceptions.IndexingException; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.NitriteIOException; @@ -35,7 +35,6 @@ import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; @@ -63,8 +62,6 @@ class DefaultNitriteCollection implements NitriteCollection { private Lock readLock; private CollectionOperations collectionOperations; private EventBus, CollectionEventListener> eventBus; - - @Getter private volatile boolean isDropped; DefaultNitriteCollection(String name, NitriteMap nitriteMap, @@ -305,9 +302,6 @@ public void drop() { if (collectionOperations != null) { // drop collection and indexes collectionOperations.dropCollection(); - - // close collection and indexes -// collectionOperations.close(); } // set all reference to null @@ -458,7 +452,7 @@ private void checkOpened() { private void validateRebuildIndex(IndexDescriptor indexDescriptor) { notNull(indexDescriptor, "index cannot be null"); - String[] indexFields = indexDescriptor.getIndexFields().getFieldNames().toArray(new String[0]); + String[] indexFields = indexDescriptor.getFields().getFieldNames().toArray(new String[0]); if (isIndexing(indexFields)) { throw new IndexingException("Cannot rebuild index, index is currently being built"); } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/FindOptions.java b/nitrite/src/main/java/org/dizitart/no2/collection/FindOptions.java index 50b3d8c1a..45469e068 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/FindOptions.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/FindOptions.java @@ -44,6 +44,7 @@ public class FindOptions { /** * Specifies the {@link Collator}. * + * @param collator the collator * @return the collator. */ @Setter(AccessLevel.PUBLIC) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/DocumentIndexWriter.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/DocumentIndexWriter.java index 394cfb706..85ec0ccc8 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/DocumentIndexWriter.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/DocumentIndexWriter.java @@ -81,7 +81,7 @@ void updateIndexEntry(Document oldDocument, Document newDocument) { private void writeIndexEntryInternal(IndexDescriptor indexDescriptor, Document document, NitriteIndexer nitriteIndexer) { if (indexDescriptor != null) { - Fields fields = indexDescriptor.getIndexFields(); + Fields fields = indexDescriptor.getFields(); FieldValues fieldValues = DocumentUtils.getValues(document, fields); // if dirty index and currently indexing is not running, rebuild @@ -97,7 +97,7 @@ private void writeIndexEntryInternal(IndexDescriptor indexDescriptor, Document d private void removeIndexEntryInternal(IndexDescriptor indexDescriptor, Document document, NitriteIndexer nitriteIndexer) { if (indexDescriptor != null) { - Fields fields = indexDescriptor.getIndexFields(); + Fields fields = indexDescriptor.getFields(); FieldValues fieldValues = DocumentUtils.getValues(document, fields); // if dirty index and currently indexing is not running, rebuild diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/FindOptimizer.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/FindOptimizer.java index 31c209e79..43a44b11a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/FindOptimizer.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/FindOptimizer.java @@ -222,7 +222,7 @@ private void planForIndexScanningFilters(FindPlan findPlan, Set> indexFilterMap = new TreeMap<>(Collections.reverseOrder()); for (IndexDescriptor indexDescriptor : indexDescriptors) { - List fieldNames = indexDescriptor.getIndexFields().getFieldNames(); + List fieldNames = indexDescriptor.getFields().getFieldNames(); List indexedFilters = new ArrayList<>(); for (String fieldName : fieldNames) { @@ -299,7 +299,7 @@ private void readSortOption(FindOptions findOptions, FindPlan findPlan) { if (indexDescriptor != null) { // get index field names - List indexedFieldNames = indexDescriptor.getIndexFields().getFieldNames(); + List indexedFieldNames = indexDescriptor.getFields().getFieldNames(); boolean canUseIndex = false; Map indexScanOrder = new HashMap<>(); diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java index 297d60c94..96bee500b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexManager.java @@ -84,7 +84,7 @@ public Collection findMatchingIndexDescriptors(Fields fields) { List indexDescriptors = new ArrayList<>(); for (IndexDescriptor indexDescriptor : getIndexDescriptors()) { - if (indexDescriptor.getIndexFields().startsWith(fields)) { + if (indexDescriptor.getFields().startsWith(fields)) { indexDescriptors.add(indexDescriptor); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java index f9de72e22..56441ad32 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/operation/IndexOperations.java @@ -70,12 +70,12 @@ void createIndex(Fields fields, String indexType) { // call to this method is already synchronized, only one thread per field // can access it only if rebuild is already not running for that field void buildIndex(IndexDescriptor indexDescriptor, boolean rebuild) { - final Fields fields = indexDescriptor.getIndexFields(); + final Fields fields = indexDescriptor.getFields(); if (getBuildFlag(fields).compareAndSet(false, true)) { buildIndexInternal(indexDescriptor, rebuild); return; } - throw new IndexingException("Index build already in progress on fields: " + indexDescriptor.getIndexFields()); + throw new IndexingException("Index build already in progress on fields: " + indexDescriptor.getFields()); } void dropIndex(Fields fields) { @@ -106,7 +106,7 @@ void dropAllIndices() { // we can drop all indices in parallel List> futures = new ArrayList<>(); for (IndexDescriptor index : listIndexes()) { - futures.add(runAsync(() -> dropIndex(index.getIndexFields()))); + futures.add(runAsync(() -> dropIndex(index.getFields()))); } for (Future future : futures) { @@ -170,7 +170,7 @@ private AtomicBoolean getBuildFlag(Fields field) { } private void buildIndexInternal(IndexDescriptor indexDescriptor, boolean rebuild) { - Fields fields = indexDescriptor.getIndexFields(); + Fields fields = indexDescriptor.getFields(); try { alert(EventType.IndexStart, fields); // first put dirty marker @@ -186,7 +186,7 @@ private void buildIndexInternal(IndexDescriptor indexDescriptor, boolean rebuild for (Pair entry : nitriteMap.entries()) { Document document = entry.getSecond(); - FieldValues fieldValues = DocumentUtils.getValues(document, indexDescriptor.getIndexFields()); + FieldValues fieldValues = DocumentUtils.getValues(document, indexDescriptor.getFields()); nitriteIndexer.writeIndexEntry(fieldValues, indexDescriptor, nitriteConfig); } } finally { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/Constants.java b/nitrite/src/main/java/org/dizitart/no2/common/Constants.java index 4f21e18ea..83118fe47 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/Constants.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/Constants.java @@ -234,5 +234,4 @@ private Constants() {} * The constant INITIAL_REVISION. */ public static final Integer INITIAL_SCHEMA_VERSION = 1; - } diff --git a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonExtension.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/EntityConverter.java similarity index 61% rename from nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonExtension.java rename to nitrite/src/main/java/org/dizitart/no2/common/mapper/EntityConverter.java index d6be1d6e1..ca14ab4c0 100644 --- a/nitrite-jackson-mapper/src/main/java/org/dizitart/no2/common/mapper/JacksonExtension.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/EntityConverter.java @@ -1,29 +1,28 @@ /* - * Copyright (c) 2017-2020. Nitrite author or authors. + * Copyright (c) 2017-2022 Nitrite author or authors. * * 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 + * 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 org.dizitart.no2.common.mapper; -import com.fasterxml.jackson.databind.Module; +import org.dizitart.no2.collection.Document; -import java.util.List; +public interface EntityConverter { + Class getEntityType(); -/** - * @author Anindya Chatterjee - */ -public interface JacksonExtension { - List> getSupportedTypes(); - Module getModule(); + Document toDocument(T entity, NitriteMapper nitriteMapper); + + T fromDocument(Document document, NitriteMapper nitriteMapper); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/Mappable.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/Mappable.java deleted file mode 100644 index c0cbf64f0..000000000 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/Mappable.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2017-2020. Nitrite author or authors. - * - * 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 org.dizitart.no2.common.mapper; - -import org.dizitart.no2.collection.Document; - -/** - * An object that maps itself to a {@link Document} - * and vice versa. - * - * @author Anindya Chatterjee - * @since 2.0 - */ -public interface Mappable { - /** - * Writes the instance data to a {@link Document} and returns it. - * - * @param mapper the {@link NitriteMapper} instance used. - * @return the document generated. - */ - Document write(NitriteMapper mapper); - - /** - * Reads the document and populate all fields of this instance. - * - * @param mapper the {@link NitriteMapper} instance used. - * @param document the document. - */ - void read(NitriteMapper mapper, Document document); -} diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/NitriteMapper.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/NitriteMapper.java index 04aa1961b..725871cd2 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/NitriteMapper.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/NitriteMapper.java @@ -35,20 +35,4 @@ public interface NitriteMapper extends NitritePlugin { * @return the target */ Target convert(Source source, Class type); - - /** - * Checks if the provided type is registered as a value type. - * - * @param type the type - * @return the boolean - */ - boolean isValueType(Class type); - - /** - * Checks if an object is of a value type. - * - * @param object the object - * @return the boolean - */ - boolean isValue(Object object); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/SimpleDocumentMapper.java similarity index 63% rename from nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java rename to nitrite/src/main/java/org/dizitart/no2/common/mapper/SimpleDocumentMapper.java index f7ae7f377..242b797d0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/MappableMapper.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/SimpleDocumentMapper.java @@ -19,79 +19,58 @@ import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.util.ObjectUtils; import org.dizitart.no2.exceptions.ObjectMappingException; import java.util.*; import static org.dizitart.no2.common.util.Iterables.listOf; -import static org.dizitart.no2.common.util.ObjectUtils.newInstance; +import static org.dizitart.no2.common.util.ValidationUtils.notNull; /** - * A {@link NitriteMapper} based on {@link Mappable} implementation. + * A {@link NitriteMapper} based on {@link EntityConverter} implementation. * * @author Anindya Chatterjee. * @since 4.0 */ -public class MappableMapper implements NitriteMapper { +public class SimpleDocumentMapper implements NitriteMapper { private final Set> valueTypes; - private final Map, MappableFactory> mappableFactories; + private final Map, EntityConverter> converterRegistry; /** - * Instantiates a new {@link MappableMapper}. + * Instantiates a new {@link SimpleDocumentMapper}. * * @param valueTypes the value types */ - public MappableMapper(Class... valueTypes) { + public SimpleDocumentMapper(Class... valueTypes) { this.valueTypes = new HashSet<>(); - this.mappableFactories = new HashMap<>(); + this.converterRegistry = new HashMap<>(); init(listOf(valueTypes)); } - /** - * Converts a document to a target object of type Target. - * - * @param the type parameter - * @param source the source - * @param type the type - * @return the target - */ + @Override @SuppressWarnings("unchecked") - protected Target convertFromDocument(Document source, Class type) { + public Target convert(Source source, Class type) { if (source == null) { return null; } - if (Mappable.class.isAssignableFrom(type)) { - Target item; - if (mappableFactories.containsKey(type)) { - MappableFactory factory = mappableFactories.get(type); - item = (Target) factory.create(); - } else { - item = newInstance(type, false); + if (isValue(source)) { + return (Target) source; + } else { + if (Document.class.isAssignableFrom(type)) { + if (source instanceof Document) { + return (Target) source; + } else { + return (Target) convertToDocument(source); + } + } else if (source instanceof Document) { + return convertFromDocument((Document) source, type); } - - if (item == null) return null; - ((Mappable) item).read(this, source); - return item; - } - - throw new ObjectMappingException(type.getName() + " is not a Mappable"); - } - - /** - * Converts an object of type Source to a document. - * - * @param the type parameter - * @param source the source - * @return the document - */ - protected Document convertToDocument(Source source) { - if (source instanceof Mappable) { - Mappable mappable = (Mappable) source; - return mappable.write(this); } - throw new ObjectMappingException("Object of type " + source.getClass().getName() + " is not Mappable"); + throw new ObjectMappingException("Can't convert object to type " + type + + ", try registering a EntityConverter for it."); } /** @@ -99,47 +78,66 @@ protected Document convertToDocument(Source source) { * * @param valueType the value type */ - protected void addValueType(Class valueType) { + public void addValueType(Class valueType) { this.valueTypes.add(valueType); } /** - * Register a {@link Mappable} factory to be used when converting a document to an object. + * Registers an {@link EntityConverter}. * - * @param the type parameter - * @param factory the factory - * @param type the type + * @param entityConverter the entity converter */ - public void registerMappable(MappableFactory factory, Class type) { - mappableFactories.put(type, factory); + public void registerEntityConverter(EntityConverter entityConverter) { + notNull(entityConverter, "entityConverter cannot be null"); + converterRegistry.put(entityConverter.getEntityType(), entityConverter); } @Override + public void initialize(NitriteConfig nitriteConfig) { + } + + /** + * Converts a document to a target object of type Target. + * + * @param the type parameter + * @param source the source + * @param type the type + * @return the target + */ @SuppressWarnings("unchecked") - public Target convert(Source source, Class type) { + protected Target convertFromDocument(Document source, Class type) { if (source == null) { return null; } - if (isValue(source)) { - return (Target) source; - } else { - if (Document.class.isAssignableFrom(type)) { - if (source instanceof Document) { - return (Target) source; - } else { - return (Target) convertToDocument(source); - } - } else if (source instanceof Document) { - return convertFromDocument((Document) source, type); - } + if (converterRegistry.containsKey(type)) { + EntityConverter serializer = (EntityConverter) converterRegistry.get(type); + return serializer.fromDocument(source, this); } - throw new ObjectMappingException("Can't convert object of type " + source.getClass() + " to type " + type); + throw new ObjectMappingException("Can't convert Document to type " + type + + ", try registering a EntityConverter for it."); } - @Override - public boolean isValueType(Class type) { + /** + * Converts an object of type Source to a document. + * + * @param the type parameter + * @param source the source + * @return the document + */ + @SuppressWarnings("unchecked") + protected Document convertToDocument(Source source) { + if (converterRegistry.containsKey(source.getClass())) { + EntityConverter serializer = (EntityConverter) converterRegistry.get(source.getClass()); + return serializer.toDocument(source, this); + } + + throw new ObjectMappingException("Can't convert object of type " + source.getClass().getName() + + " to Document, try registering a EntityConverter for it."); + } + + private boolean isValueType(Class type) { if (type.isPrimitive() && type != void.class) return true; if (valueTypes.contains(type)) return true; for (Class valueType : valueTypes) { @@ -148,25 +146,17 @@ public boolean isValueType(Class type) { return false; } - @Override - public boolean isValue(Object object) { + private boolean isValue(Object object) { return isValueType(object.getClass()); } - @Override - public void initialize(NitriteConfig nitriteConfig) { - - } - private void init(List> valueTypes) { - this.valueTypes.add(Number.class); - this.valueTypes.add(Boolean.class); - this.valueTypes.add(Character.class); - this.valueTypes.add(String.class); - this.valueTypes.add(byte[].class); + for (Class builtInType : ObjectUtils.builtInTypes()) { + this.valueTypes.add(builtInType); + } + this.valueTypes.add(Enum.class); this.valueTypes.add(NitriteId.class); - this.valueTypes.add(Date.class); if (valueTypes != null && !valueTypes.isEmpty()) { this.valueTypes.addAll(valueTypes); diff --git a/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java b/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java index 04fd3a403..b724975d7 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/module/PluginManager.java @@ -22,7 +22,7 @@ import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.PluginException; import org.dizitart.no2.index.*; -import org.dizitart.no2.common.mapper.MappableMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.store.NitriteStore; import org.dizitart.no2.store.memory.InMemoryStoreModule; @@ -188,7 +188,7 @@ protected void loadInternalPlugins() { if (nitriteMapper == null) { log.debug("Loading mappable mapper"); - NitritePlugin plugin = new MappableMapper(); + NitritePlugin plugin = new SimpleDocumentMapper(); loadPlugin(plugin); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/DocumentUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/DocumentUtils.java index 9c92db1ab..904cb81c5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/DocumentUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/DocumentUtils.java @@ -70,10 +70,7 @@ public static Filter createUniqueFilter(Document document) { * @return the document */ public static Document skeletonDocument(NitriteMapper nitriteMapper, Class type) { - if (nitriteMapper.isValueType(type)) { - return Document.createDocument(); - } - T dummy = newInstance(type, true); + T dummy = newInstance(type, true, nitriteMapper); Document document = nitriteMapper.convert(dummy, Document.class); return removeValues(document); } @@ -106,13 +103,14 @@ public static FieldValues getValues(Document document, Fields fields) { private static Document removeValues(Document document) { if (document == null) return null; + Document newDoc = Document.createDocument(); for (Pair entry : document) { if (entry.getSecond() instanceof Document) { - document.put(entry.getFirst(), removeValues((Document) entry.getSecond())); + newDoc.put(entry.getFirst(), removeValues((Document) entry.getSecond())); } else { - document.put(entry.getFirst(), null); + newDoc.put(entry.getFirst(), null); } } - return document; + return newDoc; } } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/IndexUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/IndexUtils.java index bfb9413b1..a304200d1 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/IndexUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/IndexUtils.java @@ -41,7 +41,7 @@ public static String deriveIndexMapName(IndexDescriptor descriptor) { INTERNAL_NAME_SEPARATOR + descriptor.getCollectionName() + INTERNAL_NAME_SEPARATOR + - descriptor.getIndexFields().getEncodedName() + + descriptor.getFields().getEncodedName() + INTERNAL_NAME_SEPARATOR + descriptor.getIndexType(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java index 40c7526df..9779f202f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java @@ -17,21 +17,25 @@ package org.dizitart.no2.common.util; import lombok.extern.slf4j.Slf4j; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ObjectMappingException; import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Entity; -import org.objenesis.Objenesis; -import org.objenesis.ObjenesisSerializer; -import org.objenesis.ObjenesisStd; -import org.objenesis.instantiator.ObjectInstantiator; import java.io.*; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; import java.util.*; +import java.util.regex.Pattern; import static org.dizitart.no2.common.Constants.KEY_OBJ_SEPARATOR; import static org.dizitart.no2.common.util.Iterables.toArray; @@ -46,8 +50,6 @@ @Slf4j public class ObjectUtils { private static final Map, Class> PRIMITIVE_TO_WRAPPER_TYPE; - private static final Objenesis stdObjenesis = new ObjenesisStd(true); - private static final Objenesis serializerObjenesis = new ObjenesisSerializer(true); static { Map, Class> primToWrap = new LinkedHashMap<>(); @@ -92,6 +94,14 @@ public static String findRepositoryName(String entityName, String key) { } } + public static String findRepositoryNameByDecorator(EntityDecorator entityDecorator, String key) { + String entityName = entityDecorator.getEntityName(); + if (entityName.contains(KEY_OBJ_SEPARATOR)) { + throw new ValidationException(entityName + " is not a valid entity name"); + } + return findRepositoryName(entityName, key); + } + /** * Gets the key name of a keyed-{@link ObjectRepository} * @@ -181,15 +191,13 @@ public static boolean deepEquals(Object o1, Object o2) { } } - @SuppressWarnings({"unchecked", "rawtypes"}) - public static T newInstance(Class type, boolean createSkeleton) { + public static T newInstance(Class type, boolean createSkeleton, NitriteMapper nitriteMapper) { try { if (type.isPrimitive() || type.isArray() || type == String.class) { return defaultValue(type); } - ObjectInstantiator instantiator = getInstantiatorOf(type); - T item = (T) instantiator.newInstance(); + T item = nitriteMapper.convert(Document.createDocument(), type); if (createSkeleton) { Field[] fields = type.getDeclaredFields(); @@ -199,17 +207,8 @@ public static T newInstance(Class type, boolean createSkeleton) { if (!Modifier.isStatic(field.getModifiers())) { field.setAccessible(true); - // remove final modifier - try { - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); - } catch (NoSuchFieldException e) { - // ignore in case of java 12+ - } - if (isSkeletonRequired(type, field.getType())) { - field.set(item, newInstance(field.getType(), true)); + field.set(item, newInstance(field.getType(), true, nitriteMapper)); } else { field.set(item, defaultValue(field.getType())); } @@ -224,14 +223,65 @@ public static T newInstance(Class type, boolean createSkeleton) { } } - public static boolean isValueType(Class retType) { + public static boolean isValue(Object value, NitriteMapper nitriteMapper) { + try { + if (value == null) return true; // special case + + nitriteMapper.convert(value, Comparable.class); + return true; + } catch (Exception e) { + return isBuiltInValueType(value.getClass()); + } + } + + public static boolean isValueType(Class type, NitriteMapper nitriteMapper) { + try { + Object value = newInstance(type, false, nitriteMapper); + if (value != null) { + return isValue(value, nitriteMapper); + } else { + return isBuiltInValueType(type); + } + } catch (Exception e) { + return isBuiltInValueType(type); + } + } + + public static boolean isBuiltInValueType(Class retType) { if (retType.isPrimitive() && retType != void.class) return true; if (Number.class.isAssignableFrom(retType)) return true; - if (Boolean.class == retType) return true; - if (Character.class == retType) return true; - if (String.class == retType) return true; if (byte[].class.isAssignableFrom(retType)) return true; - return Enum.class.isAssignableFrom(retType); + if (Enum.class.isAssignableFrom(retType)) return true; + return builtInTypes().contains(retType); + } + + public static List> builtInTypes() { + return Iterables.listOf( + byte[].class, + Number.class, + Byte.class, + Short.class, + Integer.class, + Long.class, + Float.class, + Double.class, + BigDecimal.class, + BigInteger.class, + Boolean.class, + Character.class, + String.class, + Date.class, + URL.class, + URI.class, + Currency.class, + Calendar.class, + StringBuffer.class, + StringBuilder.class, + Locale.class, + Void.class, + UUID.class, + Pattern.class + ); } public static boolean isCompatibleTypes(Class type1, Class type2) { @@ -292,14 +342,6 @@ private static Class toWrapperType(Class type) { return (wrapped == null) ? type : wrapped; } - private static ObjectInstantiator getInstantiatorOf(Class type) { - if (Serializable.class.isAssignableFrom(type)) { - return serializerObjenesis.getInstantiatorOf(type); - } else { - return stdObjenesis.getInstantiatorOf(type); - } - } - private static boolean isSkeletonRequired(Class

enclosingType, Class fieldType) { String fieldTypePackage = getPackageName(fieldType); String enclosingTypePackage = getPackageName(enclosingType); @@ -349,7 +391,7 @@ private static T defaultValue(Class type) { } if (type == String.class) { - return (T) ""; + return (T) null; } return null; diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java index 43b656da4..4d9b9e08f 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java @@ -23,6 +23,7 @@ import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.FilterException; +import static org.dizitart.no2.common.util.ObjectUtils.isValue; import static org.dizitart.no2.common.util.ValidationUtils.notEmpty; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -67,7 +68,7 @@ public Object getValue() { if (getObjectFilter()) { NitriteMapper nitriteMapper = getNitriteConfig().nitriteMapper(); validateSearchTerm(nitriteMapper, field, value); - if (nitriteMapper.isValue(value)) { + if (isValue(value, nitriteMapper)) { value = nitriteMapper.convert(value, Comparable.class); } } @@ -81,7 +82,7 @@ protected void validateSearchTerm(NitriteMapper nitriteMapper, String field, Obj notEmpty(field, "field cannot be empty"); if (value != null) { - if (!nitriteMapper.isValue(value) && !(value instanceof Comparable)) { + if (!isValue(value, nitriteMapper) && !(value instanceof Comparable)) { throw new FilterException("The value for field '" + field + "' is not a valid search term"); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java index d49e815ad..0198249cc 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexDescriptor.java @@ -54,11 +54,10 @@ public class IndexDescriptor implements Comparable, Serializabl /** * Gets the target fields for the index. * - * @param indexFields fields in the index * @return the target fields. */ @Getter - private Fields indexFields; + private Fields fields; /** * Gets the collection name. @@ -83,7 +82,7 @@ public IndexDescriptor(String indexType, Fields fields, String collectionName) { notEmpty(collectionName, "collectionName cannot be empty"); this.indexType = indexType; - this.indexFields = fields; + this.fields = fields; this.collectionName = collectionName; } @@ -100,13 +99,13 @@ public int compareTo(IndexDescriptor other) { // for two unique indices, the one with encompassing higher // number of fields has the higher cardinality if (this.isUniqueIndex()) { - return this.indexFields.compareTo(other.indexFields); + return this.fields.compareTo(other.fields); } // for two non-unique indices, the one with encompassing higher // number of fields has the higher cardinality if (!other.isUniqueIndex()) { - return this.indexFields.compareTo(other.indexFields); + return this.fields.compareTo(other.fields); } return -1; @@ -118,7 +117,7 @@ public int compareTo(IndexDescriptor other) { * @return the boolean */ public boolean isCompoundIndex() { - return indexFields.getFieldNames().size() > 1; + return fields.getFieldNames().size() > 1; } private boolean isUniqueIndex() { @@ -127,13 +126,13 @@ private boolean isUniqueIndex() { private void writeObject(ObjectOutputStream stream) throws IOException { stream.writeUTF(indexType); - stream.writeObject(indexFields); + stream.writeObject(fields); stream.writeUTF(collectionName); } private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { indexType = stream.readUTF(); - indexFields = (Fields) stream.readObject(); + fields = (Fields) stream.readObject(); collectionName = stream.readUTF(); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexFields.java b/nitrite/src/main/java/org/dizitart/no2/index/IndexFields.java new file mode 100644 index 000000000..5ae7bdc12 --- /dev/null +++ b/nitrite/src/main/java/org/dizitart/no2/index/IndexFields.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.index; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.dizitart.no2.common.Fields; +import org.dizitart.no2.common.util.StringUtils; + +import java.util.Arrays; + +import static org.dizitart.no2.common.util.ValidationUtils.notEmpty; +import static org.dizitart.no2.common.util.ValidationUtils.notNull; + +/** + * Represents an index fields. + * + * @author Anindya Chatterjee + * @since 4.0 + */ +@EqualsAndHashCode(callSuper = true) +public class IndexFields extends Fields { + private static final long serialVersionUID = 1662219840L; + + /** + * The index type. + */ + @Getter + private String indexType; + + /** + * Creates a {@link IndexFields} instance with field names and index type. + * + * @param indexType the index type + * @param fields the fields + * @return the fields + */ + public static IndexFields create(String indexType, String... fields) { + notNull(fields, "fields cannot be null"); + notEmpty(fields, "fields cannot be empty"); + + if (StringUtils.isNullOrEmpty(indexType)) { + indexType = IndexType.UNIQUE; + } + + IndexFields f = new IndexFields(); + f.fieldNames.addAll(Arrays.asList(fields)); + f.indexType = indexType; + return f; + } + + @Override + public String getEncodedName() { + return indexType + "[" + super.getEncodedName() + "]"; + } + + @Override + public String toString() { + return this.getEncodedName(); + } +} diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/DatabaseInstruction.java b/nitrite/src/main/java/org/dizitart/no2/migration/DatabaseInstruction.java index c1c2acb4c..cd2bf594d 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/DatabaseInstruction.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/DatabaseInstruction.java @@ -3,6 +3,7 @@ import org.dizitart.no2.common.tuples.Pair; import org.dizitart.no2.common.tuples.Triplet; import org.dizitart.no2.common.util.SecureString; +import org.dizitart.no2.repository.EntityDecorator; import static org.dizitart.no2.common.util.ObjectUtils.getEntityName; @@ -15,7 +16,7 @@ public interface DatabaseInstruction extends Instruction { /** - * Adds an instruction to set an user authentication to the database. + * Adds an instruction to set a user authentication to the database. * * @param username the username * @param password the password @@ -66,28 +67,48 @@ default DatabaseInstruction dropCollection(String collectionName) { * @return the database instruction */ default DatabaseInstruction dropRepository(Class type) { - return dropRepository(getEntityName(type)); + return dropRepository(type, null); + } + + /** + * Adds an instruction to drop a keyed {@link org.dizitart.no2.repository.ObjectRepository} from the database. + * + * @param type the type + * @param key the key + * @return the database instruction + */ + default DatabaseInstruction dropRepository(Class type, String key) { + return dropRepository(getEntityName(type), key); } /** * Adds an instruction to drop an {@link org.dizitart.no2.repository.ObjectRepository} from the database. * - * @param typeName the type name + * @param entityDecorator the entityDecorator * @return the database instruction */ - default DatabaseInstruction dropRepository(String typeName) { - return dropRepository(typeName, null); + default DatabaseInstruction dropRepository(EntityDecorator entityDecorator) { + return dropRepository(entityDecorator, null); } /** - * Adds an instruction to drop a keyed {@link org.dizitart.no2.repository.ObjectRepository} from the database. + * Adds an instruction to drop an {@link org.dizitart.no2.repository.ObjectRepository} from the database. * - * @param type the type - * @param key the key + * @param entityDecorator the entityDecorator * @return the database instruction */ - default DatabaseInstruction dropRepository(Class type, String key) { - return dropRepository(getEntityName(type), key); + default DatabaseInstruction dropRepository(EntityDecorator entityDecorator, String key) { + return dropRepository(entityDecorator.getEntityName(), key); + } + + /** + * Adds an instruction to drop an {@link org.dizitart.no2.repository.ObjectRepository} from the database. + * + * @param typeName the type name + * @return the database instruction + */ + default DatabaseInstruction dropRepository(String typeName) { + return dropRepository(typeName, null); } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/InstructionSet.java b/nitrite/src/main/java/org/dizitart/no2/migration/InstructionSet.java index 46c338b8c..5848490dc 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/InstructionSet.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/InstructionSet.java @@ -1,5 +1,7 @@ package org.dizitart.no2.migration; +import org.dizitart.no2.repository.EntityDecorator; + import static org.dizitart.no2.common.util.ObjectUtils.getEntityName; /** @@ -19,32 +21,53 @@ public interface InstructionSet { /** * Creates a {@link RepositoryInstruction}. * - * @param typeName the type name + * @param type the type * @return the repository instruction */ - default RepositoryInstruction forRepository(String typeName) { - return forRepository(typeName, null); + default RepositoryInstruction forRepository(Class type) { + return forRepository(type, null); } /** * Creates a {@link RepositoryInstruction}. * * @param type the type + * @param key the key * @return the repository instruction */ - default RepositoryInstruction forRepository(Class type) { - return forRepository(getEntityName(type), null); + default RepositoryInstruction forRepository(Class type, String key) { + return forRepository(getEntityName(type), key); } /** * Creates a {@link RepositoryInstruction}. * - * @param type the type + * @param entityDecorator the entityDecorator + * @return the repository instruction + */ + default RepositoryInstruction forRepository(EntityDecorator entityDecorator) { + return forRepository(entityDecorator, null); + } + + /** + * Creates a {@link RepositoryInstruction}. + * + * @param entityDecorator the entityDecorator * @param key the key * @return the repository instruction */ - default RepositoryInstruction forRepository(Class type, String key) { - return forRepository(getEntityName(type), key); + default RepositoryInstruction forRepository(EntityDecorator entityDecorator, String key) { + return forRepository(entityDecorator.getEntityName(), key); + } + + /** + * Creates a {@link RepositoryInstruction}. + * + * @param typeName the type name + * @return the repository instruction + */ + default RepositoryInstruction forRepository(String typeName) { + return forRepository(typeName, null); } /** diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructionSet.java b/nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructionSet.java index 2d1e3bc53..01f56f3ee 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructionSet.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/NitriteInstructionSet.java @@ -25,11 +25,11 @@ public DatabaseInstruction forDatabase() { } @Override - public RepositoryInstruction forRepository(String typeName, String key) { + public RepositoryInstruction forRepository(String entityName, String key) { return new RepositoryInstruction() { @Override public String entityName() { - return typeName; + return entityName; } @Override diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/commands/Rename.java b/nitrite/src/main/java/org/dizitart/no2/migration/commands/Rename.java index 923aa4205..5a2bcb47e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/commands/Rename.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/commands/Rename.java @@ -39,7 +39,7 @@ public void execute(Nitrite nitrite) { try (IndexManager indexManager = new IndexManager(oldName, nitrite.getConfig())) { Collection indexEntries = indexManager.getIndexDescriptors(); for (IndexDescriptor indexDescriptor : indexEntries) { - Fields field = indexDescriptor.getIndexFields(); + Fields field = indexDescriptor.getFields(); String indexType = indexDescriptor.getIndexType(); newOperations.createIndex(field, indexType); } diff --git a/nitrite/src/main/java/org/dizitart/no2/migration/commands/RenameField.java b/nitrite/src/main/java/org/dizitart/no2/migration/commands/RenameField.java index 6f9f92ec8..564a4878b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/migration/commands/RenameField.java +++ b/nitrite/src/main/java/org/dizitart/no2/migration/commands/RenameField.java @@ -47,9 +47,9 @@ public void execute(Nitrite nitrite) { for (IndexDescriptor matchingIndexDescriptor : matchingIndexDescriptors) { String indexType = matchingIndexDescriptor.getIndexType(); - Fields oldIndexFields = matchingIndexDescriptor.getIndexFields(); + Fields oldIndexFields = matchingIndexDescriptor.getFields(); Fields newIndexFields = getNewIndexFields(oldIndexFields, oldName, newName); - operations.dropIndex(matchingIndexDescriptor.getIndexFields()); + operations.dropIndex(matchingIndexDescriptor.getFields()); operations.createIndex(newIndexFields, indexType); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java b/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java index d34b352ce..29ee27be9 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java @@ -49,7 +49,7 @@ public AnnotationScanner(Class type, NitriteCollection collection, NitriteMap this.collection = collection; this.reflector = new Reflector(); this.indices = new HashSet<>(); - this.indexValidator = new IndexValidator(reflector); + this.indexValidator = new IndexValidator(); } public void createIndices() { diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java b/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java index d60923af3..b007dfeb6 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/DefaultObjectRepository.java @@ -22,13 +22,13 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.UpdateOptions; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.meta.Attributes; +import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexOptions; -import org.dizitart.no2.common.mapper.NitriteMapper; -import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.store.NitriteStore; import java.util.Collection; @@ -43,7 +43,8 @@ class DefaultObjectRepository implements ObjectRepository { private final NitriteCollection collection; private final NitriteConfig nitriteConfig; - private final Class type; + private Class type; + private EntityDecorator entityDecorator; private RepositoryOperations operations; DefaultObjectRepository(Class type, @@ -55,6 +56,15 @@ class DefaultObjectRepository implements ObjectRepository { initialize(); } + DefaultObjectRepository(EntityDecorator entityDecorator, + NitriteCollection collection, + NitriteConfig nitriteConfig) { + this.entityDecorator = entityDecorator; + this.collection = collection; + this.nitriteConfig = nitriteConfig; + initialize(); + } + @Override public void addProcessor(Processor processor) { notNull(processor, "a null processor cannot be added"); @@ -148,7 +158,7 @@ public void clear() { @Override public Cursor find(Filter filter, FindOptions findOptions) { - return operations.find(filter, findOptions, type); + return operations.find(filter, findOptions, getType()); } @Override @@ -209,7 +219,11 @@ public void setAttributes(Attributes attributes) { @Override public Class getType() { - return type; + if (entityDecorator != null) { + return entityDecorator.getEntityType(); + } else { + return type; + } } @Override @@ -219,8 +233,11 @@ public NitriteCollection getDocumentCollection() { private void initialize() { NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); - operations = new RepositoryOperations(type, nitriteMapper, collection); + if (entityDecorator != null) { + operations = new RepositoryOperations(entityDecorator, nitriteMapper, collection); + } else { + operations = new RepositoryOperations(type, nitriteMapper, collection); + } operations.createIndices(); } - } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecorator.java b/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecorator.java new file mode 100644 index 000000000..5f2dbfe23 --- /dev/null +++ b/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecorator.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.repository; + +import org.dizitart.no2.index.IndexFields; + +import java.util.List; + +public interface EntityDecorator { + Class getEntityType(); + + EntityId getIdField(); + + List getIndexFields(); + + default String getEntityName() { + return getEntityType().getName(); + } +} diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorReader.java b/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorReader.java new file mode 100644 index 000000000..5b1db48f4 --- /dev/null +++ b/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorReader.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.repository; + +import lombok.AccessLevel; +import lombok.Getter; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.util.StringUtils; +import org.dizitart.no2.index.IndexFields; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.dizitart.no2.index.IndexOptions.indexOptions; + +/** + * @author Anindya Chatterjee + */ +public class EntityDecoratorReader { + private final EntityDecorator entityDecorator; + private final NitriteCollection collection; + private final NitriteMapper nitriteMapper; + private final IndexValidator indexValidator; + + private final Reflector reflector; + + @Getter(AccessLevel.PACKAGE) + private final Set indices; + + @Getter + private ObjectIdField objectIdField; + + public EntityDecoratorReader(EntityDecorator entityDecorator, + NitriteCollection collection, + NitriteMapper nitriteMapper) { + this.entityDecorator = entityDecorator; + this.collection = collection; + this.nitriteMapper = nitriteMapper; + this.indices = new HashSet<>(); + this.indexValidator = new IndexValidator(); + this.reflector = new Reflector(); + } + + public void readEntity() { + readIndices(); + readIdField(); + } + + public void createIndices() { + for (IndexFields index : indices) { + String[] fields = index.getFieldNames().toArray(new String[0]); + if (!collection.hasIndex(fields)) { + collection.createIndex(indexOptions(index.getIndexType()), fields); + } + } + } + + public void createIdIndex() { + if (objectIdField != null) { + String[] fieldNames = objectIdField.getEmbeddedFieldNames(); + if (!collection.hasIndex(fieldNames)) { + collection.createIndex(fieldNames); + } + } + } + + private void readIndices() { + if (entityDecorator.getIndexFields() != null) { + for (IndexFields indexField : entityDecorator.getIndexFields()) { + List names = indexField.getFieldNames(); + List entityFields = new ArrayList<>(); + + for (String name : names) { + Field field = reflector.getField(entityDecorator.getEntityType(), name); + if (field != null) { + entityFields.add(field); + indexValidator.validate(field.getType(), field.getName(), nitriteMapper); + } + } + + if (entityFields.size() == names.size()) { + // validation for all field are success + indices.add(indexField); + } + } + } + } + + private void readIdField() { + if (entityDecorator != null) { + EntityId entityId = entityDecorator.getIdField(); + if (entityId != null) { + String idFieldName = entityId.getFieldName(); + if (!StringUtils.isNullOrEmpty(idFieldName)) { + Field field = reflector.getField(entityDecorator.getEntityType(), idFieldName); + indexValidator.validateId(entityId, field.getType(), idFieldName, nitriteMapper); + + objectIdField = new ObjectIdField(); + objectIdField.setField(field); + objectIdField.setIdFieldName(idFieldName); + objectIdField.setEmbedded(entityId.isEmbedded()); + objectIdField.setFieldNames(entityId.getSubFields()); + } + } + } + } +} diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/EntityId.java b/nitrite/src/main/java/org/dizitart/no2/repository/EntityId.java new file mode 100644 index 000000000..99921a6f9 --- /dev/null +++ b/nitrite/src/main/java/org/dizitart/no2/repository/EntityId.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.repository; + +import lombok.Getter; +import org.dizitart.no2.NitriteConfig; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.exceptions.ObjectMappingException; +import org.dizitart.no2.filters.Filter; +import org.dizitart.no2.filters.NitriteFilter; + +import java.util.ArrayList; +import java.util.List; + +import static org.dizitart.no2.filters.FluentFilter.where; + +public class EntityId { + @Getter + private String fieldName; + + @Getter + private String[] subFields; + + private List embeddedFieldNames; + + public EntityId(String fieldName, String... subFields) { + this.fieldName = fieldName; + this.subFields = subFields; + } + + public List getEmbeddedFieldNames() { + if (embeddedFieldNames != null) return embeddedFieldNames; + embeddedFieldNames = new ArrayList<>(); + + if (subFields != null) { + for (String subField : subFields) { + embeddedFieldNames.add(fieldName + NitriteConfig.getFieldSeparator() + subField); + } + } + return embeddedFieldNames; + } + + public boolean isEmbedded() { + return subFields != null && subFields.length != 0; + } + + public Filter createUniqueFilter(Object value, NitriteMapper nitriteMapper) { + if (isEmbedded()) { + Document document = nitriteMapper.convert(value, Document.class); + if (document == null) { + throw new ObjectMappingException("Failed to map object to document"); + } + + List filters = new ArrayList<>(); + for (String subField : subFields) { + String filterField = fieldName + NitriteConfig.getFieldSeparator() + subField; + Object fieldValue = document.get(subField); + filters.add(where(filterField).eq(fieldValue)); + } + + NitriteFilter nitriteFilter = (NitriteFilter) Filter.and(filters.toArray(new Filter[] {})); + nitriteFilter.setObjectFilter(true); + return nitriteFilter; + } else { + return where(fieldName).eq(value); + } + } +} diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java b/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java index 521014623..a4969e5e4 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java @@ -19,6 +19,7 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.util.ObjectUtils; import org.dizitart.no2.exceptions.IndexingException; import org.dizitart.no2.repository.annotations.Id; @@ -28,11 +29,6 @@ * @author Anindya Chatterjee */ public class IndexValidator { - private final Reflector reflector; - - public IndexValidator(Reflector reflector) { - this.reflector = reflector; - } /** * Validate an index field of an {@link org.dizitart.no2.repository.annotations.Entity} object. @@ -45,7 +41,7 @@ public void validate(Class fieldType, String field, NitriteMapper nitriteMapp if (fieldType.isPrimitive() || fieldType == NitriteId.class || fieldType.isInterface() - || nitriteMapper.isValueType(fieldType) + || ObjectUtils.isValueType(fieldType, nitriteMapper) || Modifier.isAbstract(fieldType.getModifiers()) || fieldType.isArray() || Iterable.class.isAssignableFrom(fieldType)) { @@ -62,7 +58,7 @@ public void validateId(Id id, Class fieldType, String field, NitriteMapper ni if (fieldType.isPrimitive() || fieldType == NitriteId.class || fieldType.isInterface() - || nitriteMapper.isValueType(fieldType) + || ObjectUtils.isValueType(fieldType, nitriteMapper) || Modifier.isAbstract(fieldType.getModifiers()) || fieldType.isArray() || Iterable.class.isAssignableFrom(fieldType)) { @@ -74,4 +70,21 @@ public void validateId(Id id, Class fieldType, String field, NitriteMapper ni throw new IndexingException("Invalid Id field " + field); } } + + public void validateId(EntityId entityId, Class fieldType, String field, NitriteMapper nitriteMapper) { + if (fieldType.isPrimitive() + || fieldType == NitriteId.class + || fieldType.isInterface() + || ObjectUtils.isValueType(fieldType, nitriteMapper) + || Modifier.isAbstract(fieldType.getModifiers()) + || fieldType.isArray() + || Iterable.class.isAssignableFrom(fieldType)) { + // we will validate the solid class during insertion/update + return; + } + + if (entityId.getEmbeddedFieldNames().size() == 0) { + throw new IndexingException("Invalid Id field " + field); + } + } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java index 174aa3e28..92ae5f53b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java @@ -22,6 +22,7 @@ import org.dizitart.no2.common.Lookup; import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.common.streams.MutatedObjectStream; +import org.dizitart.no2.common.util.ObjectUtils; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.common.mapper.NitriteMapper; @@ -83,8 +84,8 @@ private Document emptyDocument(NitriteMapper nitriteMapper, Class type) { throw new ValidationException("Cannot project to array"); } else if (Modifier.isAbstract(type.getModifiers())) { throw new ValidationException("Cannot project to abstract type"); - } else if (nitriteMapper.isValueType(type)) { - throw new ValidationException("Cannot to project to nitrite mapper's value type"); + } else if (ObjectUtils.isValueType(type, nitriteMapper)) { + throw new ValidationException("Cannot project to a value type"); } Document dummyDoc = skeletonDocument(nitriteMapper, type); diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java index 86693a4d5..e56a59152 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java @@ -19,6 +19,7 @@ import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.CollectionFactory; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.util.ObjectUtils; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ValidationException; @@ -31,6 +32,7 @@ import java.util.concurrent.locks.ReentrantLock; import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryName; +import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryNameByDecorator; /** * The {@link ObjectRepository} factory. @@ -105,6 +107,41 @@ public ObjectRepository getRepository(NitriteConfig nitriteConfig, Class< } } + + public ObjectRepository getRepository(NitriteConfig nitriteConfig, EntityDecorator entityDecorator) { + return getRepository(nitriteConfig, entityDecorator, null); + } + + @SuppressWarnings("unchecked") + public ObjectRepository getRepository(NitriteConfig nitriteConfig, EntityDecorator entityDecorator, String key) { + if (entityDecorator == null) { + throw new ValidationException("entityDecorator cannot be null"); + } + + if (nitriteConfig == null) { + throw new ValidationException("nitriteConfig cannot be null"); + } + + String collectionName = findRepositoryNameByDecorator(entityDecorator, key); + + try { + lock.lock(); + if (repositoryMap.containsKey(collectionName)) { + ObjectRepository repository = (ObjectRepository) repositoryMap.get(collectionName); + if (repository.isDropped() || !repository.isOpen()) { + repositoryMap.remove(collectionName); + return createRepositoryByDecorator(nitriteConfig, entityDecorator, collectionName, key); + } else { + return repository; + } + } else { + return createRepositoryByDecorator(nitriteConfig, entityDecorator, collectionName, key); + } + } finally { + lock.unlock(); + } + } + /** * Closes all opened {@link ObjectRepository}s and clear internal data from this class. */ @@ -126,7 +163,7 @@ private ObjectRepository createRepository(NitriteConfig nitriteConfig, Cl String collectionName, String key) { NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); NitriteStore store = nitriteConfig.getNitriteStore(); - if (nitriteMapper.isValueType(type)) { + if (ObjectUtils.isValueType(type, nitriteMapper)) { throw new ValidationException("Cannot create a repository for a value type"); } @@ -143,6 +180,30 @@ private ObjectRepository createRepository(NitriteConfig nitriteConfig, Cl return repository; } + private ObjectRepository createRepositoryByDecorator(NitriteConfig nitriteConfig, + EntityDecorator entityDecorator, + String collectionName, String key) { + NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); + NitriteStore store = nitriteConfig.getNitriteStore(); + + if (store.getCollectionNames().contains(collectionName)) { + throw new ValidationException("A collection with same entity name already exists"); + } + + if (ObjectUtils.isValueType(entityDecorator.getEntityType(), nitriteMapper)) { + throw new ValidationException("Cannot create a repository for a value type"); + } + + NitriteCollection nitriteCollection = collectionFactory.getCollection(collectionName, + nitriteConfig, false); + + ObjectRepository repository = new DefaultObjectRepository<>(entityDecorator, nitriteCollection, nitriteConfig); + repositoryMap.put(collectionName, repository); + + writeCatalog(store, collectionName, key); + return repository; + } + private void writeCatalog(NitriteStore store, String name, String key) { StoreCatalog storeCatalog = store.getCatalog(); if (StringUtils.isNullOrEmpty(key)) { diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java index 3b597f076..9cb9798d8 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java @@ -43,10 +43,11 @@ */ public class RepositoryOperations { private final NitriteMapper nitriteMapper; - private final Class type; private final NitriteCollection collection; - private final AnnotationScanner annotationScanner; + private final Class type; + private AnnotationScanner annotationScanner; private ObjectIdField objectIdField; + private EntityDecoratorReader entityDecoratorReader; /** * Instantiates a new {@link RepositoryOperations}. @@ -56,8 +57,8 @@ public class RepositoryOperations { * @param collection the collection */ public RepositoryOperations(Class type, - NitriteMapper nitriteMapper, - NitriteCollection collection) { + NitriteMapper nitriteMapper, + NitriteCollection collection) { this.type = type; this.nitriteMapper = nitriteMapper; this.collection = collection; @@ -65,14 +66,31 @@ public RepositoryOperations(Class type, validateCollection(); } + public RepositoryOperations(EntityDecorator entityDecorator, + NitriteMapper nitriteMapper, + NitriteCollection collection) { + this.type = entityDecorator.getEntityType(); + this.nitriteMapper = nitriteMapper; + this.collection = collection; + this.entityDecoratorReader = new EntityDecoratorReader(entityDecorator, collection, nitriteMapper); + validateCollection(); + } + /** * Create indices. */ public void createIndices() { - annotationScanner.performScan(); - annotationScanner.createIndices(); - annotationScanner.createIdIndex(); - objectIdField = annotationScanner.getObjectIdField(); + if (annotationScanner != null) { + annotationScanner.performScan(); + annotationScanner.createIndices(); + annotationScanner.createIdIndex(); + objectIdField = annotationScanner.getObjectIdField(); + } else if (entityDecoratorReader != null) { + entityDecoratorReader.readEntity(); + entityDecoratorReader.createIndices(); + entityDecoratorReader.createIdIndex(); + objectIdField = entityDecoratorReader.getObjectIdField(); + } } /** @@ -100,7 +118,8 @@ public void serializeFields(Document document) { * @return the document [ ] */ public Document[] toDocuments(T[] others) { - if (others == null || others.length == 0) return null; + if (others == null || others.length == 0) + return null; Document[] documents = new Document[others.length]; for (int i = 0; i < others.length; i++) { documents[i] = toDocument(others[i], false); // this method is for insert only @@ -133,7 +152,8 @@ public Document toDocument(T object, boolean update) { idField.set(object, id); document.put(objectIdField.getIdFieldName(), nitriteMapper.convert(id, Comparable.class)); } else if (!update) { - // if it is an insert, then we should not allow to insert the document with user provided id + // if it is an insert, then we should not allow to insert the document with user + // provided id throw new InvalidIdException("Auto generated id should not be set manually"); } } catch (IllegalAccessException iae) { @@ -186,7 +206,7 @@ public void removeNitriteId(Document document) { if (objectIdField != null) { Field idField = objectIdField.getField(); if (idField != null && !objectIdField.isEmbedded() - && idField.getType() == NitriteId.class) { + && idField.getType() == NitriteId.class) { document.remove(idField.getName()); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java b/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java index fc7e24e1f..d8b0a8fd3 100644 --- a/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java +++ b/nitrite/src/main/java/org/dizitart/no2/store/StoreCatalog.java @@ -59,11 +59,11 @@ public void writeCollectionEntry(String name) { document = Document.createDocument(); } - // parse the document to create collection meta data object + // parse the document to create collection metadata object MapMetaData metaData = new MapMetaData(document); metaData.getMapNames().add(name); - // convert the meta data object to document and save + // convert the metadata object to document and save catalogMap.put(TAG_COLLECTIONS, metaData.getInfo()); } @@ -78,11 +78,11 @@ public void writeRepositoryEntry(String name) { document = Document.createDocument(); } - // parse the document to create collection meta data object + // parse the document to create collection metadata object MapMetaData metaData = new MapMetaData(document); metaData.getMapNames().add(name); - // convert the meta data object to document and save + // convert the metadata object to document and save catalogMap.put(TAG_REPOSITORIES, metaData.getInfo()); } @@ -97,7 +97,7 @@ public void writeKeyedRepositoryEntry(String name) { document = Document.createDocument(); } - // parse the document to create collection meta data object + // parse the document to create collection metadata object MapMetaData metaData = new MapMetaData(document); metaData.getMapNames().add(name); diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java index fd959067d..c2fe7af27 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalCollection.java @@ -394,7 +394,7 @@ public void dropIndex(String... fieldNames) { journalEntry.setChangeType(ChangeType.DropIndex); journalEntry.setCommit(() -> { for (IndexDescriptor entry : primary.listIndices()) { - if (entry.getIndexFields().equals(fields)) { + if (entry.getFields().equals(fields)) { indexEntry.set(entry); break; } @@ -429,7 +429,7 @@ public void dropAllIndices() { }); journalEntry.setRollback(() -> { for (IndexDescriptor indexDescriptor : indexEntries) { - String[] fieldNames = indexDescriptor.getIndexFields().getFieldNames().toArray(new String[0]); + String[] fieldNames = indexDescriptor.getFields().getFieldNames().toArray(new String[0]); primary.createIndex(indexOptions(indexDescriptor.getIndexType()), fieldNames); } }); @@ -624,9 +624,9 @@ private void closeEventBus() { private void validateRebuildIndex(IndexDescriptor indexDescriptor) { notNull(indexDescriptor, "indexDescriptor cannot be null"); - String[] fieldNames = indexDescriptor.getIndexFields().getFieldNames().toArray(new String[0]); + String[] fieldNames = indexDescriptor.getFields().getFieldNames().toArray(new String[0]); if (isIndexing(fieldNames)) { - throw new IndexingException("Indexing on fields " + indexDescriptor.getIndexFields() + " is currently running"); + throw new IndexingException("Indexing on fields " + indexDescriptor.getFields() + " is currently running"); } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java index fe81102a0..d2de7972a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/DefaultTransactionalRepository.java @@ -6,16 +6,17 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.collection.UpdateOptions; import org.dizitart.no2.collection.events.CollectionEventListener; -import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.common.WriteResult; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.meta.Attributes; +import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexDescriptor; import org.dizitart.no2.index.IndexOptions; -import org.dizitart.no2.common.mapper.NitriteMapper; -import org.dizitart.no2.common.processors.Processor; import org.dizitart.no2.repository.Cursor; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.RepositoryOperations; +import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.store.NitriteStore; import java.util.Collection; @@ -29,11 +30,12 @@ * @since 4.0 */ class DefaultTransactionalRepository implements ObjectRepository { - private final Class type; private final ObjectRepository primary; private final NitriteCollection backingCollection; private final NitriteConfig nitriteConfig; private RepositoryOperations operations; + private Class type; + private EntityDecorator entityDecorator; public DefaultTransactionalRepository(Class type, ObjectRepository primary, @@ -46,6 +48,17 @@ public DefaultTransactionalRepository(Class type, initialize(); } + public DefaultTransactionalRepository(EntityDecorator entityDecorator, + ObjectRepository primary, + NitriteCollection backingCollection, + TransactionConfig nitriteConfig) { + this.entityDecorator = entityDecorator; + this.primary = primary; + this.backingCollection = backingCollection; + this.nitriteConfig = nitriteConfig; + initialize(); + } + @Override public void addProcessor(Processor processor) { backingCollection.addProcessor(processor); @@ -206,7 +219,11 @@ public void setAttributes(Attributes attributes) { @Override public Class getType() { - return type; + if (entityDecorator != null) { + return entityDecorator.getEntityType(); + } else { + return type; + } } @Override @@ -216,7 +233,11 @@ public NitriteCollection getDocumentCollection() { private void initialize() { NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); - this.operations = new RepositoryOperations(type, nitriteMapper, backingCollection); + if (entityDecorator != null) { + this.operations = new RepositoryOperations(entityDecorator, nitriteMapper, backingCollection); + } else { + this.operations = new RepositoryOperations(type, nitriteMapper, backingCollection); + } this.operations.createIndices(); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java index 1421ae91a..ae3d8e94a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/NitriteTransaction.java @@ -11,6 +11,7 @@ import org.dizitart.no2.common.module.NitriteModule; import org.dizitart.no2.exceptions.TransactionException; import org.dizitart.no2.repository.ObjectRepository; +import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.store.NitriteMap; import org.dizitart.no2.store.NitriteStore; @@ -19,6 +20,7 @@ import java.util.concurrent.locks.Lock; import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryName; +import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryNameByDecorator; /** * @author Anindya Chatterjee @@ -149,6 +151,78 @@ public synchronized ObjectRepository getRepository(Class type, String return txRepository; } + @Override + @SuppressWarnings("unchecked") + public synchronized ObjectRepository getRepository(EntityDecorator entityDecorator) { + checkState(); + + String name = findRepositoryNameByDecorator(entityDecorator, null); + if (repositoryRegistry.containsKey(name)) { + return (ObjectRepository) repositoryRegistry.get(name); + } + + ObjectRepository primary; + if (nitrite.hasRepository(entityDecorator)) { + primary = nitrite.getRepository(entityDecorator); + } else { + throw new TransactionException("Repository of type " + entityDecorator.getEntityName() + " does not exists"); + } + + NitriteMap txMap = transactionStore.openMap(name, + NitriteId.class, Document.class); + + TransactionContext context = new TransactionContext(); + context.setCollectionName(name); + context.setNitriteMap(txMap); + context.setJournal(new LinkedList<>()); + context.setConfig(transactionConfig); + + NitriteCollection primaryCollection = primary.getDocumentCollection(); + NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context); + ObjectRepository txRepository = new DefaultTransactionalRepository<>(entityDecorator, + primary, backingCollection, transactionConfig); + + repositoryRegistry.put(name, txRepository); + contextMap.put(name, context); + return txRepository; + } + + @Override + @SuppressWarnings("unchecked") + public synchronized ObjectRepository getRepository(EntityDecorator entityDecorator, String key) { + checkState(); + + String name = findRepositoryNameByDecorator(entityDecorator, key); + if (repositoryRegistry.containsKey(name)) { + return (ObjectRepository) repositoryRegistry.get(name); + } + + ObjectRepository primary; + if (nitrite.hasRepository(entityDecorator, key)) { + primary = nitrite.getRepository(entityDecorator, key); + } else { + throw new TransactionException("Repository of type " + entityDecorator.getEntityName() + + " and key " + key + " does not exists"); + } + + NitriteMap txMap = transactionStore.openMap(name, + NitriteId.class, Document.class); + + TransactionContext context = new TransactionContext(); + context.setCollectionName(name); + context.setNitriteMap(txMap); + context.setJournal(new LinkedList<>()); + context.setConfig(transactionConfig); + + NitriteCollection primaryCollection = primary.getDocumentCollection(); + NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context); + ObjectRepository txRepository = new DefaultTransactionalRepository<>(entityDecorator, + primary, backingCollection, transactionConfig); + repositoryRegistry.put(name, txRepository); + contextMap.put(name, context); + return txRepository; + } + @Override public synchronized void commit() { checkState(); diff --git a/nitrite/src/main/java/org/dizitart/no2/transaction/Transaction.java b/nitrite/src/main/java/org/dizitart/no2/transaction/Transaction.java index d09ff2bd1..ffc156a72 100644 --- a/nitrite/src/main/java/org/dizitart/no2/transaction/Transaction.java +++ b/nitrite/src/main/java/org/dizitart/no2/transaction/Transaction.java @@ -2,6 +2,7 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.repository.ObjectRepository; +import org.dizitart.no2.repository.EntityDecorator; /** * Represents an ACID transaction on nitrite database. @@ -51,6 +52,26 @@ public interface Transaction extends AutoCloseable { */ ObjectRepository getRepository(Class type, String key); + + /** + * Gets an {@link ObjectRepository} to perform ACID operations on it. + * + * @param the type parameter + * @param entityDecorator the entityDecorator + * @return the repository + */ + ObjectRepository getRepository(EntityDecorator entityDecorator); + + /** + * Gets an {@link ObjectRepository} to perform ACID operations on it. + * + * @param the type parameter + * @param entityDecorator the entityDecorator + * @param key the key + * @return the repository + */ + ObjectRepository getRepository(EntityDecorator entityDecorator, String key); + /** * Completes the transaction and commits the data to the underlying store. */ diff --git a/nitrite/src/test/java/org/dizitart/no2/NitriteBuilderTest.java b/nitrite/src/test/java/org/dizitart/no2/NitriteBuilderTest.java index 2eaec3571..b9da9bf68 100644 --- a/nitrite/src/test/java/org/dizitart/no2/NitriteBuilderTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/NitriteBuilderTest.java @@ -24,6 +24,7 @@ import org.dizitart.no2.common.FieldValues; import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.common.module.NitriteModule; import org.dizitart.no2.common.module.NitritePlugin; import org.dizitart.no2.common.module.PluginManager; @@ -138,7 +139,7 @@ public void testOpenOrCreate() { assertFalse(actualOpenOrCreateResult.isClosed()); assertFalse(store.isClosed()); assertSame(store, pluginManager.getNitriteStore()); - assertTrue(pluginManager.getNitriteMapper() instanceof org.dizitart.no2.common.mapper.MappableMapper); + assertTrue(pluginManager.getNitriteMapper() instanceof SimpleDocumentMapper); } @Test @@ -152,7 +153,7 @@ public void testOpenOrCreate2() { assertFalse(store.isClosed()); PluginManager pluginManager = config.getPluginManager(); assertEquals(3, pluginManager.getIndexerMap().size()); - assertTrue(pluginManager.getNitriteMapper() instanceof org.dizitart.no2.common.mapper.MappableMapper); + assertTrue(pluginManager.getNitriteMapper() instanceof SimpleDocumentMapper); assertTrue(store.getCatalog().getKeyedRepositoryNames().isEmpty()); assertSame(store, pluginManager.getNitriteStore()); assertTrue(((InMemoryConfig) store.getStoreConfig()).eventListeners().isEmpty()); @@ -169,7 +170,7 @@ public void testOpenOrCreate3() { assertFalse(store.isClosed()); PluginManager pluginManager = config.getPluginManager(); assertEquals(3, pluginManager.getIndexerMap().size()); - assertTrue(pluginManager.getNitriteMapper() instanceof org.dizitart.no2.common.mapper.MappableMapper); + assertTrue(pluginManager.getNitriteMapper() instanceof SimpleDocumentMapper); assertTrue(store.getCatalog().getKeyedRepositoryNames().isEmpty()); assertSame(store, pluginManager.getNitriteStore()); assertTrue(((InMemoryConfig) store.getStoreConfig()).eventListeners().isEmpty()); @@ -184,7 +185,7 @@ public void testOpenOrCreate4() { assertEquals(3, pluginManager.getIndexerMap().size()); NitriteStore nitriteStore = nitriteConfig.getNitriteStore(); assertSame(nitriteStore, pluginManager.getNitriteStore()); - assertTrue(pluginManager.getNitriteMapper() instanceof org.dizitart.no2.common.mapper.MappableMapper); + assertTrue(pluginManager.getNitriteMapper() instanceof SimpleDocumentMapper); assertFalse(nitriteStore.isClosed()); assertTrue(((InMemoryConfig) nitriteStore.getStoreConfig()).eventListeners().isEmpty()); } @@ -198,7 +199,7 @@ public void testOpenOrCreate5() { NitriteStore nitriteStore = nitriteConfig.getNitriteStore(); assertFalse(nitriteStore.isClosed()); assertSame(nitriteStore, pluginManager.getNitriteStore()); - assertTrue(pluginManager.getNitriteMapper() instanceof org.dizitart.no2.common.mapper.MappableMapper); + assertTrue(pluginManager.getNitriteMapper() instanceof SimpleDocumentMapper); } @Test @@ -312,16 +313,6 @@ public Target convert(Source source, Class type) { return null; } - @Override - public boolean isValueType(Class type) { - return false; - } - - @Override - public boolean isValue(Object object) { - return false; - } - @Override public void initialize(NitriteConfig nitriteConfig) { diff --git a/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java b/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java index deeea8545..eb8bc3d55 100644 --- a/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/NitriteConfigTest.java @@ -17,6 +17,7 @@ package org.dizitart.no2; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.common.module.NitriteModule; import org.dizitart.no2.common.module.NitritePlugin; import org.dizitart.no2.common.module.PluginManager; @@ -114,7 +115,7 @@ public void testAutoConfigure() { assertEquals(3, pluginManager.getIndexerMap().size()); NitriteStore nitriteStore = nitriteConfig.getNitriteStore(); assertSame(nitriteStore, pluginManager.getNitriteStore()); - assertTrue(pluginManager.getNitriteMapper() instanceof org.dizitart.no2.common.mapper.MappableMapper); + assertTrue(pluginManager.getNitriteMapper() instanceof SimpleDocumentMapper); assertFalse(nitriteStore.isClosed()); assertTrue(((InMemoryConfig) nitriteStore.getStoreConfig()).eventListeners().isEmpty()); } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/event/EventTest.java b/nitrite/src/test/java/org/dizitart/no2/common/event/EventTest.java index 1213cf3dc..cd274e7c1 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/event/EventTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/event/EventTest.java @@ -19,6 +19,7 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; import org.dizitart.no2.collection.UpdateOptions; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.collection.events.EventType; import org.dizitart.no2.repository.ObjectRepository; @@ -75,6 +76,9 @@ public void setUp() { db = nitriteBuilder.openOrCreate(); } + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + employeeRepository = db.getRepository(Employee.class); listener = new SampleListenerCollection(); employeeRepository.subscribe(listener); diff --git a/nitrite/src/test/java/org/dizitart/no2/common/mapper/Company.java b/nitrite/src/test/java/org/dizitart/no2/common/mapper/Company.java index ad947db76..3e99586d3 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/mapper/Company.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/mapper/Company.java @@ -25,27 +25,13 @@ * @author Anindya Chatterjee. */ @Data -public class Company implements Mappable { +public class Company { private String name; private Long id; private CompanyId companyId; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("name", name) - .put("companyId", mapper.convert(companyId, Document.class)); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - id = document.get("id", Long.class); - companyId = document.get("companyId", CompanyId.class); - } - @Data - public static class CompanyId implements Comparable, Serializable, Mappable { + public static class CompanyId implements Comparable, Serializable { private Long idValue; public CompanyId(long value) { @@ -57,14 +43,47 @@ public int compareTo(CompanyId other) { return idValue.compareTo(other.idValue); } + public static class CompanyIdConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return CompanyId.class; + } + + @Override + public Document toDocument(CompanyId entity, NitriteMapper nitriteMapper) { + return Document.createDocument().put("idValue", entity.idValue); + } + + @Override + public CompanyId fromDocument(Document document, NitriteMapper nitriteMapper) { + CompanyId entity = new CompanyId(0L); + entity.idValue = document.get("idValue", Long.class); + return entity; + } + } + } + + public static class CompanyConverter implements EntityConverter { + @Override + public Class getEntityType() { + return Company.class; + } + @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("idValue", idValue); + public Document toDocument(Company entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("name", entity.name) + .put("companyId", nitriteMapper.convert(entity.companyId, Document.class)); } @Override - public void read(NitriteMapper mapper, Document document) { - idValue = document.get("idValue", Long.class); + public Company fromDocument(Document document, NitriteMapper nitriteMapper) { + Company entity = new Company(); + entity.name = document.get("name", String.class); + entity.id = document.get("id", Long.class); + entity.companyId = nitriteMapper.convert(document.get("companyId", Document.class), CompanyId.class); + return entity; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/mapper/Department.java b/nitrite/src/test/java/org/dizitart/no2/common/mapper/Department.java index 5d1a5eb59..5c83a37fe 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/mapper/Department.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/mapper/Department.java @@ -28,32 +28,42 @@ */ @Data @ToString -public class Department implements Mappable { +public class Department { private String name; private List employeeList; - @Override - public Document write(NitriteMapper mapper) { - List docList = new ArrayList<>(); - if (employeeList != null && !employeeList.isEmpty()) { - employeeList.stream().map(employee -> mapper.convert(employee, Document.class)) - .forEach(docList::add); + public static class DepartmentConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Department.class; } - return Document.createDocument().put("name", name) - .put("employeeList", docList); - } + @Override + public Document toDocument(Department entity, NitriteMapper nitriteMapper) { + List docList = new ArrayList<>(); + if (entity.employeeList != null && !entity.employeeList.isEmpty()) { + entity.employeeList.stream().map(employee -> nitriteMapper.convert(employee, Document.class)) + .forEach(docList::add); + } + + return Document.createDocument().put("name", entity.name) + .put("employeeList", docList); + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - employeeList = new ArrayList<>(); - List documentList = (List) document.get("employeeList", ArrayList.class); - if (documentList != null && !documentList.isEmpty()) { - documentList.stream().map(doc -> mapper.convert(doc, MappableEmployee.class)) - .forEach(employeeList::add); + @Override + @SuppressWarnings("unchecked") + public Department fromDocument(Document document, NitriteMapper nitriteMapper) { + Department entity = new Department(); + entity.employeeList = new ArrayList<>(); + List documentList = (List) document.get("employeeList", ArrayList.class); + if (documentList != null && !documentList.isEmpty()) { + documentList.stream().map(doc -> nitriteMapper.convert(doc, MappableEmployee.class)) + .forEach(entity.employeeList::add); + } + entity.name = document.get("name", String.class); + return entity; } - name = document.get("name", String.class); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/mapper/Employee.java b/nitrite/src/test/java/org/dizitart/no2/common/mapper/Employee.java index 057a0f0cb..3d76fb41f 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/mapper/Employee.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/mapper/Employee.java @@ -27,34 +27,44 @@ */ @Data @ToString -public class Employee implements Mappable { +public class Employee { private String empId; private String name; private Date joiningDate; private Employee boss; - @Override - public Document write(NitriteMapper mapper) { - Document document = Document.createDocument("empId", getEmpId()) - .put("name", getName()) - .put("joiningDate", getJoiningDate()); + public static class Converter implements EntityConverter { - if (getBoss() != null) { - document.put("boss", mapper.convert(getBoss(), Document.class)); + @Override + public Class getEntityType() { + return Employee.class; + } + + @Override + public Document toDocument(Employee entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument("empId", entity.getEmpId()) + .put("name", entity.getName()) + .put("joiningDate", entity.getJoiningDate()); + + if (entity.getBoss() != null) { + document.put("boss", nitriteMapper.convert(entity.getBoss(), Document.class)); + } + return document; } - return document; - } - @Override - public void read(NitriteMapper mapper, Document document) { - setEmpId(document.get("empId", String.class)); - setName(document.get("name", String.class)); - setJoiningDate(document.get("joiningDate", Date.class)); + @Override + public Employee fromDocument(Document document, NitriteMapper nitriteMapper) { + Employee entity = new Employee(); + entity.setEmpId(document.get("empId", String.class)); + entity.setName(document.get("name", String.class)); + entity.setJoiningDate(document.get("joiningDate", Date.class)); - Document bossDoc = document.get("boss", Document.class); - if (bossDoc != null) { - Employee boss = mapper.convert(bossDoc, Employee.class); - setBoss(boss); + Document bossDoc = document.get("boss", Document.class); + if (bossDoc != null) { + Employee boss = nitriteMapper.convert(bossDoc, Employee.class); + entity.setBoss(boss); + } + return entity; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableDepartment.java b/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableDepartment.java index b61548d0f..31dd66714 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableDepartment.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableDepartment.java @@ -28,41 +28,50 @@ */ @Data @ToString -public class MappableDepartment implements Mappable { +public class MappableDepartment { private String name; private List employeeList; - @Override - public Document write(NitriteMapper mapper) { - Document document = Document.createDocument(); - document.put("name", getName()); - List employees = new ArrayList<>(); - for (MappableEmployee employee : getEmployeeList()) { - employees.add(employee.write(mapper)); + private List getEmployeeList() { + if (employeeList == null) { + employeeList = new ArrayList<>(); } - document.put("employeeList", employees); - - return document; + return employeeList; } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - setName((String) document.get("name")); - for (Document doc : (List) document.get("employeeList")) { - MappableEmployee me = new MappableEmployee(); - me.read(mapper, doc); - getEmployeeList().add(me); + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return MappableDepartment.class; + } + + @Override + public Document toDocument(MappableDepartment entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + + document.put("name", entity.getName()); + List employees = new ArrayList<>(); + for (MappableEmployee employee : entity.getEmployeeList()) { + employees.add(nitriteMapper.convert(employee, Document.class)); } + document.put("employeeList", employees); + + return document; } - } - private List getEmployeeList() { - if (employeeList == null) { - employeeList = new ArrayList<>(); + @Override + public MappableDepartment fromDocument(Document document, NitriteMapper nitriteMapper) { + MappableDepartment entity = new MappableDepartment(); + if (document != null) { + entity.setName((String) document.get("name")); + for (Document doc : (List) document.get("employeeList")) { + MappableEmployee me = nitriteMapper.convert(doc, MappableEmployee.class); + entity.getEmployeeList().add(me); + } + } + return entity; } - return employeeList; } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableEmployee.java b/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableEmployee.java index a2d7f79a1..dd0d01e90 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableEmployee.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableEmployee.java @@ -27,39 +27,50 @@ */ @Data @ToString -public class MappableEmployee implements Mappable { +public class MappableEmployee { private String empId; private String name; private Date joiningDate; private MappableEmployee boss; - @Override - public Document write(NitriteMapper mapper) { - Document document = Document.createDocument(); - document.put("empId", getEmpId()); - document.put("name", getName()); - document.put("joiningDate", getJoiningDate()); + public static class MappableEmployeeConverter implements EntityConverter { - if (getBoss() != null) { - Document bossDoc = getBoss().write(mapper); - document.put("boss", bossDoc); + @Override + public Class getEntityType() { + return MappableEmployee.class; } - return document; - } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - setEmpId((String) document.get("empId")); - setName((String) document.get("name")); - setJoiningDate((Date) document.get("joiningDate")); + @Override + public Document toDocument(MappableEmployee entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + document.put("empId", entity.getEmpId()); + document.put("name", entity.getName()); + document.put("joiningDate", entity.getJoiningDate()); - Document bossDoc = (Document) document.get("boss"); - if (bossDoc != null) { - MappableEmployee bossEmp = new MappableEmployee(); - bossEmp.read(mapper, bossDoc); - setBoss(bossEmp); + if (entity.getBoss() != null) { + Document bossDoc = nitriteMapper.convert(entity.getBoss(), Document.class); + document.put("boss", bossDoc); } + return document; + } + + @Override + public MappableEmployee fromDocument(Document document, NitriteMapper nitriteMapper) { + MappableEmployee entity = new MappableEmployee(); + + if (document != null) { + entity.setEmpId((String) document.get("empId")); + entity.setName((String) document.get("name")); + entity.setJoiningDate((Date) document.get("joiningDate")); + + Document bossDoc = (Document) document.get("boss"); + if (bossDoc != null) { + MappableEmployee bossEmp = nitriteMapper.convert(bossDoc, MappableEmployee.class); + entity.setBoss(bossEmp); + } + } + + return entity; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableMapperTest.java b/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableMapperTest.java deleted file mode 100644 index 1e64953db..000000000 --- a/nitrite/src/test/java/org/dizitart/no2/common/mapper/MappableMapperTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.dizitart.no2.common.mapper; - -import org.dizitart.no2.integration.NitriteStressTest; -import org.dizitart.no2.integration.NitriteTest; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.exceptions.ObjectMappingException; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class MappableMapperTest { - @Test - public void testConvert() { - MappableMapper mappableMapper = new MappableMapper(Integer.class); - assertEquals(0, ((Integer) mappableMapper.convert(0, Object.class)).intValue()); - } - - @Test - public void testConvert2() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - MappableMapper mappableMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); - assertEquals("source", mappableMapper.convert("source", Object.class)); - } - - @Test - public void testConvert3() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - MappableMapper mappableMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); - assertEquals(0, ((Integer) mappableMapper.convert(0, Object.class)).intValue()); - } - - @Test - public void testConvertFromDocument() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - MappableMapper mappableMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); - assertNull(mappableMapper.convertFromDocument(null, Object.class)); - } - - @Test - public void testConvertFromDocument2() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - MappableMapper mappableMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); - Class type = Object.class; - assertThrows(ObjectMappingException.class, - () -> mappableMapper.convertFromDocument(Document.createDocument(), type)); - } - - @Test - public void testConvertFromDocument3() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - assertNull( - (new MappableMapper(forNameResult, forNameResult1, Object.class)).convertFromDocument(null, null)); - } - - @Test - public void testConvertToDocument() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - assertThrows(ObjectMappingException.class, - () -> (new MappableMapper(forNameResult, forNameResult1, Object.class)).convertToDocument("source")); - } - - @Test - public void testConvertToDocument2() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - MappableMapper mappableMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); - assertEquals(2, mappableMapper.convertToDocument(new NitriteTest.CompatChild()).size()); - } - - @Test - public void testConvertToDocument3() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - MappableMapper mappableMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); - assertEquals(7, mappableMapper.convertToDocument(new NitriteStressTest.TestDto()).size()); - } - - @Test - public void testConvertToDocument4() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - MappableMapper mappableMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); - assertEquals(3, mappableMapper.convertToDocument(new Company()).size()); - } - - @Test - public void testIsValueType() { - MappableMapper mappableMapper = new MappableMapper(); - assertFalse(mappableMapper.isValueType(Object.class)); - } - - @Test - public void testIsValueType2() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - MappableMapper mappableMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); - assertTrue(mappableMapper.isValueType(Object.class)); - } - - @Test - public void testIsValue() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - assertTrue((new MappableMapper(forNameResult, forNameResult1, Object.class)).isValue("object")); - } -} - diff --git a/nitrite/src/test/java/org/dizitart/no2/common/mapper/MapperTest.java b/nitrite/src/test/java/org/dizitart/no2/common/mapper/MapperTest.java index e8eb86a86..a7be25eda 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/mapper/MapperTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/mapper/MapperTest.java @@ -31,14 +31,15 @@ * @author Anindya Chatterjee */ public class MapperTest { - private MappableMapper mappableMapper; + private SimpleDocumentMapper simpleDocumentMapper; @Rule public Retry retry = new Retry(3); @Test public void testWithConverter() { - mappableMapper = new MappableMapper(); + simpleDocumentMapper = new SimpleDocumentMapper(); + simpleDocumentMapper.registerEntityConverter(new Employee.Converter()); Employee boss = new Employee(); boss.setEmpId("1"); @@ -52,12 +53,12 @@ public void testWithConverter() { emp1.setBoss(boss); long start = System.currentTimeMillis(); - Document document = mappableMapper.convert(emp1, Document.class); + Document document = simpleDocumentMapper.convert(emp1, Document.class); long diff = System.currentTimeMillis() - start; System.out.println(diff); start = System.currentTimeMillis(); - Employee employee = mappableMapper.convert(document, Employee.class); + Employee employee = simpleDocumentMapper.convert(document, Employee.class); diff = System.currentTimeMillis() - start; System.out.println(diff); assertEquals(emp1, employee); @@ -65,7 +66,10 @@ public void testWithConverter() { @Test public void testWithMappable() { - mappableMapper = new MappableMapper(); + simpleDocumentMapper = new SimpleDocumentMapper(); + simpleDocumentMapper.registerEntityConverter(new Department.DepartmentConverter()); + simpleDocumentMapper.registerEntityConverter(new MappableEmployee.MappableEmployeeConverter()); + simpleDocumentMapper.registerEntityConverter(new MappableDepartment.Converter()); MappableEmployee boss = new MappableEmployee(); boss.setEmpId("1"); @@ -79,12 +83,12 @@ public void testWithMappable() { emp1.setBoss(boss); long start = System.currentTimeMillis(); - Document document = mappableMapper.convert(emp1, Document.class); + Document document = simpleDocumentMapper.convert(emp1, Document.class); long diff = System.currentTimeMillis() - start; System.out.println(diff); start = System.currentTimeMillis(); - MappableEmployee employee = mappableMapper.convert(document, MappableEmployee.class); + MappableEmployee employee = simpleDocumentMapper.convert(document, MappableEmployee.class); diff = System.currentTimeMillis() - start; System.out.println(diff); assertEquals(emp1, employee); @@ -92,7 +96,10 @@ public void testWithMappable() { @Test public void testWithConverterAndMappableMix() { - mappableMapper = new MappableMapper(); + simpleDocumentMapper = new SimpleDocumentMapper(); + simpleDocumentMapper.registerEntityConverter(new Department.DepartmentConverter()); + simpleDocumentMapper.registerEntityConverter(new MappableEmployee.MappableEmployeeConverter()); + simpleDocumentMapper.registerEntityConverter(new MappableDepartment.Converter()); final MappableEmployee boss = new MappableEmployee(); boss.setEmpId("1"); @@ -113,12 +120,12 @@ public void testWithConverterAndMappableMix() { }}); long start = System.currentTimeMillis(); - Document document = mappableMapper.convert(department, Document.class); + Document document = simpleDocumentMapper.convert(department, Document.class); long diff = System.currentTimeMillis() - start; System.out.println(diff); start = System.currentTimeMillis(); - Department dept = mappableMapper.convert(document, Department.class); + Department dept = simpleDocumentMapper.convert(document, Department.class); diff = System.currentTimeMillis() - start; System.out.println(diff); assertEquals(department, dept); @@ -126,7 +133,10 @@ public void testWithConverterAndMappableMix() { @Test public void testNested() { - mappableMapper = new MappableMapper(); + simpleDocumentMapper = new SimpleDocumentMapper(); + simpleDocumentMapper.registerEntityConverter(new Department.DepartmentConverter()); + simpleDocumentMapper.registerEntityConverter(new MappableEmployee.MappableEmployeeConverter()); + simpleDocumentMapper.registerEntityConverter(new MappableDepartment.Converter()); final MappableEmployee boss = new MappableEmployee(); boss.setEmpId("1"); @@ -147,12 +157,12 @@ public void testNested() { }}); long start = System.currentTimeMillis(); - Document document = mappableMapper.convert(department, Document.class); + Document document = simpleDocumentMapper.convert(department, Document.class); long diff = System.currentTimeMillis() - start; System.out.println(diff); start = System.currentTimeMillis(); - MappableDepartment dept = mappableMapper.convert(document, MappableDepartment.class); + MappableDepartment dept = simpleDocumentMapper.convert(document, MappableDepartment.class); diff = System.currentTimeMillis() - start; System.out.println(diff); assertEquals(department, dept); @@ -160,13 +170,16 @@ public void testNested() { @Test public void testWithValueType() { - mappableMapper = new MappableMapper(); + simpleDocumentMapper = new SimpleDocumentMapper(); + simpleDocumentMapper.registerEntityConverter(new Company.CompanyConverter()); + simpleDocumentMapper.registerEntityConverter(new Company.CompanyId.CompanyIdConverter()); + Company company = new Company(); company.setName("test"); company.setId(1L); company.setCompanyId(new Company.CompanyId(1L)); - Document document = mappableMapper.convert(company, Document.class); + Document document = simpleDocumentMapper.convert(company, Document.class); Object companyId = document.get("companyId"); assertTrue(companyId instanceof Document); } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/mapper/SimpleDocumentMapperTest.java b/nitrite/src/test/java/org/dizitart/no2/common/mapper/SimpleDocumentMapperTest.java new file mode 100644 index 000000000..f9aaebf42 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/common/mapper/SimpleDocumentMapperTest.java @@ -0,0 +1,96 @@ +package org.dizitart.no2.common.mapper; + +import org.dizitart.no2.integration.NitriteStressTest; +import org.dizitart.no2.integration.NitriteTest; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.exceptions.ObjectMappingException; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class SimpleDocumentMapperTest { + @Test + public void testConvert() { + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(Integer.class); + assertEquals(0, ((Integer) simpleDocumentMapper.convert(0, Object.class)).intValue()); + } + + @Test + public void testConvert2() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); + assertEquals("source", simpleDocumentMapper.convert("source", Object.class)); + } + + @Test + public void testConvert3() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); + assertEquals(0, ((Integer) simpleDocumentMapper.convert(0, Object.class)).intValue()); + } + + @Test + public void testConvertFromDocument() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); + assertNull(simpleDocumentMapper.convertFromDocument(null, Object.class)); + } + + @Test + public void testConvertFromDocument2() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); + Class type = Object.class; + assertThrows(ObjectMappingException.class, + () -> simpleDocumentMapper.convertFromDocument(Document.createDocument(), type)); + } + + @Test + public void testConvertFromDocument3() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + assertNull( + (new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class)).convertFromDocument(null, null)); + } + + @Test + public void testConvertToDocument() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + assertThrows(ObjectMappingException.class, + () -> (new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class)).convertToDocument("source")); + } + + @Test + public void testConvertToDocument2() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); + simpleDocumentMapper.registerEntityConverter(new NitriteTest.CompatChild.CompatChildConverter()); + assertEquals(2, simpleDocumentMapper.convertToDocument(new NitriteTest.CompatChild()).size()); + } + + @Test + public void testConvertToDocument3() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); + simpleDocumentMapper.registerEntityConverter(new NitriteStressTest.TestDto.Converter()); + assertEquals(7, simpleDocumentMapper.convertToDocument(new NitriteStressTest.TestDto()).size()); + } + + @Test + public void testConvertToDocument4() { + Class forNameResult = Object.class; + Class forNameResult1 = Object.class; + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); + simpleDocumentMapper.registerEntityConverter(new Company.CompanyConverter()); + assertEquals(3, simpleDocumentMapper.convertToDocument(new Company()).size()); + } + +} + diff --git a/nitrite/src/test/java/org/dizitart/no2/common/module/PluginManagerTest.java b/nitrite/src/test/java/org/dizitart/no2/common/module/PluginManagerTest.java index ceb44e2d5..459dd6a84 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/module/PluginManagerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/module/PluginManagerTest.java @@ -18,6 +18,7 @@ package org.dizitart.no2.common.module; import org.dizitart.no2.NitriteConfig; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.PluginException; import org.dizitart.no2.store.NitriteStore; import org.junit.Test; @@ -62,7 +63,7 @@ public void testFindAndLoadPlugins() { NitriteStore nitriteStore = pluginManager.getNitriteStore(); assertTrue(nitriteStore instanceof org.dizitart.no2.store.memory.InMemoryStore); assertEquals(3, pluginManager.getIndexerMap().size()); - assertTrue(pluginManager.getNitriteMapper() instanceof org.dizitart.no2.common.mapper.MappableMapper); + assertTrue(pluginManager.getNitriteMapper() instanceof SimpleDocumentMapper); assertFalse(nitriteStore.isClosed()); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java index 8776519f7..a5fda50e7 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java @@ -18,9 +18,9 @@ import org.dizitart.no2.NitriteBuilderTest; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.MappableMapper; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.filters.ComparableFilter; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.integration.Retry; @@ -83,7 +83,7 @@ public void testSkeletonDocument() { public void testSkeletonDocument2() { Class forNameResult = Object.class; Class forNameResult1 = Object.class; - MappableMapper nitriteMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); + SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); assertEquals(0, DocumentUtils.skeletonDocument(nitriteMapper, Object.class).size()); } @@ -103,7 +103,9 @@ public void testIsSimilar2() { @Test public void testDummyDocument() { - NitriteMapper nitriteMapper = new MappableMapper(); + SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(); + nitriteMapper.registerEntityConverter(new DummyTest.Converter()); + Document document = skeletonDocument(nitriteMapper, DummyTest.class); assertTrue(document.containsKey("first")); assertTrue(document.containsKey("second")); @@ -111,20 +113,30 @@ public void testDummyDocument() { assertNull(document.get("second")); } - private static class DummyTest implements Mappable { + private static class DummyTest { private String first; private Double second; - @Override - public Document write(NitriteMapper mapper) { - return createDocument("first", first) - .put("second", second); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - first = document.get("first", String.class); - second = document.get("second", Double.class); + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return DummyTest.class; + } + + @Override + public Document toDocument(DummyTest entity, NitriteMapper nitriteMapper) { + return createDocument("first", entity.first) + .put("second", entity.second); + } + + @Override + public DummyTest fromDocument(Document document, NitriteMapper nitriteMapper) { + DummyTest entity = new DummyTest(); + entity.first = document.get("first", String.class); + entity.second = document.get("second", Double.class); + return entity; + } } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/IndexUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/IndexUtilsTest.java index d3de97e68..b25ba2933 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/IndexUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/IndexUtilsTest.java @@ -33,7 +33,7 @@ public void testDeriveIndexMapName() { @Test public void testDeriveIndexMapName2() { IndexDescriptor indexDescriptor = new IndexDescriptor("Index Type", new Fields(), "Collection Name"); - indexDescriptor.setIndexFields(new Fields()); + indexDescriptor.setFields(new Fields()); assertEquals("$nitrite_index|Collection Name||Index Type", IndexUtils.deriveIndexMapName(indexDescriptor)); } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/NumbersTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/NumbersTest.java index 872ab873e..853a19943 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/NumbersTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/NumbersTest.java @@ -78,7 +78,7 @@ public void testCompare() { @Test public void testCompare2() { - Integer x = new Integer(1); - assertEquals(0, Numbers.compare(x, new Integer(1))); + Integer x = 1; + assertEquals(0, Numbers.compare(x, 1)); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java index c4f43b629..4280333e9 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java @@ -23,15 +23,25 @@ import org.apache.commons.lang3.mutable.MutableDouble; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.integration.repository.data.ChildClass; +import org.dizitart.no2.integration.repository.data.Company; import org.dizitart.no2.integration.repository.data.Employee; +import org.dizitart.no2.integration.repository.data.Note; +import org.dizitart.no2.integration.repository.decorator.Manufacturer; +import org.dizitart.no2.integration.repository.decorator.ManufacturerDecorator; +import org.dizitart.no2.integration.repository.decorator.ProductDecorator; import org.dizitart.no2.repository.annotations.Entity; import org.dizitart.no2.repository.annotations.Index; import org.junit.Test; import java.io.Serializable; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneId; import static org.dizitart.no2.common.util.ObjectUtils.newInstance; import static org.junit.Assert.*; @@ -54,6 +64,16 @@ public void testFindRepositoryName() { assertEquals("java.lang.Object", ObjectUtils.findRepositoryName(Object.class, "")); } + @Test + public void testFindRepositoryNameByDecorator() { + assertEquals("product", ObjectUtils.findRepositoryNameByDecorator(new ProductDecorator(), "")); + assertEquals("product+key", ObjectUtils.findRepositoryNameByDecorator(new ProductDecorator(), "key")); + assertEquals(Manufacturer.class.getName() + "+key", + ObjectUtils.findRepositoryNameByDecorator(new ManufacturerDecorator(), "key")); + assertEquals(Manufacturer.class.getName(), + ObjectUtils.findRepositoryNameByDecorator(new ManufacturerDecorator(), "")); + } + @Test public void testDeepEquals() { assertFalse(ObjectUtils.deepEquals("o1", "o2")); @@ -97,13 +117,21 @@ public void testDeepEquals8() { @Test public void testNewInstance() { - EnclosingType type = newInstance(EnclosingType.class, true); + SimpleDocumentMapper mapper = new SimpleDocumentMapper(); + mapper.registerEntityConverter(new EnclosingType.Converter()); + mapper.registerEntityConverter(new ChildClass.Converter()); + mapper.registerEntityConverter(new FieldType.Converter()); + mapper.registerEntityConverter(new Employee.EmployeeConverter()); + mapper.registerEntityConverter(new Company.CompanyConverter()); + mapper.registerEntityConverter(new Note.NoteConverter()); + + EnclosingType type = newInstance(EnclosingType.class, true, mapper); System.out.println(type); } @Test public void testIsValueType() { - assertFalse(ObjectUtils.isValueType(Object.class)); + assertFalse(ObjectUtils.isValueType(Object.class, new SimpleDocumentMapper())); } @Test @@ -149,12 +177,64 @@ public void testValidEntity() { private static class EnclosingType { private ChildClass childClass; private FieldType fieldType; + + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EnclosingType.class; + } + + @Override + public Document toDocument(EnclosingType entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("childClass", nitriteMapper.convert(entity.childClass, Document.class)) + .put("fieldType", nitriteMapper.convert(entity.fieldType, Document.class)); + } + + @Override + public EnclosingType fromDocument(Document document, NitriteMapper nitriteMapper) { + EnclosingType entity = new EnclosingType(); + entity.childClass = nitriteMapper.convert(document.get("childClass", Document.class), + ChildClass.class); + entity.fieldType = nitriteMapper.convert(document.get("fieldType", Document.class), + FieldType.class); + return entity; + } + } } @Data private static class FieldType { private Employee employee; private LocalDateTime currentDate; + + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return FieldType.class; + } + + @Override + public Document toDocument(FieldType entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("currentDate", entity.currentDate.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) + .put("employee", nitriteMapper.convert(entity.employee, Document.class)); + } + + @Override + public FieldType fromDocument(Document document, NitriteMapper nitriteMapper) { + FieldType entity = new FieldType(); + entity.employee = nitriteMapper.convert(document.get("employee", Document.class), Employee.class); + if (document.get("currentDate", Long.class) != null) { + entity.currentDate = LocalDateTime.ofInstant( + Instant.ofEpochMilli(document.get("currentDate", Long.class)), + ZoneId.systemDefault()); + } + return entity; + } + } } @Data diff --git a/nitrite/src/test/java/org/dizitart/no2/index/IndexFieldsTest.java b/nitrite/src/test/java/org/dizitart/no2/index/IndexFieldsTest.java new file mode 100644 index 000000000..a02015b9a --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/index/IndexFieldsTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.index; + +import org.dizitart.no2.exceptions.ValidationException; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class IndexFieldsTest { + + @Test + public void testIndextype() { + IndexFields indexFields = IndexFields.create(IndexType.UNIQUE, "a", "b"); + assertEquals(indexFields.getIndexType(), IndexType.UNIQUE); + } + + @Test + public void testGetFields() { + IndexFields indexFields = IndexFields.create(IndexType.UNIQUE, "a", "b"); + assertArrayEquals(indexFields.getFieldNames().toArray(), new String[] {"a", "b"}); + } + + @Test(expected = ValidationException.class) + public void testGetFieldsWithoutFields() { + IndexFields indexFields = IndexFields.create(IndexType.UNIQUE); + assertArrayEquals(indexFields.getFieldNames().toArray(), new String[0]); + } + + @Test + public void testGetEncodedName() { + IndexFields indexFields = IndexFields.create(IndexType.UNIQUE, "a", "b"); + assertEquals("Unique[a|b]", indexFields.getEncodedName()); + } + + @Test + public void testToString() { + IndexFields indexFields = IndexFields.create(IndexType.UNIQUE, "a", "b"); + assertEquals("Unique[a|b]", indexFields.toString()); + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/index/NitriteTextIndexerTest.java b/nitrite/src/test/java/org/dizitart/no2/index/NitriteTextIndexerTest.java index 4ccfe6ca9..daece55b9 100644 --- a/nitrite/src/test/java/org/dizitart/no2/index/NitriteTextIndexerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/index/NitriteTextIndexerTest.java @@ -77,11 +77,11 @@ public void testDropIndex2() { NitriteTextIndexer nitriteTextIndexer = new NitriteTextIndexer(); IndexDescriptor indexDescriptor = mock(IndexDescriptor.class); when(indexDescriptor.getIndexType()).thenThrow(new IndexingException("An error occurred")); - when(indexDescriptor.getIndexFields()).thenReturn(new Fields()); + when(indexDescriptor.getFields()).thenReturn(new Fields()); when(indexDescriptor.getCollectionName()).thenReturn("foo"); nitriteTextIndexer.dropIndex(indexDescriptor, new NitriteConfig()); verify(indexDescriptor).getIndexType(); - verify(indexDescriptor).getIndexFields(); + verify(indexDescriptor).getFields(); verify(indexDescriptor).getCollectionName(); } @@ -90,13 +90,13 @@ public void testDropIndex3() { NitriteTextIndexer nitriteTextIndexer = new NitriteTextIndexer(); IndexDescriptor indexDescriptor = mock(IndexDescriptor.class); when(indexDescriptor.getIndexType()).thenReturn("foo"); - when(indexDescriptor.getIndexFields()).thenReturn(new Fields()); + when(indexDescriptor.getFields()).thenReturn(new Fields()); when(indexDescriptor.getCollectionName()).thenReturn("foo"); NitriteConfig nitriteConfig = mock(NitriteConfig.class); doReturn(new InMemoryStore()).when(nitriteConfig).getNitriteStore(); nitriteTextIndexer.dropIndex(indexDescriptor, nitriteConfig); verify(indexDescriptor).getIndexType(); - verify(indexDescriptor).getIndexFields(); + verify(indexDescriptor).getFields(); verify(indexDescriptor).getCollectionName(); verify(nitriteConfig).getNitriteStore(); } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java index 50426568f..7c2c22836 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java @@ -22,8 +22,9 @@ import lombok.Data; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.ObjectRepository; @@ -52,6 +53,8 @@ public class NitriteStressTest { @Test public void stressTest() { Nitrite database = createDb(); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) database.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TestDto.Converter()); ObjectRepository testRepository = database.getRepository(TestDto.class); testRepository.createIndex(IndexOptions.indexOptions(IndexType.FULL_TEXT), "lastName"); testRepository.createIndex(IndexOptions.indexOptions(IndexType.NON_UNIQUE), "birthDate"); @@ -78,7 +81,7 @@ private List createTestSet() { } @Data - public static class TestDto implements Mappable { + public static class TestDto { @XmlElement( name = "StudentNumber", @@ -126,27 +129,37 @@ public static class TestDto implements Mappable { public TestDto() { } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("studentNumber", studentNumber) - .put("lastName", lastName) - .put("prefixes", prefixes) - .put("initials", initials) - .put("firstNames", firstNames) - .put("nickName", nickName) - .put("birthDate", birthDate); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TestDto.class; + } - @Override - public void read(NitriteMapper mapper, Document document) { - studentNumber = document.get("studentNumber", String.class); - lastName = document.get("lastName", String.class); - prefixes = document.get("prefixes", String.class); - initials = document.get("initials", String.class); - firstNames = document.get("firstNames", String.class); - nickName = document.get("nickName", String.class); - birthDate = document.get("birthDate", String.class); + @Override + public Document toDocument(TestDto entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("studentNumber", entity.studentNumber) + .put("lastName", entity.lastName) + .put("prefixes", entity.prefixes) + .put("initials", entity.initials) + .put("firstNames", entity.firstNames) + .put("nickName", entity.nickName) + .put("birthDate", entity.birthDate); + } + + @Override + public TestDto fromDocument(Document document, NitriteMapper nitriteMapper) { + TestDto entity = new TestDto(); + entity.studentNumber = document.get("studentNumber", String.class); + entity.lastName = document.get("lastName", String.class); + entity.prefixes = document.get("prefixes", String.class); + entity.initials = document.get("initials", String.class); + entity.firstNames = document.get("firstNames", String.class); + entity.nickName = document.get("nickName", String.class); + entity.birthDate = document.get("birthDate", String.class); + return entity; + } } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java index 76055bfd1..3bb030c56 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -28,12 +28,13 @@ import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.common.WriteResult; import org.dizitart.no2.common.concurrent.ThreadPoolManager; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -54,12 +55,12 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; -import static org.dizitart.no2.integration.TestUtil.createDb; import static org.dizitart.no2.collection.Document.createDocument; import static org.dizitart.no2.common.Constants.INTERNAL_NAME_SEPARATOR; import static org.dizitart.no2.common.Constants.META_MAP_NAME; import static org.dizitart.no2.filters.Filter.ALL; import static org.dizitart.no2.filters.FluentFilter.where; +import static org.dizitart.no2.integration.TestUtil.createDb; import static org.junit.Assert.*; /** @@ -74,6 +75,9 @@ public class NitriteTest { @Before public void setUp() throws ParseException { db = createDb("test-user", "test-password"); + SimpleDocumentMapper nitriteMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + nitriteMapper.registerEntityConverter(new Receipt.ReceiptConverter()); + nitriteMapper.registerEntityConverter(new CompatChild.CompatChildConverter()); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH); @@ -198,7 +202,7 @@ public void testMultipleGetRepository() { @Test(expected = ValidationException.class) public void testGetRepositoryInvalid() { - db.getRepository(null); + db.getRepository((Class) null); } @Test(expected = NitriteIOException.class) @@ -377,20 +381,30 @@ public void run() { @Data @AllArgsConstructor @NoArgsConstructor - public static class CompatChild implements Mappable { + public static class CompatChild { private Long childId; private String lastName; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("childId", childId) - .put("lastName", lastName); - } + public static class CompatChildConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return CompatChild.class; + } - @Override - public void read(NitriteMapper mapper, Document document) { - childId = document.get("childId", Long.class); - lastName = document.get("lastName", String.class); + @Override + public Document toDocument(CompatChild entity, NitriteMapper nitriteMapper) { + return Document.createDocument("childId", entity.getChildId()) + .put("lastName", entity.getLastName()); + } + + @Override + public CompatChild fromDocument(Document document, NitriteMapper nitriteMapper) { + CompatChild compatChild = new CompatChild(); + compatChild.setChildId(document.get("childId", Long.class)); + compatChild.setLastName(document.get("lastName", String.class)); + return compatChild; + } } } @@ -400,39 +414,50 @@ public void read(NitriteMapper mapper, Document document) { @Indices({ @Index(value = "synced", type = IndexType.NON_UNIQUE) }) - public static class Receipt implements Mappable { + public static class Receipt { @Id private String clientRef; private Boolean synced; private Long createdTimestamp = System.currentTimeMillis(); private Status status; - @Override - public Document write(NitriteMapper mapper) { - return createDocument("status", status) - .put("clientRef", clientRef) - .put("synced", synced) - .put("createdTimestamp", createdTimestamp); + public enum Status { + COMPLETED, + PREPARING, } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - Object status = document.get("status"); - if (status instanceof Status) { - this.status = (Status) status; - } else { - this.status = Status.valueOf(status.toString()); - } - this.clientRef = document.get("clientRef", String.class); - this.synced = document.get("synced", Boolean.class); - this.createdTimestamp = document.get("createdTimestamp", Long.class); + public static class ReceiptConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Receipt.class; } - } - public enum Status { - COMPLETED, - PREPARING, + @Override + public Document toDocument(Receipt entity, NitriteMapper nitriteMapper) { + return createDocument("status", entity.getStatus()) + .put("clientRef", entity.getClientRef()) + .put("synced", entity.getSynced()) + .put("createdTimestamp", entity.getCreatedTimestamp()); + } + + @Override + public Receipt fromDocument(Document document, NitriteMapper nitriteMapper) { + Receipt receipt = new Receipt(); + + if (document != null) { + Object status = document.get("status"); + if (status instanceof Receipt.Status) { + receipt.status = (Receipt.Status) status; + } else { + receipt.status = Receipt.Status.valueOf(status.toString()); + } + receipt.clientRef = document.get("clientRef", String.class); + receipt.synced = document.get("synced", Boolean.class); + receipt.createdTimestamp = document.get("createdTimestamp", Long.class); + } + return receipt; + } } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/StressTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/StressTest.java index 65142470c..1f9eabec3 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/StressTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/StressTest.java @@ -22,11 +22,12 @@ import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Index; import org.dizitart.no2.repository.annotations.Indices; @@ -61,6 +62,10 @@ public void before() { .fieldSeparator(".") .openOrCreate(); + SimpleDocumentMapper mapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + mapper.registerEntityConverter(new PerfTest.PerfTestConverter()); + mapper.registerEntityConverter(new PerfTestIndexed.PerfTestIndexedConverter()); + collection = db.getCollection("test"); } @@ -182,28 +187,38 @@ private List getItems(Class type) { } @Data - public static class PerfTest implements Mappable { + public static class PerfTest { private String firstName; private String lastName; private Integer age; private String text; - @Override - public Document write(NitriteMapper mapper) { - Document document = Document.createDocument(); - document.put("firstName", firstName); - document.put("lastName", lastName); - document.put("age", age); - document.put("text", text); - return document; - } + public static class PerfTestConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return PerfTest.class; + } + + @Override + public Document toDocument(PerfTest entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + document.put("firstName", entity.firstName); + document.put("lastName", entity.lastName); + document.put("age", entity.age); + document.put("text", entity.text); + return document; + } - @Override - public void read(NitriteMapper mapper, Document document) { - this.firstName = (String) document.get("firstName"); - this.lastName = (String) document.get("lastName"); - this.age = (Integer) document.get("age"); - this.text = (String) document.get("text"); + @Override + public PerfTest fromDocument(Document document, NitriteMapper nitriteMapper) { + PerfTest entity = new PerfTest(); + entity.firstName = (String) document.get("firstName"); + entity.lastName = (String) document.get("lastName"); + entity.age = (Integer) document.get("age"); + entity.text = (String) document.get("text"); + return entity; + } } } @@ -213,5 +228,32 @@ public void read(NitriteMapper mapper, Document document) { @Index(value = "text", type = IndexType.FULL_TEXT), }) private static class PerfTestIndexed extends PerfTest { + public static class PerfTestIndexedConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return PerfTestIndexed.class; + } + + @Override + public Document toDocument(PerfTestIndexed entity, NitriteMapper nitriteMapper) { + Document document = Document.createDocument(); + document.put("firstName", entity.getFirstName()); + document.put("lastName", entity.getLastName()); + document.put("age", entity.getAge()); + document.put("text", entity.getText()); + return document; + } + + @Override + public PerfTestIndexed fromDocument(Document document, NitriteMapper nitriteMapper) { + PerfTestIndexed entity = new PerfTestIndexed(); + entity.setFirstName((String) document.get("firstName")); + entity.setLastName((String) document.get("lastName")); + entity.setAge((Integer) document.get("age")); + entity.setText((String) document.get("text")); + return entity; + } + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java index c0e84a688..e1ecacaa5 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindNegativeTest.java @@ -21,6 +21,7 @@ import org.dizitart.no2.collection.DocumentCursor; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.exceptions.FilterException; +import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; import org.junit.Test; @@ -55,7 +56,7 @@ public void testFindOptionsNegativeSize() { collection.find(skipBy(0).limit(-1)); } - @Test(expected = ValidationException.class) + @Test(expected = InvalidOperationException.class) public void testFindInvalidSort() { insert(); collection.find(orderBy("data", SortOrder.Descending)).toList(); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java index 84fd453ac..0fabd00c6 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionIndexTest.java @@ -130,7 +130,7 @@ public void testRebuildIndex() { insert(); Collection indices = collection.listIndices(); for (IndexDescriptor idx : indices) { - collection.rebuildIndex(idx.getIndexFields().getFieldNames().toArray(new String[0])); + collection.rebuildIndex(idx.getFields().getFieldNames().toArray(new String[0])); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java index fe37845e1..a59e7e459 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionSingleFieldIndexTest.java @@ -134,7 +134,7 @@ public void testRebuildIndex() { insert(); Collection indices = collection.listIndices(); for (IndexDescriptor idx : indices) { - collection.rebuildIndex(idx.getIndexFields().getFieldNames().toArray(new String[0])); + collection.rebuildIndex(idx.getFields().getFieldNames().toArray(new String[0])); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java index f3ae1b720..2b01b1b19 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/BaseObjectRepositoryTest.java @@ -19,8 +19,11 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.*; +import org.dizitart.no2.integration.repository.decorator.*; +import org.dizitart.no2.integration.transaction.TxData; import org.dizitart.no2.repository.ObjectRepository; import org.junit.After; import org.junit.Before; @@ -44,6 +47,8 @@ public abstract class BaseObjectRepositoryTest { protected ObjectRepository aObjectRepository; protected ObjectRepository cObjectRepository; protected ObjectRepository bookRepository; + protected ObjectRepository productRepository; + protected ObjectRepository upcomingProductRepository; @Rule public Retry retry = new Retry(3); @@ -68,6 +73,9 @@ public void setUp() { bookRepository = db.getRepository(Book.class); + productRepository = db.getRepository(new ProductDecorator()); + upcomingProductRepository = db.getRepository(new ProductDecorator(), "upcoming"); + for (int i = 0; i < 10; i++) { Company company = DataGenerator.generateCompanyRecord(); companyRepository.insert(company); @@ -80,6 +88,12 @@ public void setUp() { Book book = DataGenerator.randomBook(); bookRepository.insert(book); + + Product product = DataGenerator.randomProduct(); + productRepository.insert(product); + + product = DataGenerator.randomProduct(); + upcomingProductRepository.insert(product); } } @@ -92,6 +106,30 @@ private void openDb() { } else { db = nitriteBuilder.openOrCreate(); } + + SimpleDocumentMapper mapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + mapper.registerEntityConverter(new Company.CompanyConverter()); + mapper.registerEntityConverter(new Employee.EmployeeConverter()); + mapper.registerEntityConverter(new Note.NoteConverter()); + mapper.registerEntityConverter(new Book.BookConverter()); + mapper.registerEntityConverter(new BookId.BookIdConverter()); + mapper.registerEntityConverter(new ClassA.ClassAConverter()); + mapper.registerEntityConverter(new ClassBConverter()); + mapper.registerEntityConverter(new ClassC.ClassCConverter()); + mapper.registerEntityConverter(new ElemMatch.Converter()); + mapper.registerEntityConverter(new InternalClass.Converter()); + mapper.registerEntityConverter(new UniversalTextTokenizerTest.TextData.Converter()); + mapper.registerEntityConverter(new SubEmployee.Converter()); + mapper.registerEntityConverter(new ProductScore.Converter()); + mapper.registerEntityConverter(new PersonEntity.Converter()); + mapper.registerEntityConverter(new RepeatableIndexTest.Converter()); + mapper.registerEntityConverter(new EncryptedPerson.Converter()); + mapper.registerEntityConverter(new TxData.Converter()); + mapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); + mapper.registerEntityConverter(new ProductConverter()); + mapper.registerEntityConverter(new ProductIdConverter()); + mapper.registerEntityConverter(new ManufacturerConverter()); + mapper.registerEntityConverter(new MiniProduct.Converter()); } @After @@ -116,6 +154,14 @@ public void clear() throws Exception { bookRepository.remove(ALL); } + if (productRepository != null && !productRepository.isDropped()) { + productRepository.remove(ALL); + } + + if (upcomingProductRepository != null && !upcomingProductRepository.isDropped()) { + upcomingProductRepository.remove(ALL); + } + if (db != null && !db.isClosed()) { db.commit(); db.close(); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java index fe768e51a..014e57f0b 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java @@ -23,17 +23,18 @@ import lombok.ToString; import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteConfig; -import org.dizitart.no2.integration.Retry; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; +import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.integration.Retry; +import org.dizitart.no2.integration.repository.data.Company; +import org.dizitart.no2.integration.repository.data.Note; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; import org.dizitart.no2.repository.annotations.Indices; -import org.dizitart.no2.integration.repository.data.Company; -import org.dizitart.no2.integration.repository.data.Note; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -61,6 +62,11 @@ public void setUp() { .fieldSeparator(":") .openOrCreate(); + SimpleDocumentMapper mapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + mapper.registerEntityConverter(new Company.CompanyConverter()); + mapper.registerEntityConverter(new EmployeeForCustomSeparator.EmployeeForCustomSeparatorConverter()); + mapper.registerEntityConverter(new Note.NoteConverter()); + repository = db.getRepository(EmployeeForCustomSeparator.class); } @@ -111,7 +117,7 @@ public void testFindByEmbeddedField() { @Index(value = "address", type = IndexType.FULL_TEXT), @Index(value = "employeeNote:text", type = IndexType.FULL_TEXT) }) - public static class EmployeeForCustomSeparator implements Serializable, Mappable { + public static class EmployeeForCustomSeparator implements Serializable { @Id @Getter @Setter @@ -149,29 +155,41 @@ public EmployeeForCustomSeparator(EmployeeForCustomSeparator copy) { employeeNote = copy.employeeNote; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("empId", empId) - .put("joinDate", joinDate) - .put("address", address) - .put("blob", blob) - .put("company", company.write(mapper)) - .put("employeeNote", employeeNote.write(mapper)); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); - blob = document.get("blob", byte[].class); - employeeNote = new Note(); - Document doc = document.get("employeeNote", Document.class); - employeeNote.read(mapper, doc); - company = new Company(); - doc = document.get("company", Document.class); - company.read(mapper, doc); + public static class EmployeeForCustomSeparatorConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmployeeForCustomSeparator.class; + } + + @Override + public Document toDocument(EmployeeForCustomSeparator entity, NitriteMapper nitriteMapper) { + return Document.createDocument().put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address) + .put("blob", entity.blob) + .put("company", nitriteMapper.convert(entity.company, Document.class)) + .put("employeeNote", nitriteMapper.convert(entity.employeeNote, Document.class)); + } + + @Override + public EmployeeForCustomSeparator fromDocument(Document document, NitriteMapper nitriteMapper) { + EmployeeForCustomSeparator entity = new EmployeeForCustomSeparator(); + + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + entity.blob = document.get("blob", byte[].class); + + Document doc = document.get("employeeNote", Document.class); + entity.employeeNote = nitriteMapper.convert(doc, Note.class); + + doc = document.get("company", Document.class); + entity.company = nitriteMapper.convert(doc, Company.class); + return entity; + } } } + } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java index 1a52708ce..7096ddaaf 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -27,20 +27,29 @@ * @author Anindya Chatterjee. */ @Data -class InternalClass implements Mappable { +class InternalClass { @Id private long id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("name", name); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return InternalClass.class; + } + + @Override + public Document toDocument(InternalClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - name = document.get("name", String.class); + @Override + public InternalClass fromDocument(Document document, NitriteMapper nitriteMapper) { + InternalClass entity = new InternalClass(); + entity.id = document.get("id", Long.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java index cc1036d16..c92f7b6a3 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java @@ -18,6 +18,7 @@ package org.dizitart.no2.integration.repository; import org.dizitart.no2.Nitrite; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.TestUtil; import org.dizitart.no2.collection.NitriteId; @@ -48,6 +49,9 @@ public class NitriteIdAsIdTest { @Before public void before() { db = TestUtil.createDb(); + SimpleDocumentMapper mapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + mapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); + repo = db.getRepository(WithNitriteId.class); } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java index 91ced7232..c6825342b 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java @@ -18,6 +18,7 @@ package org.dizitart.no2.integration.repository; import org.dizitart.no2.Nitrite; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.TestUtil; import org.dizitart.no2.collection.NitriteId; @@ -45,6 +46,18 @@ public class ObjectRepositoryNegativeTest { @Before public void setUp() { db = TestUtil.createDb(); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new WithPublicField.Converter()); + documentMapper.registerEntityConverter(new WithObjectId.Converter()); + documentMapper.registerEntityConverter(new WithOutId.Converter()); + documentMapper.registerEntityConverter(new WithoutEmbeddedId.Converter()); + documentMapper.registerEntityConverter(new WithoutEmbeddedId.NestedId.Converter()); + documentMapper.registerEntityConverter(new WithEmptyStringId.Converter()); + documentMapper.registerEntityConverter(new WithNullId.Converter()); + documentMapper.registerEntityConverter(new Employee.EmployeeConverter()); + documentMapper.registerEntityConverter(new Company.CompanyConverter()); + documentMapper.registerEntityConverter(new Note.NoteConverter()); + documentMapper.registerEntityConverter(new WithNitriteId.WithNitriteIdConverter()); } @After @@ -156,7 +169,7 @@ public void testRemoveNoId() { repository.remove(object); } - @Test(expected = ValidationException.class) + @Test(expected = ObjectMappingException.class) public void testProjectionFailedInstantiate() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index efdb91cc2..a51aec703 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -20,16 +20,17 @@ import com.github.javafaker.Faker; import lombok.Data; import org.dizitart.no2.Nitrite; -import org.dizitart.no2.integration.Retry; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.common.meta.Attributes; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.MappableMapper; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; +import org.dizitart.no2.common.meta.Attributes; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.repository.data.*; +import org.dizitart.no2.integration.repository.decorator.*; import org.dizitart.no2.repository.Cursor; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Entity; @@ -61,7 +62,24 @@ public class ObjectRepositoryTest { @Before public void setUp() { - NitriteMapper mapper = new MappableMapper(); + SimpleDocumentMapper mapper = new SimpleDocumentMapper(); + mapper.registerEntityConverter(new InternalClass.Converter()); + mapper.registerEntityConverter(new EmployeeEntity.Converter()); + mapper.registerEntityConverter(new StressRecord.Converter()); + mapper.registerEntityConverter(new WithClassField.Converter()); + mapper.registerEntityConverter(new WithDateId.Converter()); + mapper.registerEntityConverter(new WithTransientField.Converter()); + mapper.registerEntityConverter(new WithOutId.Converter()); + mapper.registerEntityConverter(new ChildClass.Converter()); + mapper.registerEntityConverter(new WithOutGetterSetter.Converter()); + mapper.registerEntityConverter(new WithPrivateConstructor.Converter()); + mapper.registerEntityConverter(new WithPublicField.Converter()); + mapper.registerEntityConverter(new Employee.EmployeeConverter()); + mapper.registerEntityConverter(new Company.CompanyConverter()); + mapper.registerEntityConverter(new ProductConverter()); + mapper.registerEntityConverter(new ProductIdConverter()); + mapper.registerEntityConverter(new ManufacturerConverter()); + mapper.registerEntityConverter(new MiniProduct.Converter()); db = Nitrite.builder() .loadModule(module(mapper)) @@ -334,12 +352,152 @@ public void testIssue217() { await().atMost(5, TimeUnit.SECONDS).until(() -> counter.get() == 1); } + @Test + public void testRepositoryName() { + ObjectRepository productRepository = db.getRepository(new ProductDecorator()); + ObjectRepository upcomingProductRepository = db.getRepository(new ProductDecorator(), "upcoming"); + ObjectRepository manufacturerRepository = db.getRepository(new ManufacturerDecorator()); + ObjectRepository exManufacturerRepository = db.getRepository(new ManufacturerDecorator(), "ex"); + ObjectRepository employeeRepository = db.getRepository(Employee.class); + ObjectRepository managerRepository = db.getRepository(Employee.class, "manager"); + + assertEquals(productRepository.getDocumentCollection().getName(), "product"); + assertEquals(upcomingProductRepository.getDocumentCollection().getName(), + "product+upcoming"); + assertEquals(manufacturerRepository.getDocumentCollection().getName(), Manufacturer.class.getName()); + assertEquals(exManufacturerRepository.getDocumentCollection().getName(), Manufacturer.class.getName() + "+ex"); + assertEquals(employeeRepository.getDocumentCollection().getName(), Employee.class.getName()); + assertEquals(managerRepository.getDocumentCollection().getName(), Employee.class.getName() + "+manager"); + } + + @Test + public void testRepositoryType() { + ObjectRepository productRepository = db.getRepository(new ProductDecorator()); + ObjectRepository upcomingProductRepository = db.getRepository(new ProductDecorator(), "upcoming"); + ObjectRepository manufacturerRepository = db.getRepository(new ManufacturerDecorator()); + ObjectRepository exManufacturerRepository = db.getRepository(new ManufacturerDecorator(), "ex"); + ObjectRepository employeeRepository = db.getRepository(Employee.class); + ObjectRepository managerRepository = db.getRepository(Employee.class, "manager"); + + assertEquals(productRepository.getType(), Product.class); + assertEquals(upcomingProductRepository.getType(), Product.class); + assertEquals(manufacturerRepository.getType(), Manufacturer.class); + assertEquals(exManufacturerRepository.getType(), Manufacturer.class); + assertEquals(employeeRepository.getType(), Employee.class); + assertEquals(managerRepository.getType(), Employee.class); + } + + @Test + public void testDestroyRepository() { + ObjectRepository productRepository = db.getRepository(new ProductDecorator()); + ObjectRepository upcomingProductRepository = db.getRepository(new ProductDecorator(), "upcoming"); + ObjectRepository manufacturerRepository = db.getRepository(new ManufacturerDecorator()); + ObjectRepository exManufacturerRepository = db.getRepository(new ManufacturerDecorator(), "ex"); + ObjectRepository employeeRepository = db.getRepository(Employee.class); + ObjectRepository managerRepository = db.getRepository(Employee.class, "manager"); + + assertNotNull(productRepository); + assertNotNull(upcomingProductRepository); + assertNotNull(manufacturerRepository); + assertNotNull(exManufacturerRepository); + assertNotNull(employeeRepository); + assertNotNull(managerRepository); + + assertTrue(db.hasRepository(new ProductDecorator())); + assertTrue(db.hasRepository(new ProductDecorator(), "upcoming")); + assertTrue(db.hasRepository(new ManufacturerDecorator())); + assertTrue(db.hasRepository(new ManufacturerDecorator(), "ex")); + assertTrue(db.hasRepository(Employee.class)); + assertTrue(db.hasRepository(Employee.class, "manager")); + + db.destroyRepository(new ProductDecorator()); + assertFalse(db.hasRepository(new ProductDecorator())); + assertTrue(db.hasRepository(new ProductDecorator(), "upcoming")); + + db.destroyRepository(new ProductDecorator(), "upcoming"); + assertFalse(db.hasRepository(new ProductDecorator())); + assertFalse(db.hasRepository(new ProductDecorator(), "upcoming")); + + db.destroyRepository(new ManufacturerDecorator()); + db.destroyRepository(new ManufacturerDecorator(), "ex"); + assertFalse(db.hasRepository(new ManufacturerDecorator())); + assertFalse(db.hasRepository(new ManufacturerDecorator(), "ex")); + + db.destroyRepository(Employee.class); + assertFalse(db.hasRepository(Employee.class)); + assertTrue(db.hasRepository(Employee.class, "manager")); + + db.destroyRepository(Employee.class, "manager"); + assertFalse(db.hasRepository(Employee.class)); + assertFalse(db.hasRepository(Employee.class, "manager")); + } + + @Test + public void testDestroyRepositoryWrongDecorator() { + ObjectRepository productRepository = db.getRepository(new ProductDecorator()); + assertNotNull(productRepository); + assertTrue(db.hasRepository(new ProductDecorator())); + assertFalse(db.hasRepository(new ManufacturerDecorator())); + + db.destroyRepository(new ManufacturerDecorator()); + assertTrue(db.hasRepository(new ProductDecorator())); + assertFalse(db.hasRepository(new ManufacturerDecorator())); + } + + @Test + public void testDestroyRepositoryWrongDecoratorWithKey() { + ObjectRepository productRepository = db.getRepository(new ProductDecorator(), "upcoming"); + assertNotNull(productRepository); + assertTrue(db.hasRepository(new ProductDecorator(), "upcoming")); + assertFalse(db.hasRepository(new ManufacturerDecorator())); + + db.destroyRepository(new ManufacturerDecorator(), "upcoming"); + assertTrue(db.hasRepository(new ProductDecorator(), "upcoming")); + assertFalse(db.hasRepository(new ManufacturerDecorator())); + } + + @Test + public void testDestroyRepositoryWrongClassName() { + ObjectRepository productRepository = db.getRepository(new ProductDecorator()); + assertNotNull(productRepository); + assertFalse(db.hasRepository(Product.class)); + + db.destroyRepository(Product.class); + assertTrue(db.hasRepository(new ProductDecorator())); + assertFalse(db.hasRepository(Product.class)); + } + + @Test + public void testDestroyRepositoryWrongClassNameAndKey() { + ObjectRepository employeeRepository = db.getRepository(Employee.class); + assertNotNull(employeeRepository); + assertTrue(db.hasRepository(Employee.class)); + + db.destroyRepository(Employee.class, "manager"); + assertTrue(db.hasRepository(Employee.class)); + assertFalse(db.hasRepository(Employee.class, "manager")); + } + + @Test + public void testHasRepository() { + ObjectRepository employeeRepository = db.getRepository(Employee.class); + ObjectRepository productRepository = db.getRepository(new ProductDecorator()); + + assertNotNull(employeeRepository); + assertNotNull(productRepository); + + assertTrue(db.hasRepository(Employee.class)); + assertFalse(db.hasRepository(Employee.class, "manager")); + assertTrue(db.hasRepository(new ProductDecorator())); + assertFalse(db.hasRepository(new ProductDecorator(), "ex")); + } + @Data @Entity(value = "entity.employee", indices = { @Index(value = "firstName", type = IndexType.NON_UNIQUE), @Index(value = "lastName", type = IndexType.NON_UNIQUE), }) - private static class EmployeeEntity implements Mappable { + private static class EmployeeEntity { private static final Faker faker = new Faker(); @Id @@ -353,18 +511,28 @@ public EmployeeEntity() { lastName = faker.name().lastName(); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("firstName", firstName) - .put("lastName", lastName); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - firstName = document.get("firstName", String.class); - lastName = document.get("lastName", String.class); + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmployeeEntity.class; + } + + @Override + public Document toDocument(EmployeeEntity entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("firstName", entity.firstName) + .put("lastName", entity.lastName); + } + + @Override + public EmployeeEntity fromDocument(Document document, NitriteMapper nitriteMapper) { + EmployeeEntity entity = new EmployeeEntity(); + entity.id = document.get("id", Long.class); + entity.firstName = document.get("firstName", String.class); + entity.lastName = document.get("lastName", String.class); + return entity; + } } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 2e6273d05..538c20d13 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -59,7 +59,7 @@ public void testRepositoryFactory() { public void testNullType() { RepositoryFactory factory = new RepositoryFactory(new CollectionFactory(new LockService())); db = TestUtil.createDb(); - factory.getRepository(db.getConfig(), null, "dummy"); + factory.getRepository(db.getConfig(), (Class) null, "dummy"); } @Test diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java index 84b60956d..8f8d3a697 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryJoinTest.java @@ -20,14 +20,15 @@ import lombok.Data; import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; -import org.dizitart.no2.integration.Retry; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.Lookup; import org.dizitart.no2.common.RecordStream; -import org.dizitart.no2.exceptions.InvalidOperationException; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; +import org.dizitart.no2.exceptions.InvalidOperationException; +import org.dizitart.no2.integration.Retry; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Id; import org.junit.After; @@ -103,6 +104,11 @@ private void openDb() { } else { db = nitriteBuilder.openOrCreate(); } + + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new Person.Converter()); + documentMapper.registerEntityConverter(new Address.Converter()); + documentMapper.registerEntityConverter(new PersonDetails.Converter()); } @After @@ -171,80 +177,120 @@ public void testRemove() { } @Data - public static class Person implements Mappable { + public static class Person { @Id private NitriteId nitriteId; private String id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("id", id) - .put("name", name); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - id = document.get("id", String.class); - name = document.get("name", String.class); + @Override + public Class getEntityType() { + return Person.class; + } + + @Override + public Document toDocument(Person entity, NitriteMapper nitriteMapper) { + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("id", entity.id) + .put("name", entity.name); + } + + @Override + public Person fromDocument(Document document, NitriteMapper nitriteMapper) { + Person entity = new Person(); + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.id = document.get("id", String.class); + entity.name = document.get("name", String.class); + return entity; + } } } @Data - public static class Address implements Mappable { + public static class Address { @Id private NitriteId nitriteId; private String personId; private String street; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("personId", personId) - .put("street", street); - } + public static class Converter implements EntityConverter
{ - @Override - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - personId = document.get("personId", String.class); - street = document.get("street", String.class); + @Override + public Class
getEntityType() { + return Address.class; + } + + @Override + public Document toDocument(Address entity, NitriteMapper nitriteMapper) { + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("personId", entity.personId) + .put("street", entity.street); + } + + @Override + public Address fromDocument(Document document, NitriteMapper nitriteMapper) { + Address entity = new Address(); + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.personId = document.get("personId", String.class); + entity.street = document.get("street", String.class); + return entity; + } } } @Data - public static class PersonDetails implements Mappable { + public static class PersonDetails { @Id private NitriteId nitriteId; private String id; private String name; private List
addresses; - @Override - public Document write(NitriteMapper mapper) { - return createDocument() - .put("nitriteId", nitriteId) - .put("personId", id) - .put("street", name) - .put("addresses", addresses); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PersonDetails.class; + } + + @Override + public Document toDocument(PersonDetails entity, NitriteMapper nitriteMapper) { + List documents = new ArrayList<>(); + if (entity.addresses != null) { + for (Address address : entity.addresses) { + documents.add(nitriteMapper.convert(address, Document.class)); + } + } + + return createDocument() + .put("nitriteId", entity.nitriteId) + .put("personId", entity.id) + .put("street", entity.name) + .put("addresses", documents); + } + + @Override + public PersonDetails fromDocument(Document document, NitriteMapper nitriteMapper) { + PersonDetails entity = new PersonDetails(); + + entity.nitriteId = document.get("nitriteId", NitriteId.class); + entity.id = document.get("id", String.class); + entity.name = document.get("name", String.class); + + Collection documents = document.get("addresses", Collection.class); + if (documents != null) { + entity.addresses = new ArrayList<>(); + for (Document doc : documents) { + Address address = nitriteMapper.convert(doc, Address.class); + entity.addresses.add(address); + } + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - nitriteId = document.get("nitriteId", NitriteId.class); - id = document.get("id", String.class); - name = document.get("name", String.class); - Set documents = document.get("addresses", Set.class); - this.addresses = new ArrayList<>(); - for (Document doc : documents) { - Address address = new Address(); - address.read(mapper, doc); - addresses.add(address); + return entity; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java index 29524a7cf..d89a0b123 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java @@ -20,8 +20,9 @@ import lombok.Getter; import org.dizitart.no2.collection.Document; import org.dizitart.no2.common.SortOrder; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.FilterException; import org.dizitart.no2.exceptions.InvalidIdException; import org.dizitart.no2.exceptions.NotIdentifiableException; @@ -318,7 +319,7 @@ public void testRegexFilter() { int count = employees.toList().size(); List employeeList = employeeRepository.find(where("emailAddress") - .regex("^[a-zA-Z0-9+_.-]+@[a-zA-Z0-9.-]+$")) + .regex("^[a-zA-Z0-9+_.-]+@[a-zA-Z0-9.-]+$")) .toList(); assertEquals(employeeList.size(), count); @@ -364,21 +365,20 @@ public void testElemMatchFilter() { final ProductScore score6 = new ProductScore("xyz", 8); ObjectRepository repository = db.getRepository(ElemMatch.class); - ElemMatch e1 = new ElemMatch() {{ - setId(1); - setStrArray(new String[]{"a", "b"}); - setProductScores(new ProductScore[]{score1, score4}); - }}; - ElemMatch e2 = new ElemMatch() {{ - setId(2); - setStrArray(new String[]{"d", "e"}); - setProductScores(new ProductScore[]{score2, score5}); - }}; - ElemMatch e3 = new ElemMatch() {{ - setId(3); - setStrArray(new String[]{"a", "f"}); - setProductScores(new ProductScore[]{score3, score6}); - }}; + ElemMatch e1 = new ElemMatch(); + e1.setId(1); + e1.setStrArray(new String[]{"a", "b"}); + e1.setProductScores(new ProductScore[]{score1, score4}); + + ElemMatch e2 = new ElemMatch(); + e2.setId(2); + e2.setStrArray(new String[]{"d", "e"}); + e2.setProductScores(new ProductScore[]{score2, score5}); + + ElemMatch e3 = new ElemMatch(); + e3.setId(3); + e3.setStrArray(new String[]{"a", "f"}); + e3.setProductScores(new ProductScore[]{score3, score6}); repository.insert(e1, e2, e3); @@ -563,24 +563,37 @@ public void testIdSet() { @Test public void testBetweenFilter() { @Getter - class TestData implements Mappable { + class TestData { private Date age; public TestData(Date age) { this.age = age; } + } + + class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TestData.class; + } @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("age", age); + public Document toDocument(TestData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("age", entity.age); } @Override - public void read(NitriteMapper mapper, Document document) { - age = document.get("age", Date.class); + public TestData fromDocument(Document document, NitriteMapper nitriteMapper) { + TestData entity = new TestData(new Date()); + entity.age = document.get("age", Date.class); + return entity; } } + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new Converter()); + TestData data1 = new TestData(new GregorianCalendar(2020, Calendar.JANUARY, 11).getTime()); TestData data2 = new TestData(new GregorianCalendar(2021, Calendar.FEBRUARY, 12).getTime()); TestData data3 = new TestData(new GregorianCalendar(2022, Calendar.MARCH, 13).getTime()); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/UnAnnotatedObjectTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/UnAnnotatedObjectTest.java index 5ed5e452d..c5c27490e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/UnAnnotatedObjectTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/UnAnnotatedObjectTest.java @@ -18,16 +18,18 @@ package org.dizitart.no2.integration.repository; +import org.dizitart.no2.common.RecordStream; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.integration.repository.data.ClassA; import org.dizitart.no2.integration.repository.data.ClassC; +import org.dizitart.no2.integration.repository.decorator.MiniProduct; +import org.dizitart.no2.integration.repository.decorator.Product; import org.dizitart.no2.repository.Cursor; import org.junit.Test; import static org.dizitart.no2.collection.FindOptions.orderBy; import static org.dizitart.no2.filters.FluentFilter.where; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.*; /** * @author Anindya Chatterjee. @@ -74,4 +76,64 @@ public void testFind() { System.out.println(classC); } } + + @Test + public void testDecoratedEntityFind() { + Cursor cursor = productRepository.find(); + assertEquals(cursor.size(), 10); + assertFalse(cursor.isEmpty()); + + assertTrue(productRepository.hasIndex("productId.uniqueId", "productId.productCode")); + assertTrue(productRepository.hasIndex("manufacturer.name")); + assertTrue(productRepository.hasIndex("productName", "manufacturer.uniqueId")); + + cursor = productRepository.find(where("productId.uniqueId").notEq(null) + .and(where("price").gt(0.0))); + + assertTrue(!cursor.isEmpty()); + assertEquals(cursor.size(), 10); + + RecordStream miniProducts = cursor.project(MiniProduct.class); + + for (MiniProduct miniProduct : miniProducts) { + Cursor products = productRepository.find(where("productId.uniqueId") + .eq(miniProduct.getUniqueId())); + + assertNotNull(products); + assertFalse(products.isEmpty()); + assertEquals(1, products.size()); + assertEquals(miniProduct.getManufacturerName(), products.firstOrNull().getManufacturer().getName()); + assertEquals(miniProduct.getPrice(), products.firstOrNull().getPrice()); + } + } + + @Test + public void testDecoratedEntityFindWithTag() { + Cursor cursor = upcomingProductRepository.find(); + assertEquals(cursor.size(), 10); + assertFalse(cursor.isEmpty()); + + assertTrue(upcomingProductRepository.hasIndex("productId.uniqueId", "productId.productCode")); + assertTrue(upcomingProductRepository.hasIndex("manufacturer.name")); + assertTrue(upcomingProductRepository.hasIndex("productName", "manufacturer.uniqueId")); + + cursor = upcomingProductRepository.find(where("productId.uniqueId").notEq(null) + .and(where("price").gt(0.0))); + + assertTrue(!cursor.isEmpty()); + assertEquals(cursor.size(), 10); + + RecordStream miniProducts = cursor.project(MiniProduct.class); + + for (MiniProduct miniProduct : miniProducts) { + Cursor products = upcomingProductRepository.find(where("productId.uniqueId") + .eq(miniProduct.getUniqueId())); + + assertNotNull(products); + assertFalse(products.isEmpty()); + assertEquals(1, products.size()); + assertEquals(miniProduct.getManufacturerName(), products.firstOrNull().getManufacturer().getName()); + assertEquals(miniProduct.getPrice(), products.firstOrNull().getPrice()); + } + } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index 8d19eac2a..1dc5bf294 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -20,12 +20,13 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.NitriteBuilder; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.index.NitriteTextIndexer; import org.dizitart.no2.index.fulltext.Languages; import org.dizitart.no2.index.fulltext.UniversalTextTokenizer; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.Cursor; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Index; @@ -34,10 +35,9 @@ import org.junit.Before; import org.junit.Test; -import static org.dizitart.no2.integration.DbTestOperations.getRandomTempDbFile; +import static org.dizitart.no2.common.module.NitriteModule.module; import static org.dizitart.no2.filters.Filter.ALL; import static org.dizitart.no2.filters.FluentFilter.where; -import static org.dizitart.no2.common.module.NitriteModule.module; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -45,7 +45,6 @@ * @author Anindya Chatterjee */ public class UniversalTextTokenizerTest extends BaseObjectRepositoryTest { - private final String fileName = getRandomTempDbFile(); private ObjectRepository textRepository; @Before @@ -54,6 +53,8 @@ public void setUp() { openDb(); textRepository = db.getRepository(TextData.class); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new TextData.Converter()); for (int i = 0; i < 10; i++) { TextData data = new TextData(); @@ -159,20 +160,29 @@ public void testUniversalFullTextIndexing() { @Indices( @Index(value = "text", type = IndexType.FULL_TEXT) ) - public static class TextData implements Mappable { + public static class TextData { public int id; public String text; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("text", text); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return TextData.class; + } + + @Override + public Document toDocument(TextData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("text", entity.text); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Integer.class); - text = document.get("text", String.class); + @Override + public TextData fromDocument(Document document, NitriteMapper nitriteMapper) { + TextData entity = new TextData(); + entity.id = document.get("id", Integer.class); + entity.text = document.get("text", String.class); + return entity; + } } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index c80c7b060..cb3c972ca 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Entity; @@ -39,7 +39,7 @@ @Index(value = "description", type = IndexType.FULL_TEXT), @Index(value = { "price", "publisher" }) }) -public class Book implements Mappable { +public class Book { @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) private BookId bookId; @@ -51,22 +51,32 @@ public class Book implements Mappable { private String description; - @Override - public Document write(NitriteMapper mapper) { - return createDocument("book_id", mapper.convert(bookId, Document.class)) - .put("publisher", publisher) - .put("price", price) - .put("tags", tags) - .put("description", description); - } + public static class BookConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Book.class; + } + + @Override + public Document toDocument(Book entity, NitriteMapper nitriteMapper) { + return createDocument("book_id", nitriteMapper.convert(entity.bookId, Document.class)) + .put("publisher", entity.publisher) + .put("price", entity.price) + .put("tags", entity.tags) + .put("description", entity.description); + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - bookId = mapper.convert(document.get("book_id"), BookId.class); - publisher = document.get("publisher", String.class); - price = document.get("price", Double.class); - tags = (List) document.get("tags", List.class); - description = document.get("description", String.class); + @Override + @SuppressWarnings("unchecked") + public Book fromDocument(Document document, NitriteMapper nitriteMapper) { + Book entity = new Book(); + entity.bookId = nitriteMapper.convert(document.get("book_id"), BookId.class); + entity.publisher = document.get("publisher", String.class); + entity.price = document.get("price", Double.class); + entity.tags = (List) document.get("tags", List.class); + entity.description = document.get("description", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java index 2afcc42f6..4aa11f1af 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/BookId.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import static org.dizitart.no2.collection.Document.createDocument; @@ -28,24 +28,34 @@ * @author Anindya Chatterjee */ @Data -public class BookId implements Mappable { +public class BookId { private String isbn; private String name; private String author; - @Override - public Document write(NitriteMapper mapper) { - return createDocument("isbn", isbn) - .put("book_name", name) - .put("author", author); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - isbn = document.get("isbn", String.class); - name = document.get("book_name", String.class); - author = document.get("author", String.class); + public static class BookIdConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return BookId.class; + } + + @Override + public Document toDocument(BookId entity, NitriteMapper nitriteMapper) { + return createDocument("isbn", entity.isbn) + .put("book_name", entity.name) + .put("author", entity.author); + } + + @Override + public BookId fromDocument(Document document, NitriteMapper nitriteMapper) { + BookId entity = new BookId(); + entity.isbn = document.get("isbn", String.class); + entity.name = document.get("book_name", String.class); + entity.author = document.get("author", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java index d3c2ba644..6f0470d26 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ChildClass.java @@ -20,9 +20,12 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.InheritIndices; +import java.util.Date; + /** * @author Anindya Chatterjee */ @@ -32,14 +35,30 @@ public class ChildClass extends ParentClass { private String name; - @Override - public Document write(NitriteMapper mapper) { - return super.write(mapper).put("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ChildClass.class; + } + + @Override + public Document toDocument(ChildClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.getName()) + .put("id", entity.getId()) + .put("date", entity.getDate()) + .put("text", entity.getText()); + } - @Override - public void read(NitriteMapper mapper, Document document) { - super.read(mapper, document); - name = document.get("name", String.class); + @Override + public ChildClass fromDocument(Document document, NitriteMapper nitriteMapper) { + ChildClass entity = new ChildClass(); + entity.setId(document.get("id", Long.class)); + entity.setDate(document.get("date", Date.class)); + entity.setText(document.get("text", String.class)); + entity.setName(document.get("name", String.class)); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java index a8161c4e2..4e736981d 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassA.java @@ -22,14 +22,14 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.UUID; @EqualsAndHashCode @ToString -public class ClassA implements Mappable { +public class ClassA { @Getter @Setter private ClassB b; @@ -53,23 +53,33 @@ public static ClassA create(int seed) { return classA; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("b", b != null ? b.write(mapper) : null) - .put("uid", uid) - .put("string", string) - .put("blob", blob); - } + public static class ClassAConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassA.class; + } + + @Override + public Document toDocument(ClassA entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("b", nitriteMapper.convert(entity.b, Document.class)) + .put("uid", entity.uid) + .put("string", entity.string) + .put("blob", entity.blob); + } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document.get("b") != null) { - b = new ClassB(); - b.read(mapper, document.get("b", Document.class)); + @Override + public ClassA fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassA entity = new ClassA(); + if (document.get("b") != null) { + Document doc = document.get("b", Document.class); + entity.b = nitriteMapper.convert(doc, ClassB.class); + } + entity.uid = document.get("uid", UUID.class); + entity.string = document.get("string", String.class); + entity.blob = document.get("blob", byte[].class); + return entity; } - uid = document.get("uid", UUID.class); - string = document.get("string", String.class); - blob = document.get("blob", byte[].class); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java index a546b91e2..51cc9b416 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java @@ -21,13 +21,10 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; @EqualsAndHashCode @ToString -class ClassB implements Comparable, Mappable { +class ClassB implements Comparable { @Getter @Setter private int number; @@ -47,16 +44,4 @@ public int compareTo(ClassB o) { return Integer.compare(number, o.number); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("number", number) - .put("text", text); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - number = document.get("number", Integer.class); - text = document.get("text", String.class); - } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java new file mode 100644 index 000000000..6d5f88bab --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassBConverter.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.data; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ClassBConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassB.class; + } + + @Override + public Document toDocument(ClassB entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("number", entity.getNumber()) + .put("text", entity.getText()); + } + + @Override + public ClassB fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassB entity = new ClassB(); + if (document.get("number") != null) { + entity.setNumber(document.get("number", Integer.class)); + } + entity.setText(document.get("text", String.class)); + return entity; + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java index 860fe1e93..0dd8d58d6 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassC.java @@ -22,12 +22,12 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; @EqualsAndHashCode @ToString -public class ClassC implements Mappable { +public class ClassC { @Getter @Setter private long id; @@ -46,21 +46,37 @@ public static ClassC create(int seed) { return classC; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("id", id) - .put("digit", digit) - .put("parent", parent != null ? parent.write(mapper) : null); - } + public static class ClassCConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return ClassC.class; + } + + @Override + public Document toDocument(ClassC entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("id", entity.id) + .put("digit", entity.digit) + .put("parent", nitriteMapper.convert(entity.parent, Document.class)); + } + + @Override + public ClassC fromDocument(Document document, NitriteMapper nitriteMapper) { + ClassC entity = new ClassC(); + if (document.get("id") != null) { + entity.id = document.get("id", Long.class); + } + + if (document.get("digit") != null) { + entity.digit = document.get("digit", Double.class); + } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - digit = document.get("digit", Double.class); - if (document.get("parent") != null) { - parent = new ClassA(); - parent.read(mapper, document.get("parent", Document.class)); + if (document.get("parent") != null) { + Document doc = document.get("parent", Document.class); + entity.parent = nitriteMapper.convert(doc, ClassA.class); + } + return entity; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Company.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Company.java index 944e17358..54c1acf94 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Company.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Company.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -39,7 +39,7 @@ @Indices({ @Index(value = "companyName") }) -public class Company implements Serializable, Mappable { +public class Company implements Serializable { @Id(fieldName = "company_id") @Getter @Setter @@ -61,25 +61,6 @@ public class Company implements Serializable, Mappable { @Setter private Map> employeeRecord; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("company_id", companyId) - .put("companyName", companyName) - .put("dateCreated", dateCreated) - .put("departments", departments) - .put("employeeRecord", employeeRecord); - } - - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - companyId = document.get("company_id", Long.class); - companyName = document.get("companyName", String.class); - dateCreated = document.get("dateCreated", Date.class); - departments = document.get("departments", List.class); - employeeRecord = document.get("employeeRecord", Map.class); - } - @Override public String toString() { return "Company{" + @@ -89,4 +70,32 @@ public String toString() { ", departments=" + departments + '}'; } + + public static class CompanyConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Company.class; + } + + @Override + public Document toDocument(Company entity, NitriteMapper nitriteMapper) { + return Document.createDocument("company_id", entity.companyId) + .put("companyName", entity.companyName) + .put("dateCreated", entity.dateCreated) + .put("departments", entity.departments) + .put("employeeRecord", entity.employeeRecord); + } + + @Override + public Company fromDocument(Document document, NitriteMapper nitriteMapper) { + Company entity = new Company(); + entity.companyId = document.get("company_id", Long.class); + entity.companyName = document.get("companyName", String.class); + entity.dateCreated = document.get("dateCreated", Date.class); + entity.departments = document.get("departments", List.class); + entity.employeeRecord = document.get("employeeRecord", Map.class); + return entity; + } + } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java index 26e0dcf4a..6d83c2940 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/DataGenerator.java @@ -19,6 +19,9 @@ import com.github.javafaker.Faker; import lombok.val; +import org.dizitart.no2.integration.repository.decorator.Manufacturer; +import org.dizitart.no2.integration.repository.decorator.Product; +import org.dizitart.no2.integration.repository.decorator.ProductId; import java.nio.charset.StandardCharsets; import java.util.*; @@ -102,6 +105,15 @@ public static Book randomBook() { return book; } + public static Product randomProduct() { + Product product = new Product(); + product.setProductName(faker.name().name()); + product.setProductId(randomProductId()); + product.setManufacturer(randomManufacturer()); + product.setPrice(Double.parseDouble(faker.commerce().price())); + return product; + } + private static List departments() { return new ArrayList() {{ add("dev"); @@ -114,4 +126,19 @@ private static List departments() { add("support"); }}; } + + private static ProductId randomProductId() { + ProductId productId = new ProductId(); + productId.setProductCode(faker.code().ean13()); + productId.setUniqueId(UUID.randomUUID().toString()); + return productId; + } + + private static Manufacturer randomManufacturer() { + Manufacturer manufacturer = new Manufacturer(); + manufacturer.setUniqueId(random.nextInt()); + manufacturer.setName(faker.name().name()); + manufacturer.setAddress(faker.address().fullAddress()); + return manufacturer; + } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java index cb5304e29..3ef376e44 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.ArrayList; @@ -29,38 +29,46 @@ * @author Anindya Chatterjee */ @Data -public class ElemMatch implements Mappable { +public class ElemMatch { private long id; private String[] strArray; private ProductScore[] productScores; - @Override - public Document write(NitriteMapper mapper) { - List list = new ArrayList<>(); - if (productScores != null) { - for (ProductScore productScore : productScores) { - Document document = productScore.write(mapper); - list.add(document); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ElemMatch.class; } - return Document.createDocument("id", id) - .put("strArray", strArray) - .put("productScores", list); - } + @Override + public Document toDocument(ElemMatch entity, NitriteMapper nitriteMapper) { + List list = new ArrayList<>(); + if (entity.productScores != null) { + for (ProductScore productScore : entity.productScores) { + Document document = nitriteMapper.convert(productScore, Document.class); + list.add(document); + } + } + + return Document.createDocument("id", entity.id) + .put("strArray", entity.strArray) + .put("productScores", list); + } - @Override - @SuppressWarnings("unchecked") - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - strArray = document.get("strArray", String[].class); - List list = document.get("productScores", List.class); - if (list != null) { - productScores = new ProductScore[list.size()]; - for (int i = 0; i < list.size(); i++) { - productScores[i] = new ProductScore(); - productScores[i].read(mapper, list.get(i)); + @Override + public ElemMatch fromDocument(Document document, NitriteMapper nitriteMapper) { + ElemMatch entity = new ElemMatch(); + entity.id = document.get("id", Long.class); + entity.strArray = document.get("strArray", String[].class); + List list = document.get("productScores", List.class); + if (list != null) { + entity.productScores = new ProductScore[list.size()]; + for (int i = 0; i < list.size(); i++) { + entity.productScores[i] = nitriteMapper.convert(list.get(i), ProductScore.class); + } } + return entity; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java index 7204f5654..dbba83965 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java @@ -22,9 +22,9 @@ import lombok.Setter; import lombok.ToString; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -39,7 +39,7 @@ @Index(value = "joinDate", type = IndexType.NON_UNIQUE) @Index(value = "address", type = IndexType.FULL_TEXT) @Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) -public class Employee implements Serializable, Mappable { +public class Employee implements Serializable { @Id @Getter @Setter @@ -82,28 +82,39 @@ public Employee(Employee copy) { emailAddress = copy.emailAddress; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("empId", empId) - .put("joinDate", joinDate) - .put("address", address) - .put("blob", blob) - .put("emailAddress", emailAddress) - .put("employeeNote", employeeNote != null ? employeeNote.write(mapper) : null); - } + public static class EmployeeConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Employee.class; + } + + @Override + public Document toDocument(Employee entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address) + .put("blob", entity.blob) + .put("emailAddress", entity.emailAddress) + .put("employeeNote", nitriteMapper.convert(entity.employeeNote, Document.class)); + } + + @Override + public Employee fromDocument(Document document, NitriteMapper nitriteMapper) { + Employee entity = new Employee(); + + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + entity.blob = document.get("blob", byte[].class); + entity.emailAddress = document.get("emailAddress", String.class); - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); - blob = document.get("blob", byte[].class); - emailAddress = document.get("emailAddress", String.class); - - if (document.get("employeeNote") != null) { - employeeNote = new Note(); - employeeNote.read(mapper, document.get("employeeNote", Document.class)); + if (document.get("employeeNote") != null) { + Document doc = document.get("employeeNote", Document.class); + entity.employeeNote = nitriteMapper.convert(doc, Note.class);; + } + return entity; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java index 8b9d4b23b..7003db015 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/EncryptedPerson.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Entity; @@ -30,25 +30,35 @@ */ @Data @Entity -public class EncryptedPerson implements Mappable { +public class EncryptedPerson { private String name; private String creditCardNumber; private String cvv; private Date expiryDate; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("creditCardNumber", creditCardNumber) - .put("cvv", cvv) - .put("expiryDate", expiryDate); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EncryptedPerson.class; + } + + @Override + public Document toDocument(EncryptedPerson entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("creditCardNumber", entity.creditCardNumber) + .put("cvv", entity.cvv) + .put("expiryDate", entity.expiryDate); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - creditCardNumber = document.get("creditCardNumber", String.class); - cvv = document.get("cvv", String.class); - expiryDate = document.get("expiryDate", Date.class); + @Override + public EncryptedPerson fromDocument(Document document, NitriteMapper nitriteMapper) { + EncryptedPerson entity = new EncryptedPerson(); + entity.name = document.get("name", String.class); + entity.creditCardNumber = document.get("creditCardNumber", String.class); + entity.cvv = document.get("cvv", String.class); + entity.expiryDate = document.get("expiryDate", Date.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Note.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Note.java index 67dc307d1..7bf7f1726 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Note.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Note.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.io.Serializable; @@ -30,7 +30,7 @@ * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class Note implements Serializable, Mappable { +public class Note implements Serializable { @Getter @Setter private Long noteId; @@ -38,14 +38,26 @@ public class Note implements Serializable, Mappable { @Setter private String text; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("noteId", noteId).put("text", text); - } + public static class NoteConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return Note.class; + } + + @Override + public Document toDocument(Note entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("noteId", entity.noteId) + .put("text", entity.text); + } - @Override - public void read(NitriteMapper mapper, Document document) { - noteId = document.get("noteId", Long.class); - text = document.get("text", String.class); + @Override + public Note fromDocument(Document document, NitriteMapper nitriteMapper) { + Note entity = new Note(); + entity.noteId = document.get("noteId", Long.class); + entity.text = document.get("text", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java index bc2514543..844e39223 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java @@ -19,8 +19,6 @@ import lombok.Getter; import lombok.Setter; -import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -36,18 +34,4 @@ public class ParentClass extends SuperDuperClass { @Id protected Long id; private Date date; - - @Override - public Document write(NitriteMapper mapper) { - return super.write(mapper) - .put("id", id) - .put("date", date); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - super.read(mapper, document); - id = document.get("id", Long.class); - date = document.get("date", Date.class); - } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java index 2b90075fa..b036118c7 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java @@ -19,9 +19,9 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Entity; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -37,7 +37,7 @@ @Index(value = "name", type = IndexType.FULL_TEXT), @Index(value = "status", type = IndexType.NON_UNIQUE) }) -public class PersonEntity implements Mappable { +public class PersonEntity { @Id private String uuid; private String name; @@ -56,24 +56,34 @@ public PersonEntity(String name) { this.dateCreated = new Date(); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("uuid", uuid) - .put("name", name) - .put("status", status) - .put("friend", friend != null ? friend.write(mapper) : null) - .put("dateCreated", dateCreated); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return PersonEntity.class; + } + + @Override + public Document toDocument(PersonEntity entity, NitriteMapper nitriteMapper) { + return Document.createDocument("uuid", entity.uuid) + .put("name", entity.name) + .put("status", entity.status) + .put("friend", entity.friend != null ? nitriteMapper.convert(entity.friend, Document.class) : null) + .put("dateCreated", entity.dateCreated); + } - @Override - public void read(NitriteMapper mapper, Document document) { - if (document != null) { - uuid = document.get("uuid", String.class); - name = document.get("name", String.class); - status = document.get("status", String.class); - dateCreated = document.get("dateCreated", Date.class); - friend = new PersonEntity(); - friend.read(mapper, document.get("friend", Document.class)); + @Override + public PersonEntity fromDocument(Document document, NitriteMapper nitriteMapper) { + if (document != null) { + PersonEntity entity = new PersonEntity(); + entity.uuid = document.get("uuid", String.class); + entity.name = document.get("name", String.class); + entity.status = document.get("status", String.class); + entity.dateCreated = document.get("dateCreated", Date.class); + entity.friend = nitriteMapper.convert(document.get("friend", Document.class), PersonEntity.class); + return entity; + } + return null; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java index 65550d182..9ebe19057 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,7 +28,7 @@ */ @Getter @Setter -public class ProductScore implements Mappable { +public class ProductScore { private String product; private int score; @@ -40,15 +40,25 @@ public ProductScore(String product, int score) { this.score = score; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("product", product) - .put("score", score); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return ProductScore.class; + } + + @Override + public Document toDocument(ProductScore entity, NitriteMapper nitriteMapper) { + return Document.createDocument("product", entity.product) + .put("score", entity.score); + } - @Override - public void read(NitriteMapper mapper, Document document) { - product = document.get("product", String.class); - score = document.get("score", Integer.class); + @Override + public ProductScore fromDocument(Document document, NitriteMapper nitriteMapper) { + ProductScore entity = new ProductScore(); + entity.product = document.get("product", String.class); + entity.score = document.get("score", Integer.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java index 1ecadb243..67f77060e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java @@ -19,9 +19,9 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.annotations.Index; /** @@ -31,22 +31,32 @@ @Index(value = "firstName") @Index(value = "age", type = IndexType.NON_UNIQUE) @Index(value = "lastName", type = IndexType.FULL_TEXT) -public class RepeatableIndexTest implements Mappable { +public class RepeatableIndexTest { private String firstName; private Integer age; private String lastName; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("firstName", firstName) - .put("age", age) - .put("lastName", lastName); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return RepeatableIndexTest.class; + } + + @Override + public Document toDocument(RepeatableIndexTest entity, NitriteMapper nitriteMapper) { + return Document.createDocument("firstName", entity.firstName) + .put("age", entity.age) + .put("lastName", entity.lastName); + } - @Override - public void read(NitriteMapper mapper, Document document) { - firstName = document.get("firstName", String.class); - age = document.get("age", Integer.class); - lastName = document.get("lastName", String.class); + @Override + public RepeatableIndexTest fromDocument(Document document, NitriteMapper nitriteMapper) { + RepeatableIndexTest entity = new RepeatableIndexTest(); + entity.firstName = document.get("firstName", String.class); + entity.age = document.get("age", Integer.class); + entity.lastName = document.get("lastName", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java index d1cb199c4..9e74e8fb0 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,28 +28,37 @@ */ @Getter @Setter -public class StressRecord implements Mappable { +public class StressRecord { private String firstName; private boolean processed; private String lastName; private boolean failed; private String notes; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument().put("firstName", firstName) - .put("processed", processed) - .put("lastName", lastName) - .put("failed", failed) - .put("notes", notes); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return StressRecord.class; + } + + @Override + public Document toDocument(StressRecord entity, NitriteMapper nitriteMapper) { + return Document.createDocument().put("firstName", entity.firstName) + .put("processed", entity.processed) + .put("lastName", entity.lastName) + .put("failed", entity.failed) + .put("notes", entity.notes); + } - @Override - public void read(NitriteMapper mapper, Document document) { - firstName = document.get("firstName", String.class); - processed = document.get("processed", Boolean.class); - lastName = document.get("lastName", String.class); - failed = document.get("failed", Boolean.class); - notes = document.get("notes", String.class); + @Override + public StressRecord fromDocument(Document document, NitriteMapper nitriteMapper) { + StressRecord entity = new StressRecord(); + entity.firstName = document.get("firstName", String.class); + entity.processed = document.get("processed", Boolean.class); + entity.lastName = document.get("lastName", String.class); + entity.failed = document.get("failed", Boolean.class); + entity.notes = document.get("notes", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java index 8eaa3faf1..ddea32812 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SubEmployee.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.Date; @@ -30,7 +30,7 @@ * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class SubEmployee implements Mappable { +public class SubEmployee { @Getter @Setter private Long empId; @@ -43,18 +43,28 @@ public class SubEmployee implements Mappable { @Setter private String address; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("empId", empId) - .put("joinDate", joinDate) - .put("address", address); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return SubEmployee.class; + } + + @Override + public Document toDocument(SubEmployee entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("empId", entity.empId) + .put("joinDate", entity.joinDate) + .put("address", entity.address); + } - @Override - public void read(NitriteMapper mapper, Document document) { - empId = document.get("empId", Long.class); - joinDate = document.get("joinDate", Date.class); - address = document.get("address", String.class); + @Override + public SubEmployee fromDocument(Document document, NitriteMapper nitriteMapper) { + SubEmployee entity = new SubEmployee(); + entity.empId = document.get("empId", Long.class); + entity.joinDate = document.get("joinDate", Date.class); + entity.address = document.get("address", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java index 198f16d0c..d39ddf146 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java @@ -19,10 +19,7 @@ import lombok.Getter; import lombok.Setter; -import org.dizitart.no2.collection.Document; import org.dizitart.no2.index.IndexType; -import org.dizitart.no2.common.mapper.Mappable; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Index; /** @@ -31,16 +28,6 @@ @Getter @Setter @Index(value = "text", type = IndexType.FULL_TEXT) -public class SuperDuperClass implements Mappable { +public class SuperDuperClass { private String text; - - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("text", text); - } - - @Override - public void read(NitriteMapper mapper, Document document) { - text = document.get("text", String.class); - } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java index 79cafe0dc..bdeef8744 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithClassField.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,20 +29,29 @@ */ @Getter @Setter -public class WithClassField implements Mappable { +public class WithClassField { @Id private String name; private Class clazz; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("clazz", clazz); - } + public static class Converter implements EntityConverter { + @Override + public Class getEntityType() { + return WithClassField.class; + } + + @Override + public Document toDocument(WithClassField entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("clazz", entity.clazz); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - clazz = document.get("clazz", Class.class); + @Override + public WithClassField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithClassField entity = new WithClassField(); + entity.name = document.get("name", String.class); + entity.clazz = document.get("clazz", Class.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java index 229f1aaf7..125e94b05 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithDateId.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import java.util.Date; @@ -32,19 +32,29 @@ @Getter @Setter @EqualsAndHashCode -public class WithDateId implements Mappable { +public class WithDateId { private Date id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("id", id); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithDateId.class; + } + + @Override + public Document toDocument(WithDateId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("id", entity.id); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - id = document.get("id", Date.class); + @Override + public WithDateId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithDateId entity = new WithDateId(); + entity.name = document.get("name", String.class); + entity.id = document.get("id", Date.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java index a0048a6bb..15792f12e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithEmptyStringId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,17 +29,27 @@ */ @Getter @Setter -public class WithEmptyStringId implements Mappable { +public class WithEmptyStringId { @Id private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithEmptyStringId.class; + } + + @Override + public Document toDocument(WithEmptyStringId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); + @Override + public WithEmptyStringId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithEmptyStringId entity = new WithEmptyStringId(); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java index 2364be76d..6748256c6 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNitriteId.java @@ -20,7 +20,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -28,21 +28,31 @@ * @author Anindya Chatterjee */ @Data -public class WithNitriteId implements Mappable { +public class WithNitriteId { @Id public NitriteId idField; public String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("idField", idField) - .put("name", name); - } + public static class WithNitriteIdConverter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithNitriteId.class; + } + + @Override + public Document toDocument(WithNitriteId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("idField", entity.idField) + .put("name", entity.name); + } - @Override - public void read(NitriteMapper mapper, Document document) { - idField = document.get("idField", NitriteId.class); - name = document.get("name", String.class); + @Override + public WithNitriteId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithNitriteId entity = new WithNitriteId(); + entity.idField = document.get("idField", NitriteId.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java index 0dc3fb68b..e5ca44b78 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,21 +29,31 @@ */ @Getter @Setter -public class WithNullId implements Mappable { +public class WithNullId { @Id private String name; private long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithNullId.class; + } + + @Override + public Document toDocument(WithNullId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithNullId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithNullId entity = new WithNullId(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java index 5f00f97d4..634a90fb8 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithObjectId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,17 +29,27 @@ */ @Getter @Setter -public class WithObjectId implements Mappable { +public class WithObjectId { @Id private WithOutId withOutId; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("withOutId", withOutId); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithObjectId.class; + } + + @Override + public Document toDocument(WithObjectId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("withOutId", entity.withOutId); + } - @Override - public void read(NitriteMapper mapper, Document document) { - withOutId = document.get("withOutId", WithOutId.class); + @Override + public WithObjectId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithObjectId entity = new WithObjectId(); + entity.withOutId = document.get("withOutId", WithOutId.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java index a7a2c56c9..2ce03bf24 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java @@ -19,14 +19,14 @@ import lombok.EqualsAndHashCode; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class WithOutGetterSetter implements Mappable { +public class WithOutGetterSetter { private String name; private long number; @@ -35,16 +35,25 @@ public WithOutGetterSetter() { number = 2; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); + public static class Converter implements EntityConverter { - } + @Override + public Class getEntityType() { + return WithOutGetterSetter.class; + } + + @Override + public Document toDocument(WithOutGetterSetter entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithOutGetterSetter fromDocument(Document document, NitriteMapper nitriteMapper) { + WithOutGetterSetter entity = new WithOutGetterSetter(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java index 09b63c2c2..645dd0b45 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** @@ -28,7 +28,7 @@ */ @Getter @Setter -public class WithOutId implements Comparable, Mappable { +public class WithOutId implements Comparable { private String name; private long number; @@ -37,16 +37,26 @@ public int compareTo(WithOutId o) { return Long.compare(number, o.number); } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public Class getEntityType() { + return WithOutId.class; + } + + @Override + public Document toDocument(WithOutId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("name", entity.name) + .put("number", entity.number); + } + + @Override + public WithOutId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithOutId entity = new WithOutId(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java index a425209fb..28febec9e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java @@ -19,14 +19,14 @@ import lombok.EqualsAndHashCode; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; /** * @author Anindya Chatterjee. */ @EqualsAndHashCode -public class WithPrivateConstructor implements Mappable { +public class WithPrivateConstructor { private String name; private long number; @@ -42,15 +42,24 @@ public static WithPrivateConstructor create(final String name, final long number return obj; } - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithPrivateConstructor.class; + } + + @Override + public Document toDocument(WithPrivateConstructor entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithPrivateConstructor fromDocument(Document document, NitriteMapper nitriteMapper) { + String name = document.get("name", String.class); + Long number = document.get("number", Long.class); + return WithPrivateConstructor.create(name, number); + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java index 793388231..eae21f1de 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java @@ -18,27 +18,37 @@ package org.dizitart.no2.integration.repository.data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; /** * @author Anindya Chatterjee. */ -public class WithPublicField implements Mappable { +public class WithPublicField { @Id public String name; public long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("name", name) - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithPublicField.class; + } + + @Override + public Document toDocument(WithPublicField entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.name) + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - name = document.get("name", String.class); - number = document.get("number", Long.class); + @Override + public WithPublicField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithPublicField entity = new WithPublicField(); + entity.name = document.get("name", String.class); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java index 9c1ade657..35dde5a91 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java @@ -20,7 +20,7 @@ import lombok.Getter; import lombok.Setter; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -29,19 +29,29 @@ */ @Getter @Setter -public class WithTransientField implements Mappable { +public class WithTransientField { private transient String name; @Id private long number; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("number", number); - } + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return WithTransientField.class; + } + + @Override + public Document toDocument(WithTransientField entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("number", entity.number); + } - @Override - public void read(NitriteMapper mapper, Document document) { - number = document.get("number", Long.class); + @Override + public WithTransientField fromDocument(Document document, NitriteMapper nitriteMapper) { + WithTransientField entity = new WithTransientField(); + entity.number = document.get("number", Long.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java index 9ecb9a68b..9c721c80c 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithoutEmbeddedId.java @@ -19,7 +19,7 @@ import lombok.Data; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -27,39 +27,60 @@ * @author Anindya Chatterjee */ @Data -public class WithoutEmbeddedId implements Mappable { +public class WithoutEmbeddedId { @Id private NestedId nestedId; private String data; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument() - .put("nestedId", nestedId.write(mapper)) - .put("data", data); - } + @Data + public static class NestedId { + private Long id; + + public static class Converter implements EntityConverter { - @Override - public void read(NitriteMapper mapper, Document document) { - Document nestedId = document.get("nestedId", Document.class); - this.nestedId = mapper.convert(nestedId, NestedId.class); - this.data = document.get("data", String.class); + @Override + public Class getEntityType() { + return NestedId.class; + } + + @Override + public Document toDocument(NestedId entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("id", entity.id); + } + + @Override + public NestedId fromDocument(Document document, NitriteMapper nitriteMapper) { + NestedId entity = new NestedId(); + entity.id = document.get("id", Long.class); + return entity; + } + } } + public static class Converter implements EntityConverter { - @Data - public static class NestedId implements Mappable { - private Long id; + @Override + public Class getEntityType() { + return WithoutEmbeddedId.class; + } @Override - public Document write(NitriteMapper mapper) { + public Document toDocument(WithoutEmbeddedId entity, NitriteMapper nitriteMapper) { return Document.createDocument() - .put("id", id); + .put("nestedId", nitriteMapper.convert(entity.nestedId, Document.class)) + .put("data", entity.data); } @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); + public WithoutEmbeddedId fromDocument(Document document, NitriteMapper nitriteMapper) { + WithoutEmbeddedId entity = new WithoutEmbeddedId(); + Document nestedId = document.get("nestedId", Document.class); + + entity.nestedId = nitriteMapper.convert(nestedId, NestedId.class); + entity.data = document.get("data", String.class); + + return entity; } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java new file mode 100644 index 000000000..c6bfe8bb9 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/Manufacturer.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; + +@Data +public class Manufacturer { + private String name; + private String address; + private Integer uniqueId; +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java new file mode 100644 index 000000000..f31d7b0bb --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerConverter.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ManufacturerConverter implements EntityConverter { + @Override + public Class getEntityType() { + return Manufacturer.class; + } + + @Override + public Document toDocument(Manufacturer entity, NitriteMapper nitriteMapper) { + return Document.createDocument("name", entity.getName()) + .put("address", entity.getAddress()) + .put("uniqueId", entity.getUniqueId()); + } + + @Override + public Manufacturer fromDocument(Document document, NitriteMapper nitriteMapper) { + Manufacturer manufacturer = new Manufacturer(); + manufacturer.setName(document.get("name", String.class)); + manufacturer.setAddress(document.get("address", String.class)); + manufacturer.setUniqueId(document.get("uniqueId", Integer.class)); + return manufacturer; + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java new file mode 100644 index 000000000..8d04904f9 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityDecorator; +import org.dizitart.no2.repository.EntityId; + +import java.util.List; + +public class ManufacturerDecorator implements EntityDecorator { + @Override + public Class getEntityType() { + return Manufacturer.class; + } + + @Override + public EntityId getIdField() { + return null; + } + + @Override + public List getIndexFields() { + return null; + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java new file mode 100644 index 000000000..89b332c8a --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/MiniProduct.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +@Data +public class MiniProduct { + private String uniqueId; + private String manufacturerName; + private Double price; + + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return MiniProduct.class; + } + + @Override + public Document toDocument(MiniProduct entity, NitriteMapper nitriteMapper) { + return Document.createDocument() + .put("productId.uniqueId", entity.getUniqueId()) + .put("manufacturer.name", entity.getManufacturerName()) + .put("price", entity.getPrice()); + } + + @Override + public MiniProduct fromDocument(Document document, NitriteMapper nitriteMapper) { + MiniProduct entity = new MiniProduct(); + entity.setUniqueId(document.get("productId.uniqueId", String.class)); + entity.setManufacturerName(document.get("manufacturer.name", String.class)); + entity.setPrice(document.get("price", Double.class)); + return entity; + } + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java new file mode 100644 index 000000000..ff59219f9 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/Product.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; + +@Data +public class Product { + private ProductId productId; + private Manufacturer manufacturer; + private String productName; + private Double price; +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java new file mode 100644 index 000000000..0421c0cac --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductConverter.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ProductConverter implements EntityConverter { + @Override + public Class getEntityType() { + return Product.class; + } + + @Override + public Document toDocument(Product entity, NitriteMapper nitriteMapper) { + Document productId = nitriteMapper.convert(entity.getProductId(), Document.class); + Document manufacturer = nitriteMapper.convert(entity.getManufacturer(), Document.class); + + return Document.createDocument() + .put("productId", productId) + .put("manufacturer", manufacturer) + .put("productName", entity.getProductName()) + .put("price", entity.getPrice()); + } + + @Override + public Product fromDocument(Document document, NitriteMapper nitriteMapper) { + Product entity = new Product(); + ProductId productId = nitriteMapper.convert(document.get("productId", Document.class), ProductId.class); + Manufacturer manufacturer = nitriteMapper.convert(document.get("manufacturer", Document.class), + Manufacturer.class); + entity.setProductId(productId); + entity.setManufacturer(manufacturer); + entity.setProductName(document.get("productName", String.class)); + entity.setPrice(document.get("price", Double.class)); + return entity; + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java new file mode 100644 index 000000000..301dd8909 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.repository.EntityDecorator; +import org.dizitart.no2.repository.EntityId; + +import java.util.Arrays; +import java.util.List; + +public class ProductDecorator implements EntityDecorator { + @Override + public Class getEntityType() { + return Product.class; + } + + @Override + public EntityId getIdField() { + return new EntityId("productId", "uniqueId", "productCode"); + } + + @Override + public List getIndexFields() { + return Arrays.asList( + IndexFields.create(IndexType.NON_UNIQUE, "manufacturer.name"), + IndexFields.create(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") + ); + } + + @Override + public String getEntityName() { + return "product"; + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java new file mode 100644 index 000000000..dd2f9f04e --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductId.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import lombok.Data; + +@Data +public class ProductId { + private String uniqueId; + private String productCode; +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java new file mode 100644 index 000000000..2c02fddc0 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductIdConverter.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.decorator; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class ProductIdConverter implements EntityConverter { + @Override + public Class getEntityType() { + return ProductId.class; + } + + @Override + public Document toDocument(ProductId entity, NitriteMapper nitriteMapper) { + return Document.createDocument("uniqueId", entity.getUniqueId()) + .put("productCode", entity.getProductCode()); + } + + @Override + public ProductId fromDocument(Document document, NitriteMapper nitriteMapper) { + ProductId entity = new ProductId(); + entity.setUniqueId(document.get("uniqueId", String.class)); + entity.setProductCode(document.get("productCode", String.class)); + return entity; + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TxData.java b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TxData.java index 1e5ed7ca6..d9dd2d334 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TxData.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/transaction/TxData.java @@ -21,7 +21,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.dizitart.no2.collection.Document; -import org.dizitart.no2.common.mapper.Mappable; +import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.repository.annotations.Id; @@ -31,20 +31,31 @@ @Data @NoArgsConstructor @AllArgsConstructor -class TxData implements Mappable { +public class TxData { @Id private Long id; private String name; - @Override - public Document write(NitriteMapper mapper) { - return Document.createDocument("id", id) - .put("name", name); - } - @Override - public void read(NitriteMapper mapper, Document document) { - id = document.get("id", Long.class); - name = document.get("name", String.class); + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return TxData.class; + } + + @Override + public Document toDocument(TxData entity, NitriteMapper nitriteMapper) { + return Document.createDocument("id", entity.id) + .put("name", entity.name); + } + + @Override + public TxData fromDocument(Document document, NitriteMapper nitriteMapper) { + TxData entity = new TxData(); + entity.id = document.get("id", Long.class); + entity.name = document.get("name", String.class); + return entity; + } } } diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorReaderTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorReaderTest.java new file mode 100644 index 000000000..e01882365 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorReaderTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.repository; + +import lombok.Data; +import org.dizitart.no2.Nitrite; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.mapper.NitriteMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; +import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.integration.repository.data.ClassA; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.*; + +public class EntityDecoratorReaderTest { + + private EntityDecoratorReader reader; + private NitriteCollection collection; + + @Before + public void setUp() { + NitriteMapper nitriteMapper = new SimpleDocumentMapper(); + collection = Nitrite.builder().openOrCreate().getCollection("test"); + reader = new EntityDecoratorReader(new EntityADecorator(), collection, nitriteMapper); + } + + @Test + public void testReadEntity() { + assertNull(reader.getObjectIdField()); + assertTrue(reader.getIndices().isEmpty()); + + reader.readEntity(); + assertNotNull(reader.getObjectIdField()); + assertFalse(reader.getIndices().isEmpty()); + + ObjectIdField idField = reader.getObjectIdField(); + assertEquals(idField.getIdFieldName(), "id"); + assertArrayEquals(idField.getFieldNames(), new String[] {"uid", "string"}); + assertArrayEquals(idField.getEmbeddedFieldNames(), new String[] {"id.uid", "id.string"}); + + Set indexFields = reader.getIndices(); + assertEquals(indexFields.size(), 2); + } + + @Test + public void testCreateIndices() { + assertFalse(collection.hasIndex("name", "age")); + assertFalse(collection.hasIndex("address")); + assertFalse(collection.hasIndex("id.uid", "id.string")); + + reader.readEntity(); + reader.createIndices(); + + assertTrue(collection.hasIndex("name", "age")); + assertTrue(collection.hasIndex("address")); + assertFalse(collection.hasIndex("id.uid", "id.string")); + } + + @Test + public void testCreateIdIndex() { + assertNull(reader.getObjectIdField()); + assertFalse(collection.hasIndex("name", "age")); + assertFalse(collection.hasIndex("address")); + assertFalse(collection.hasIndex("id.uid", "id.string")); + + reader.readEntity(); + reader.createIdIndex(); + + assertNotNull(reader.getObjectIdField()); + assertFalse(collection.hasIndex("name", "age")); + assertFalse(collection.hasIndex("address")); + assertTrue(collection.hasIndex("id.uid", "id.string")); + } + + @Data + public static class EntityA { + private ClassA id; + private String name; + private String age; + private String address; + } + + public static class EntityADecorator implements EntityDecorator { + + @Override + public Class getEntityType() { + return EntityA.class; + } + + @Override + public EntityId getIdField() { + return new EntityId("id", "uid", "string"); + } + + @Override + public List getIndexFields() { + List list = new ArrayList<>(); + IndexFields fieldsA = IndexFields.create(IndexType.UNIQUE, "name", "age"); + IndexFields fieldsB = IndexFields.create(IndexType.NON_UNIQUE, "address"); + list.add(fieldsA); + list.add(fieldsB); + return list; + } + + @Override + public String getEntityName() { + return "a"; + } + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/IndexValidatorTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/IndexValidatorTest.java index 0c812ed53..7fd054b15 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/IndexValidatorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/IndexValidatorTest.java @@ -29,7 +29,7 @@ public class IndexValidatorTest { @Test public void testValidate() { - IndexValidator indexValidator = new IndexValidator(new Reflector()); + IndexValidator indexValidator = new IndexValidator(); Class fieldType = Object.class; assertThrows(IndexingException.class, () -> indexValidator.validate(fieldType, "Field", new NitriteBuilderTest.CustomNitriteMapper())); @@ -37,7 +37,7 @@ public void testValidate() { @Test public void testValidate3() { - IndexValidator indexValidator = new IndexValidator(new Reflector()); + IndexValidator indexValidator = new IndexValidator(); Class fieldType = Field.class; assertThrows(IndexingException.class, () -> indexValidator.validate(fieldType, "Field", new NitriteBuilderTest.CustomNitriteMapper())); @@ -45,7 +45,7 @@ public void testValidate3() { @Test public void testValidate4() { - IndexValidator indexValidator = new IndexValidator(new Reflector()); + IndexValidator indexValidator = new IndexValidator(); Class fieldType = Object.class; assertThrows(IndexingException.class, () -> indexValidator.validate(fieldType, "invalid type specified ", new NitriteBuilderTest.CustomNitriteMapper())); diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java index 5fcb74699..8f8957ccc 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java @@ -23,7 +23,7 @@ import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.Lookup; import org.dizitart.no2.common.RecordStream; -import org.dizitart.no2.common.mapper.MappableMapper; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.common.processors.ProcessorChain; import org.dizitart.no2.common.streams.DocumentStream; import org.dizitart.no2.common.streams.MutatedObjectStream; @@ -83,7 +83,7 @@ public void testProject() { public void testProject3() { Class forNameResult = Object.class; Class forNameResult1 = Object.class; - MappableMapper nitriteMapper = new MappableMapper(forNameResult, forNameResult1, Object.class); + SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); RecordStream> recordStream = (RecordStream>) mock( RecordStream.class); DocumentStream cursor = new DocumentStream(recordStream, new ProcessorChain()); diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryFactoryTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryFactoryTest.java index 27eeed486..09105f987 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryFactoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryFactoryTest.java @@ -21,8 +21,12 @@ import org.dizitart.no2.collection.CollectionFactory; import org.dizitart.no2.common.concurrent.LockService; import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.index.IndexFields; import org.junit.Test; +import java.util.Date; +import java.util.List; + import static org.junit.Assert.assertThrows; public class RepositoryFactoryTest { @@ -36,7 +40,7 @@ public void testGetRepository() { @Test public void testGetRepository2() { RepositoryFactory repositoryFactory = new RepositoryFactory(new CollectionFactory(new LockService())); - assertThrows(ValidationException.class, () -> repositoryFactory.getRepository(new NitriteConfig(), null)); + assertThrows(ValidationException.class, () -> repositoryFactory.getRepository(new NitriteConfig(), (Class) null)); } @Test @@ -49,7 +53,39 @@ public void testGetRepository3() { public void testGetRepository4() { RepositoryFactory repositoryFactory = new RepositoryFactory(new CollectionFactory(new LockService())); assertThrows(ValidationException.class, - () -> repositoryFactory.getRepository(new NitriteConfig(), null, "Key")); + () -> repositoryFactory.getRepository(new NitriteConfig(), (Class) null, "Key")); + } + + @Test + public void testGetRepository5() { + RepositoryFactory repositoryFactory = new RepositoryFactory(new CollectionFactory(new LockService())); + assertThrows(ValidationException.class, + () -> repositoryFactory.getRepository(new NitriteConfig(), (EntityDecorator) null, "Key")); + } + + @Test + public void testGetRepository6() { + RepositoryFactory repositoryFactory = new RepositoryFactory(new CollectionFactory(new LockService())); + assertThrows(ValidationException.class, + () -> repositoryFactory.getRepository(null, new DemoDecorator(), "Key")); + } + + public static class DemoDecorator implements EntityDecorator { + + @Override + public Class getEntityType() { + return Date.class; + } + + @Override + public EntityId getIdField() { + return null; + } + + @Override + public List getIndexFields() { + return null; + } } } diff --git a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/KNO2JacksonMapper.kt b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/KNO2JacksonMapper.kt index 308ac61d1..cf823a79b 100644 --- a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/KNO2JacksonMapper.kt +++ b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/KNO2JacksonMapper.kt @@ -16,20 +16,14 @@ package org.dizitart.kno2 +import com.fasterxml.jackson.databind.Module import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.datatype.jdk8.Jdk8Module import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.fasterxml.jackson.module.kotlin.KotlinModule import org.dizitart.no2.common.mapper.JacksonMapper -import org.dizitart.no2.common.mapper.JacksonExtension -import org.dizitart.no2.spatial.mapper.GeometryExtension -import java.time.ZoneId -import java.time.chrono.ChronoPeriod -import java.time.temporal.Temporal -import java.time.temporal.TemporalAdjuster -import java.time.temporal.TemporalAmount -import java.util.* +import org.dizitart.no2.spatial.mapper.GeometryModule /** * Default [JacksonMapper] for potassium nitrite. @@ -38,25 +32,18 @@ import java.util.* * @author Stefan Mandel * @since 2.1.0 */ -open class KNO2JacksonMapper(vararg extensions: JacksonExtension) : JacksonMapper(GeometryExtension(), *extensions) { +open class KNO2JacksonMapper(private vararg val modules: Module) : JacksonMapper() { - override fun createObjectMapper(): ObjectMapper { - // add value types from JavaTimeModule - addValueType(Temporal::class.java) - addValueType(TemporalAdjuster::class.java) - addValueType(TemporalAmount::class.java) - addValueType(ChronoPeriod::class.java) - addValueType(ZoneId::class.java) - - // add value types from Jdk8Module - addValueType(OptionalInt::class.java) - addValueType(OptionalLong::class.java) - addValueType(OptionalDouble::class.java) - - val objectMapper = super.createObjectMapper() + override fun getObjectMapper(): ObjectMapper { + val objectMapper = super.getObjectMapper() objectMapper.registerModule(KotlinModule.Builder().build()) objectMapper.registerModule(Jdk8Module()) objectMapper.registerModule(JavaTimeModule()) + objectMapper.registerModule(GeometryModule()) + for (module in modules) { + objectMapper.registerModule(module) + } + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) return objectMapper diff --git a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/KNO2Module.kt b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/KNO2Module.kt index c7cce31f1..247a53775 100644 --- a/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/KNO2Module.kt +++ b/potassium-nitrite/src/main/kotlin/org/dizitart/kno2/KNO2Module.kt @@ -16,8 +16,8 @@ package org.dizitart.kno2 +import com.fasterxml.jackson.databind.Module import org.dizitart.no2.common.util.Iterables.setOf -import org.dizitart.no2.common.mapper.JacksonExtension import org.dizitart.no2.common.module.NitriteModule import org.dizitart.no2.common.module.NitritePlugin import org.dizitart.no2.spatial.SpatialIndexer @@ -26,7 +26,7 @@ import org.dizitart.no2.spatial.SpatialIndexer * * @author Anindya Chatterjee */ -open class KNO2Module(private vararg val extensions: JacksonExtension) : NitriteModule { +open class KNO2Module(private vararg val extensions: Module) : NitriteModule { override fun plugins(): MutableSet { return setOf(KNO2JacksonMapper(*extensions), SpatialIndexer()) diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt index ef8aef206..9bd9eda24 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt @@ -23,7 +23,6 @@ import com.fasterxml.jackson.databind.module.SimpleModule import org.dizitart.no2.index.IndexType import org.dizitart.no2.repository.annotations.Id import org.dizitart.no2.repository.annotations.Index -import org.dizitart.no2.common.mapper.JacksonExtension import org.dizitart.no2.mvstore.MVStoreModule import org.junit.Test import org.threeten.bp.LocalDateTime @@ -45,36 +44,28 @@ class BackportJavaTimeTest { val time: LocalDateTime ) - class ThreeTenAbpExtension : JacksonExtension { - override fun getModule(): Module { - return object : SimpleModule() { - override fun setupModule(context: SetupContext?) { - addDeserializer(LocalDateTime::class.java, object : JsonDeserializer() { - override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): LocalDateTime? { - val timeStamp = p?.longValue - return if (timeStamp == -1L || timeStamp == null) null else { - LocalDateTime.ofEpochSecond(timeStamp, 0, ZoneOffset.UTC) - } - } - }) - - addSerializer(LocalDateTime::class.java, object : JsonSerializer() { - override fun serialize(value: LocalDateTime?, gen: JsonGenerator?, serializers: SerializerProvider?) { - if (value == null) { - gen?.writeNull() - } else { - val timeStamp = value.toEpochSecond(ZoneOffset.UTC) - gen?.writeNumber(timeStamp) - } - } - }) - super.setupModule(context) + class ThreeTenAbpModule : SimpleModule() { + override fun setupModule(context: SetupContext?) { + addDeserializer(LocalDateTime::class.java, object : JsonDeserializer() { + override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): LocalDateTime? { + val timeStamp = p?.longValue + return if (timeStamp == -1L || timeStamp == null) null else { + LocalDateTime.ofEpochSecond(timeStamp, 0, ZoneOffset.UTC) + } } - } - } + }) - override fun getSupportedTypes(): List> { - return listOf(LocalDateTime::class.java) + addSerializer(LocalDateTime::class.java, object : JsonSerializer() { + override fun serialize(value: LocalDateTime?, gen: JsonGenerator?, serializers: SerializerProvider?) { + if (value == null) { + gen?.writeNull() + } else { + val timeStamp = value.toEpochSecond(ZoneOffset.UTC) + gen?.writeNumber(timeStamp) + } + } + }) + super.setupModule(context) } } @@ -82,7 +73,7 @@ class BackportJavaTimeTest { fun testIssue59() { val db = nitrite { loadModule(MVStoreModule(dbPath)) - loadModule(KNO2Module(ThreeTenAbpExtension())) + loadModule(KNO2Module(ThreeTenAbpModule())) } val repo = db.getRepository() From 1061b291bc185ddce9703947109e075b228cf866 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Mon, 10 Oct 2022 22:41:11 +0530 Subject: [PATCH 64/78] refactoring --- .../decorator/ManufacturerDecorator.java | 4 +- .../decorator/ProductDecorator.java | 8 ++-- .../decorator/ManufacturerDecorator.java | 4 +- .../decorator/ProductDecorator.java | 8 ++-- .../no2/common/mapper/EntityConverter.java | 27 ++++++++++++ .../no2/repository/EntityDecorator.java | 36 ++++++++++++++-- ...eader.java => EntityDecoratorScanner.java} | 15 ++++--- .../EntityIndex.java} | 41 +++++-------------- .../no2/repository/RepositoryOperations.java | 14 +++---- ...exFieldsTest.java => EntityIndexTest.java} | 27 ++++-------- .../decorator/ManufacturerDecorator.java | 4 +- .../decorator/ProductDecorator.java | 8 ++-- ...t.java => EntityDecoratorScannerTest.java} | 19 ++++----- .../no2/repository/RepositoryFactoryTest.java | 3 +- 14 files changed, 120 insertions(+), 98 deletions(-) rename nitrite/src/main/java/org/dizitart/no2/repository/{EntityDecoratorReader.java => EntityDecoratorScanner.java} (90%) rename nitrite/src/main/java/org/dizitart/no2/{index/IndexFields.java => repository/EntityIndex.java} (56%) rename nitrite/src/test/java/org/dizitart/no2/index/{IndexFieldsTest.java => EntityIndexTest.java} (55%) rename nitrite/src/test/java/org/dizitart/no2/repository/{EntityDecoratorReaderTest.java => EntityDecoratorScannerTest.java} (85%) diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java index 8d04904f9..8d066491a 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java @@ -17,7 +17,7 @@ package org.dizitart.no2.integration.repository.decorator; -import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityIndex; import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.repository.EntityId; @@ -35,7 +35,7 @@ public EntityId getIdField() { } @Override - public List getIndexFields() { + public List getIndexFields() { return null; } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java index 301dd8909..180296343 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java @@ -17,7 +17,7 @@ package org.dizitart.no2.integration.repository.decorator; -import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityIndex; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.repository.EntityId; @@ -37,10 +37,10 @@ public EntityId getIdField() { } @Override - public List getIndexFields() { + public List getIndexFields() { return Arrays.asList( - IndexFields.create(IndexType.NON_UNIQUE, "manufacturer.name"), - IndexFields.create(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") + new EntityIndex(IndexType.NON_UNIQUE, "manufacturer.name"), + new EntityIndex(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") ); } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java index 8d04904f9..8d066491a 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java @@ -17,7 +17,7 @@ package org.dizitart.no2.integration.repository.decorator; -import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityIndex; import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.repository.EntityId; @@ -35,7 +35,7 @@ public EntityId getIdField() { } @Override - public List getIndexFields() { + public List getIndexFields() { return null; } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java index 301dd8909..180296343 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java @@ -17,7 +17,7 @@ package org.dizitart.no2.integration.repository.decorator; -import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityIndex; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.repository.EntityId; @@ -37,10 +37,10 @@ public EntityId getIdField() { } @Override - public List getIndexFields() { + public List getIndexFields() { return Arrays.asList( - IndexFields.create(IndexType.NON_UNIQUE, "manufacturer.name"), - IndexFields.create(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") + new EntityIndex(IndexType.NON_UNIQUE, "manufacturer.name"), + new EntityIndex(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") ); } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/EntityConverter.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/EntityConverter.java index ca14ab4c0..f140cede0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/EntityConverter.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/EntityConverter.java @@ -19,10 +19,37 @@ import org.dizitart.no2.collection.Document; +/** + * A class that implements this interface can be used to convert + * entity into a database {@link Document} and back again. + * + * @since 4.0 + * @author Anindya Chatterjee + * @param the type parameter + */ public interface EntityConverter { + /** + * Gets the entity type. + * + * @return the entity type + */ Class getEntityType(); + /** + * Converts the entity to a {@link Document}. + * + * @param entity the entity + * @param nitriteMapper the nitrite mapper + * @return the document + */ Document toDocument(T entity, NitriteMapper nitriteMapper); + /** + * Converts a {@link Document} to an entity of type {@link T}. + * + * @param document the document + * @param nitriteMapper the nitrite mapper + * @return the t + */ T fromDocument(Document document, NitriteMapper nitriteMapper); } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecorator.java b/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecorator.java index 5f2dbfe23..fee8de619 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecorator.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecorator.java @@ -17,17 +17,47 @@ package org.dizitart.no2.repository; -import org.dizitart.no2.index.IndexFields; - import java.util.List; +/** + * A class that implements this interface can be used to decorate + * an entity of type T for nitrite database where using + * {@link org.dizitart.no2.repository.annotations.Entity} + * or its related annotations is not possible on a class. + * + * @param the type parameter + * @see org.dizitart.no2.Nitrite#getRepository(EntityDecorator) + * @see org.dizitart.no2.Nitrite#getRepository(EntityDecorator, String) + * @since 4.0 + * @author Anindya Chatterjee + */ public interface EntityDecorator { + /** + * Gets the entity type of the decorator. + * + * @return the entity type + */ Class getEntityType(); + /** + * Gets id field declaration. + * + * @return the id field + */ EntityId getIdField(); - List getIndexFields(); + /** + * Gets index fields declaration. + * + * @return the index fields + */ + List getIndexFields(); + /** + * Gets entity name. + * + * @return the entity name + */ default String getEntityName() { return getEntityType().getName(); } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorReader.java b/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorScanner.java similarity index 90% rename from nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorReader.java rename to nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorScanner.java index 5b1db48f4..46667e643 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorReader.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/EntityDecoratorScanner.java @@ -22,7 +22,6 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.util.StringUtils; -import org.dizitart.no2.index.IndexFields; import java.lang.reflect.Field; import java.util.ArrayList; @@ -35,7 +34,7 @@ /** * @author Anindya Chatterjee */ -public class EntityDecoratorReader { +public class EntityDecoratorScanner { private final EntityDecorator entityDecorator; private final NitriteCollection collection; private final NitriteMapper nitriteMapper; @@ -44,14 +43,14 @@ public class EntityDecoratorReader { private final Reflector reflector; @Getter(AccessLevel.PACKAGE) - private final Set indices; + private final Set indices; @Getter private ObjectIdField objectIdField; - public EntityDecoratorReader(EntityDecorator entityDecorator, - NitriteCollection collection, - NitriteMapper nitriteMapper) { + public EntityDecoratorScanner(EntityDecorator entityDecorator, + NitriteCollection collection, + NitriteMapper nitriteMapper) { this.entityDecorator = entityDecorator; this.collection = collection; this.nitriteMapper = nitriteMapper; @@ -66,7 +65,7 @@ public void readEntity() { } public void createIndices() { - for (IndexFields index : indices) { + for (EntityIndex index : indices) { String[] fields = index.getFieldNames().toArray(new String[0]); if (!collection.hasIndex(fields)) { collection.createIndex(indexOptions(index.getIndexType()), fields); @@ -85,7 +84,7 @@ public void createIdIndex() { private void readIndices() { if (entityDecorator.getIndexFields() != null) { - for (IndexFields indexField : entityDecorator.getIndexFields()) { + for (EntityIndex indexField : entityDecorator.getIndexFields()) { List names = indexField.getFieldNames(); List entityFields = new ArrayList<>(); diff --git a/nitrite/src/main/java/org/dizitart/no2/index/IndexFields.java b/nitrite/src/main/java/org/dizitart/no2/repository/EntityIndex.java similarity index 56% rename from nitrite/src/main/java/org/dizitart/no2/index/IndexFields.java rename to nitrite/src/main/java/org/dizitart/no2/repository/EntityIndex.java index 5ae7bdc12..1342bfd36 100644 --- a/nitrite/src/main/java/org/dizitart/no2/index/IndexFields.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/EntityIndex.java @@ -15,14 +15,14 @@ * */ -package org.dizitart.no2.index; +package org.dizitart.no2.repository; -import lombok.EqualsAndHashCode; import lombok.Getter; -import org.dizitart.no2.common.Fields; import org.dizitart.no2.common.util.StringUtils; +import org.dizitart.no2.index.IndexType; import java.util.Arrays; +import java.util.List; import static org.dizitart.no2.common.util.ValidationUtils.notEmpty; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -33,24 +33,15 @@ * @author Anindya Chatterjee * @since 4.0 */ -@EqualsAndHashCode(callSuper = true) -public class IndexFields extends Fields { - private static final long serialVersionUID = 1662219840L; +public class EntityIndex { - /** - * The index type. - */ @Getter private String indexType; - /** - * Creates a {@link IndexFields} instance with field names and index type. - * - * @param indexType the index type - * @param fields the fields - * @return the fields - */ - public static IndexFields create(String indexType, String... fields) { + @Getter + private List fieldNames; + + public EntityIndex(String indexType, String... fields) { notNull(fields, "fields cannot be null"); notEmpty(fields, "fields cannot be empty"); @@ -58,19 +49,7 @@ public static IndexFields create(String indexType, String... fields) { indexType = IndexType.UNIQUE; } - IndexFields f = new IndexFields(); - f.fieldNames.addAll(Arrays.asList(fields)); - f.indexType = indexType; - return f; - } - - @Override - public String getEncodedName() { - return indexType + "[" + super.getEncodedName() + "]"; - } - - @Override - public String toString() { - return this.getEncodedName(); + this.fieldNames = Arrays.asList(fields); + this.indexType = StringUtils.isNullOrEmpty(indexType) ? IndexType.UNIQUE : indexType; } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java index 9cb9798d8..f25a25d9e 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryOperations.java @@ -47,7 +47,7 @@ public class RepositoryOperations { private final Class type; private AnnotationScanner annotationScanner; private ObjectIdField objectIdField; - private EntityDecoratorReader entityDecoratorReader; + private EntityDecoratorScanner entityDecoratorScanner; /** * Instantiates a new {@link RepositoryOperations}. @@ -72,7 +72,7 @@ public RepositoryOperations(EntityDecorator entityDecorator, this.type = entityDecorator.getEntityType(); this.nitriteMapper = nitriteMapper; this.collection = collection; - this.entityDecoratorReader = new EntityDecoratorReader(entityDecorator, collection, nitriteMapper); + this.entityDecoratorScanner = new EntityDecoratorScanner(entityDecorator, collection, nitriteMapper); validateCollection(); } @@ -85,11 +85,11 @@ public void createIndices() { annotationScanner.createIndices(); annotationScanner.createIdIndex(); objectIdField = annotationScanner.getObjectIdField(); - } else if (entityDecoratorReader != null) { - entityDecoratorReader.readEntity(); - entityDecoratorReader.createIndices(); - entityDecoratorReader.createIdIndex(); - objectIdField = entityDecoratorReader.getObjectIdField(); + } else if (entityDecoratorScanner != null) { + entityDecoratorScanner.readEntity(); + entityDecoratorScanner.createIndices(); + entityDecoratorScanner.createIdIndex(); + objectIdField = entityDecoratorScanner.getObjectIdField(); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/index/IndexFieldsTest.java b/nitrite/src/test/java/org/dizitart/no2/index/EntityIndexTest.java similarity index 55% rename from nitrite/src/test/java/org/dizitart/no2/index/IndexFieldsTest.java rename to nitrite/src/test/java/org/dizitart/no2/index/EntityIndexTest.java index a02015b9a..f58c9f23b 100644 --- a/nitrite/src/test/java/org/dizitart/no2/index/IndexFieldsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/index/EntityIndexTest.java @@ -18,40 +18,29 @@ package org.dizitart.no2.index; import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.repository.EntityIndex; import org.junit.Test; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -public class IndexFieldsTest { +public class EntityIndexTest { @Test public void testIndextype() { - IndexFields indexFields = IndexFields.create(IndexType.UNIQUE, "a", "b"); - assertEquals(indexFields.getIndexType(), IndexType.UNIQUE); + EntityIndex entityIndex = new EntityIndex(IndexType.UNIQUE, "a", "b"); + assertEquals(entityIndex.getIndexType(), IndexType.UNIQUE); } @Test public void testGetFields() { - IndexFields indexFields = IndexFields.create(IndexType.UNIQUE, "a", "b"); - assertArrayEquals(indexFields.getFieldNames().toArray(), new String[] {"a", "b"}); + EntityIndex entityIndex = new EntityIndex(IndexType.UNIQUE, "a", "b"); + assertArrayEquals(entityIndex.getFieldNames().toArray(), new String[] {"a", "b"}); } @Test(expected = ValidationException.class) public void testGetFieldsWithoutFields() { - IndexFields indexFields = IndexFields.create(IndexType.UNIQUE); - assertArrayEquals(indexFields.getFieldNames().toArray(), new String[0]); - } - - @Test - public void testGetEncodedName() { - IndexFields indexFields = IndexFields.create(IndexType.UNIQUE, "a", "b"); - assertEquals("Unique[a|b]", indexFields.getEncodedName()); - } - - @Test - public void testToString() { - IndexFields indexFields = IndexFields.create(IndexType.UNIQUE, "a", "b"); - assertEquals("Unique[a|b]", indexFields.toString()); + EntityIndex entityIndex = new EntityIndex(IndexType.UNIQUE); + assertArrayEquals(entityIndex.getFieldNames().toArray(), new String[0]); } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java index 8d04904f9..8d066491a 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ManufacturerDecorator.java @@ -17,7 +17,7 @@ package org.dizitart.no2.integration.repository.decorator; -import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityIndex; import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.repository.EntityId; @@ -35,7 +35,7 @@ public EntityId getIdField() { } @Override - public List getIndexFields() { + public List getIndexFields() { return null; } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java index 301dd8909..180296343 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/decorator/ProductDecorator.java @@ -17,7 +17,7 @@ package org.dizitart.no2.integration.repository.decorator; -import org.dizitart.no2.index.IndexFields; +import org.dizitart.no2.repository.EntityIndex; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.repository.EntityDecorator; import org.dizitart.no2.repository.EntityId; @@ -37,10 +37,10 @@ public EntityId getIdField() { } @Override - public List getIndexFields() { + public List getIndexFields() { return Arrays.asList( - IndexFields.create(IndexType.NON_UNIQUE, "manufacturer.name"), - IndexFields.create(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") + new EntityIndex(IndexType.NON_UNIQUE, "manufacturer.name"), + new EntityIndex(IndexType.UNIQUE, "productName", "manufacturer.uniqueId") ); } diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorReaderTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java similarity index 85% rename from nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorReaderTest.java rename to nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java index e01882365..ed2ca0aa5 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorReaderTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java @@ -22,7 +22,6 @@ import org.dizitart.no2.collection.NitriteCollection; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.mapper.SimpleDocumentMapper; -import org.dizitart.no2.index.IndexFields; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.integration.repository.data.ClassA; import org.junit.Before; @@ -34,16 +33,16 @@ import static org.junit.Assert.*; -public class EntityDecoratorReaderTest { +public class EntityDecoratorScannerTest { - private EntityDecoratorReader reader; + private EntityDecoratorScanner reader; private NitriteCollection collection; @Before public void setUp() { NitriteMapper nitriteMapper = new SimpleDocumentMapper(); collection = Nitrite.builder().openOrCreate().getCollection("test"); - reader = new EntityDecoratorReader(new EntityADecorator(), collection, nitriteMapper); + reader = new EntityDecoratorScanner(new EntityADecorator(), collection, nitriteMapper); } @Test @@ -60,8 +59,8 @@ public void testReadEntity() { assertArrayEquals(idField.getFieldNames(), new String[] {"uid", "string"}); assertArrayEquals(idField.getEmbeddedFieldNames(), new String[] {"id.uid", "id.string"}); - Set indexFields = reader.getIndices(); - assertEquals(indexFields.size(), 2); + Set entityIndexFields = reader.getIndices(); + assertEquals(entityIndexFields.size(), 2); } @Test @@ -115,10 +114,10 @@ public EntityId getIdField() { } @Override - public List getIndexFields() { - List list = new ArrayList<>(); - IndexFields fieldsA = IndexFields.create(IndexType.UNIQUE, "name", "age"); - IndexFields fieldsB = IndexFields.create(IndexType.NON_UNIQUE, "address"); + public List getIndexFields() { + List list = new ArrayList<>(); + EntityIndex fieldsA = new EntityIndex(IndexType.UNIQUE, "name", "age"); + EntityIndex fieldsB = new EntityIndex(IndexType.NON_UNIQUE, "address"); list.add(fieldsA); list.add(fieldsB); return list; diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryFactoryTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryFactoryTest.java index 09105f987..56e799c12 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryFactoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/RepositoryFactoryTest.java @@ -21,7 +21,6 @@ import org.dizitart.no2.collection.CollectionFactory; import org.dizitart.no2.common.concurrent.LockService; import org.dizitart.no2.exceptions.ValidationException; -import org.dizitart.no2.index.IndexFields; import org.junit.Test; import java.util.Date; @@ -83,7 +82,7 @@ public EntityId getIdField() { } @Override - public List getIndexFields() { + public List getIndexFields() { return null; } } From 0b70f4df6a0c7b895ae773a07dfac6f3fc12d2f6 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 11 Oct 2022 10:41:14 +0530 Subject: [PATCH 65/78] test added --- .../no2/common/util/DocumentUtilsTest.java | 8 ++++ .../no2/common/util/ObjectUtilsTest.java | 37 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java index a5fda50e7..359438f86 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java @@ -21,6 +21,7 @@ import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.mapper.SimpleDocumentMapper; +import org.dizitart.no2.exceptions.ObjectMappingException; import org.dizitart.no2.filters.ComparableFilter; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.integration.Retry; @@ -87,6 +88,13 @@ public void testSkeletonDocument2() { assertEquals(0, DocumentUtils.skeletonDocument(nitriteMapper, Object.class).size()); } + @Test(expected = ObjectMappingException.class) + public void testSkeletonDocument3() { + SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(); + Document document = DocumentUtils.skeletonDocument(nitriteMapper, Integer.class); + assertEquals(0, document.size()); + } + @Test public void testIsSimilar() { assertFalse(DocumentUtils.isSimilar(null, Document.createDocument(), "fields")); diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java index 4280333e9..d4d9dbdb0 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java @@ -39,9 +39,15 @@ import org.junit.Test; import java.io.Serializable; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.*; +import java.util.regex.Pattern; import static org.dizitart.no2.common.util.ObjectUtils.newInstance; import static org.junit.Assert.*; @@ -131,6 +137,37 @@ public void testNewInstance() { @Test public void testIsValueType() { + SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(); + assertFalse(ObjectUtils.isValueType(Object.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Number.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Byte.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Short.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Integer.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Long.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Float.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Double.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(BigDecimal.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(BigInteger.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Boolean.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Character.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(String.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Date.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(URL.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(URI.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Currency.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Calendar.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(StringBuffer.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(StringBuilder.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Locale.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Void.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(UUID.class, simpleDocumentMapper)); + assertTrue(ObjectUtils.isValueType(Pattern.class, simpleDocumentMapper)); + assertFalse(ObjectUtils.isValueType(NitriteId.class, simpleDocumentMapper)); + assertFalse(ObjectUtils.isValueType(Document.class, simpleDocumentMapper)); + } + + @Test + public void testIsValue() { assertFalse(ObjectUtils.isValueType(Object.class, new SimpleDocumentMapper())); } From f1872cff10a80ace6ede33ade0a1aab006cbc7db Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 11 Oct 2022 11:21:07 +0530 Subject: [PATCH 66/78] test added --- .../java/org/dizitart/no2/common/util/ObjectUtilsTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java index d4d9dbdb0..9b38d9717 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java @@ -164,6 +164,9 @@ public void testIsValueType() { assertTrue(ObjectUtils.isValueType(Pattern.class, simpleDocumentMapper)); assertFalse(ObjectUtils.isValueType(NitriteId.class, simpleDocumentMapper)); assertFalse(ObjectUtils.isValueType(Document.class, simpleDocumentMapper)); + + simpleDocumentMapper.addValueType(ValidEntity5.class); + assertTrue(ObjectUtils.isValueType(ValidEntity5.class, simpleDocumentMapper)); } @Test From 186f5338f94ba2a027529243aa3bd858f7cbd2a9 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 18 Oct 2022 11:04:27 +0530 Subject: [PATCH 67/78] nitrite mapper fix --- .../dizitart/no2/integration/NitriteTest.java | 266 ++++++++++++++++++ .../integration/migrate/MigrationTest.java | 1 + .../ObjectRepositoryNegativeTest.java | 4 +- .../repository/RepositorySearchTest.java | 10 - .../integration/repository/data/TestData.java | 33 +++ .../java/org/dizitart/no2/NitriteTest.java | 62 ++-- .../no2/integration/NitriteBuilderTest.java | 5 +- .../dizitart/no2/integration/NitriteTest.java | 7 + .../integration/migration/MigrationTest.java | 8 + .../integration/repository/InternalClass.java | 2 +- .../ObjectRepositoryNegativeTest.java | 12 +- .../repository/ObjectRepositoryTest.java | 8 +- .../repository/RepositoryFactoryTest.java | 2 +- .../RepositoryModificationTest.java | 22 +- .../repository/RepositorySearchTest.java | 6 +- .../UniversalTextTokenizerTest.java | 6 +- .../integration/repository/data/ClassB.java | 2 +- .../repository/data/ElemMatch.java | 2 +- .../repository/data/EmptyClass.java | 42 +++ .../repository/data/ProductScore.java | 2 +- .../repository/data/StressRecord.java | 4 +- .../repository/data/WithNullId.java | 2 +- .../repository/data/WithOutGetterSetter.java | 4 +- .../repository/data/WithOutId.java | 2 +- .../data/WithPrivateConstructor.java | 6 +- .../repository/data/WithPublicField.java | 2 +- .../repository/data/WithTransientField.java | 2 +- .../java/org/dizitart/no2/NitriteTest.java | 67 +++-- .../no2/integration/NitriteBuilderTest.java | 5 +- .../dizitart/no2/integration/NitriteTest.java | 6 + .../integration/migrate/MigrationTest.java | 8 + .../integration/repository/InternalClass.java | 2 +- .../ObjectRepositoryNegativeTest.java | 22 +- .../repository/ObjectRepositoryTest.java | 8 +- .../repository/RepositoryFactoryTest.java | 2 +- .../RepositoryModificationTest.java | 22 +- .../repository/RepositorySearchTest.java | 6 +- .../UniversalTextTokenizerTest.java | 5 +- .../repository/data/ElemMatch.java | 2 +- .../repository/data/EmptyClass.java | 42 +++ .../repository/data/ProductScore.java | 2 +- .../repository/data/StressRecord.java | 4 +- .../repository/data/WithNullId.java | 2 +- .../repository/data/WithOutGetterSetter.java | 4 +- .../repository/data/WithOutId.java | 6 +- .../data/WithPrivateConstructor.java | 6 +- .../repository/data/WithPublicField.java | 2 +- .../repository/data/WithTransientField.java | 2 +- .../common/mapper/SimpleDocumentMapper.java | 4 +- .../dizitart/no2/common/util/ObjectUtils.java | 28 +- .../no2/common/util/ValidationUtils.java | 33 +++ .../no2/filters/FieldBasedFilter.java | 10 +- .../no2/repository/IndexValidator.java | 29 +- .../dizitart/no2/repository/ObjectCursor.java | 10 +- .../no2/repository/RepositoryFactory.java | 13 +- .../no2/common/util/DocumentUtilsTest.java | 5 +- .../no2/common/util/ObjectUtilsTest.java | 82 +++--- .../dizitart/no2/integration/NitriteTest.java | 66 +++-- .../integration/repository/InternalClass.java | 2 +- .../ObjectRepositoryNegativeTest.java | 22 +- .../repository/ObjectRepositoryTest.java | 8 +- .../repository/RepositoryFactoryTest.java | 2 +- .../RepositoryModificationTest.java | 24 +- .../repository/RepositorySearchTest.java | 6 +- .../UniversalTextTokenizerTest.java | 6 +- .../repository/data/ElemMatch.java | 2 +- .../repository/data/EmptyClass.java | 42 +++ .../repository/data/ProductScore.java | 2 +- .../repository/data/StressRecord.java | 4 +- .../repository/data/WithNullId.java | 2 +- .../repository/data/WithOutGetterSetter.java | 4 +- .../repository/data/WithOutId.java | 2 +- .../data/WithPrivateConstructor.java | 6 +- .../repository/data/WithPublicField.java | 2 +- .../repository/data/WithTransientField.java | 2 +- .../EntityDecoratorScannerTest.java | 6 +- .../no2/repository/ObjectCursorTest.java | 4 +- 77 files changed, 832 insertions(+), 345 deletions(-) create mode 100644 nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/NitriteTest.java create mode 100644 nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/TestData.java create mode 100644 nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java create mode 100644 nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java create mode 100644 nitrite/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/NitriteTest.java new file mode 100644 index 000000000..8dffef69d --- /dev/null +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2017-2021 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.dizitart.no2.Nitrite; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.mapper.JacksonMapperModule; +import org.dizitart.no2.exceptions.NitriteIOException; +import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.index.IndexOptions; +import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.integration.repository.Retry; +import org.dizitart.no2.repository.ObjectRepository; +import org.dizitart.no2.repository.annotations.Id; +import org.dizitart.no2.repository.annotations.Index; +import org.dizitart.no2.repository.annotations.Indices; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.Set; + +import static org.dizitart.no2.collection.Document.createDocument; +import static org.dizitart.no2.common.Constants.INTERNAL_NAME_SEPARATOR; +import static org.dizitart.no2.common.Constants.META_MAP_NAME; +import static org.dizitart.no2.filters.Filter.ALL; +import static org.dizitart.no2.integration.repository.TestUtil.createDb; +import static org.junit.Assert.*; + +/** + * @author Anindya Chatterjee. + */ +public class NitriteTest { + @Rule + public Retry retry = new Retry(3); + private Nitrite db; + private NitriteCollection collection; + + @Before + public void setUp() throws ParseException { + db = createDb(new JacksonMapperModule()); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH); + + Document doc1 = createDocument("firstName", "fn1") + .put("lastName", "ln1") + .put("birthDay", simpleDateFormat.parse("2012-07-01T16:02:48.440Z")) + .put("data", new byte[]{1, 2, 3}) + .put("body", "a quick brown fox jump over the lazy dog"); + Document doc2 = createDocument("firstName", "fn2") + .put("lastName", "ln2") + .put("birthDay", simpleDateFormat.parse("2010-06-12T16:02:48.440Z")) + .put("data", new byte[]{3, 4, 3}) + .put("body", "hello world from nitrite"); + Document doc3 = createDocument("firstName", "fn3") + .put("lastName", "ln2") + .put("birthDay", simpleDateFormat.parse("2014-04-17T16:02:48.440Z")) + .put("data", new byte[]{9, 4, 8}) + .put("body", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + + "Sed nunc mi, mattis ullamcorper dignissim vitae, condimentum non lorem."); + + collection = db.getCollection("test"); + collection.remove(ALL); + + collection.createIndex(IndexOptions.indexOptions(IndexType.FULL_TEXT), "body"); + collection.createIndex(IndexOptions.indexOptions(IndexType.UNIQUE), "firstName"); + collection.insert(doc1, doc2, doc3); + } + + @After + public void tearDown() { + if (collection.isOpen()) { + collection.remove(ALL); + collection.close(); + } + if (db != null && !db.isClosed()) { + try { + db.close(); + } catch (NitriteIOException ignore) { + } + } + } + + @Test + public void testListCollectionNames() { + Set collectionNames = db.listCollectionNames(); + assertEquals(collectionNames.size(), 1); + } + + @Test(expected = ValidationException.class) + public void testListRepositories() { + db.getRepository(EmptyClass.class); + } + + @Test + public void testListRepositories2() { + db.getRepository(Receipt.class); + Set repositories = db.listRepositories(); + assertEquals(repositories.size(), 1); + } + + @Test + public void testHasCollection() { + assertTrue(db.hasCollection("test")); + assertFalse(db.hasCollection("lucene" + INTERNAL_NAME_SEPARATOR + "test")); + } + + @Test(expected = ValidationException.class) + public void testHasRepository() { + db.getRepository(EmptyClass.class); + } + + @Test + public void testHasRepository2() { + db.getRepository(Receipt.class); + assertTrue(db.hasRepository(Receipt.class)); + assertFalse(db.hasRepository(String.class)); + } + + @Test + public void testClose() { + NitriteCollection testCollection = db.getCollection("test"); + testCollection.insert(createDocument("a", "b")); + db.close(); + + assertFalse(testCollection.isOpen()); + } + + @Test + public void testGetCollection() { + NitriteCollection collection = db.getCollection("test-collection"); + assertNotNull(collection); + assertEquals(collection.getName(), "test-collection"); + } + + @Test(expected = ValidationException.class) + public void testGetRepository() { + ObjectRepository repository = db.getRepository(EmptyClass.class); + } + + @Test + public void testGetRepository2() { + ObjectRepository repository = db.getRepository(Receipt.class); + assertNotNull(repository); + assertEquals(repository.getType(), Receipt.class); + } + + @Test(expected = ValidationException.class) + public void testGetRepositoryWithKey() { + ObjectRepository repository = db.getRepository(EmptyClass.class, "key"); + } + + @Test + public void testGetRepositoryWithKey2() { + ObjectRepository repository = db.getRepository(Receipt.class, "key"); + assertNotNull(repository); + assertEquals(repository.getType(), Receipt.class); + assertFalse(db.hasRepository(Receipt.class)); + assertTrue(db.hasRepository(Receipt.class, "key")); + } + + @Test + public void testMultipleGetCollection() { + NitriteCollection collection = db.getCollection("test-collection"); + assertNotNull(collection); + assertEquals(collection.getName(), "test-collection"); + + NitriteCollection collection2 = db.getCollection("test-collection"); + assertNotNull(collection2); + assertEquals(collection2.getName(), "test-collection"); + } + + @Test + public void testMultipleGetRepository() { + ObjectRepository repository = db.getRepository(Receipt.class); + assertNotNull(repository); + assertEquals(repository.getType(), Receipt.class); + + ObjectRepository repository2 = db.getRepository(Receipt.class); + assertNotNull(repository2); + assertEquals(repository2.getType(), Receipt.class); + } + + @Test(expected = ValidationException.class) + public void testGetRepositoryInvalid() { + db.getRepository((Class) null); + } + + @Test(expected = NitriteIOException.class) + public void testGetCollectionNullStore() { + db = Nitrite.builder().openOrCreate(); + db.close(); + db.getCollection("test"); + } + + @Test(expected = NitriteIOException.class) + public void testGetRepositoryNullStore() { + db = Nitrite.builder().openOrCreate(); + db.close(); + db.getRepository(NitriteTest.class); + } + + @Test(expected = NitriteIOException.class) + public void testGetKeyedRepositoryNullStore() { + db = Nitrite.builder().openOrCreate(); + db.close(); + db.getRepository(NitriteTest.class, "key"); + } + + @Test(expected = NitriteIOException.class) + public void testCommitNullStore() { + db = Nitrite.builder().openOrCreate(); + db.close(); + db.commit(); + } + + @Test(expected = ValidationException.class) + public void testGetCollectionInvalidName() { + db.getCollection(META_MAP_NAME); + } + + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Indices({ + @Index(value = "synced", type = IndexType.NON_UNIQUE) + }) + public static class Receipt { + @Id + private String clientRef; + private Boolean synced; + private Long createdTimestamp = System.currentTimeMillis(); + private Status status; + + public enum Status { + COMPLETED, + PREPARING, + } + } + + public static class EmptyClass { + } +} diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java index 8ce2d0b2b..f0920e5c6 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java @@ -121,6 +121,7 @@ public void migrate(InstructionSet instruction) { db = Nitrite.builder() .loadModule(storeModule) + .loadModule(new JacksonMapperModule()) .fieldSeparator(".") .schemaVersion(2) .addMigrations(migration) diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java index 9e070c839..178d688ee 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java @@ -76,7 +76,7 @@ public void testWithCircularReference() { assertEquals(instance.getParent().getName(), object.getParent().getName()); } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testWithCustomConstructor() { ObjectRepository repository = db.getRepository(WithCustomConstructor.class); @@ -157,7 +157,7 @@ public void testRemoveNoId() { repository.remove(object); } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testProjectionFailedInstantiate() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java index d0bb00a1c..df689f155 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java @@ -17,7 +17,6 @@ package org.dizitart.no2.integration.repository; -import lombok.Getter; import org.dizitart.no2.common.SortOrder; import org.dizitart.no2.exceptions.FilterException; import org.dizitart.no2.exceptions.InvalidIdException; @@ -544,15 +543,6 @@ public void testIdSet() { @Test public void testBetweenFilter() { - @Getter - class TestData { - private final Date age; - - public TestData(Date age) { - this.age = age; - } - } - TestData data1 = new TestData(new GregorianCalendar(2020, Calendar.JANUARY, 11).getTime()); TestData data2 = new TestData(new GregorianCalendar(2021, Calendar.FEBRUARY, 12).getTime()); TestData data3 = new TestData(new GregorianCalendar(2022, Calendar.MARCH, 13).getTime()); diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/TestData.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/TestData.java new file mode 100644 index 000000000..d4a1733f7 --- /dev/null +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/TestData.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.data; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@Getter +@NoArgsConstructor +public class TestData { + private Date age; + + public TestData(Date age) { + this.age = age; + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java index e6e077671..c8cfe9234 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java @@ -36,6 +36,7 @@ import org.dizitart.no2.index.IndexType; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.TestUtil; +import org.dizitart.no2.integration.repository.data.EmptyClass; import org.dizitart.no2.mvstore.MVStoreModule; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Id; @@ -88,6 +89,7 @@ public void setUp() throws ParseException { SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); documentMapper.registerEntityConverter(new CompatChild.Converter()); documentMapper.registerEntityConverter(new Receipt.Converter()); + documentMapper.registerEntityConverter(new EmptyClass.Converter()); simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH); @@ -137,9 +139,14 @@ public void testListCollectionNames() { assertEquals(collectionNames.size(), 1); } - @Test + @Test(expected = ValidationException.class) public void testListRepositories() { db.getRepository(getClass()); + } + + @Test + public void testListRepositories2() { + db.getRepository(Receipt.class); Set repositories = db.listRepositories(); assertEquals(repositories.size(), 1); } @@ -150,10 +157,15 @@ public void testHasCollection() { assertFalse(db.hasCollection("lucene" + INTERNAL_NAME_SEPARATOR + "test")); } - @Test + @Test(expected = ValidationException.class) public void testHasRepository() { db.getRepository(getClass()); - assertTrue(db.hasRepository(getClass())); + } + + @Test + public void testHasRepository2() { + db.getRepository(Receipt.class); + assertTrue(db.hasRepository(Receipt.class)); assertFalse(db.hasRepository(String.class)); } @@ -245,20 +257,30 @@ public void testGetCollection() { assertEquals(collection.getName(), "test-collection"); } - @Test + @Test(expected = ValidationException.class) public void testGetRepository() { - ObjectRepository repository = db.getRepository(NitriteTest.class); - assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); + ObjectRepository repository = db.getRepository(EmptyClass.class); } @Test + public void testGetRepository2() { + ObjectRepository repository = db.getRepository(Receipt.class); + assertNotNull(repository); + assertEquals(repository.getType(), Receipt.class); + } + + @Test(expected = ValidationException.class) public void testGetRepositoryWithKey() { - ObjectRepository repository = db.getRepository(NitriteTest.class, "key"); + ObjectRepository repository = db.getRepository(EmptyClass.class, "key"); + } + + @Test + public void testGetRepositoryWithKey2() { + ObjectRepository repository = db.getRepository(Receipt.class, "key"); assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); - assertFalse(db.hasRepository(NitriteTest.class)); - assertTrue(db.hasRepository(NitriteTest.class, "key")); + assertEquals(repository.getType(), Receipt.class); + assertFalse(db.hasRepository(Receipt.class)); + assertTrue(db.hasRepository(Receipt.class, "key")); } @Test @@ -274,13 +296,13 @@ public void testMultipleGetCollection() { @Test public void testMultipleGetRepository() { - ObjectRepository repository = db.getRepository(NitriteTest.class); + ObjectRepository repository = db.getRepository(Receipt.class); assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); + assertEquals(repository.getType(), Receipt.class); - ObjectRepository repository2 = db.getRepository(NitriteTest.class); + ObjectRepository repository2 = db.getRepository(Receipt.class); assertNotNull(repository2); - assertEquals(repository2.getType(), NitriteTest.class); + assertEquals(repository2.getType(), Receipt.class); } @Test(expected = ValidationException.class) @@ -632,10 +654,12 @@ public Receipt fromDocument(Document document, NitriteMapper nitriteMapper) { Receipt receipt = new Receipt(); if (document != null) { Object status = document.get("status"); - if (status instanceof Status) { - receipt.status = (Status) status; - } else { - receipt.status = Status.valueOf(status.toString()); + if (status != null) { + if (status instanceof Receipt.Status) { + receipt.status = (Receipt.Status) status; + } else { + receipt.status = Receipt.Status.valueOf(status.toString()); + } } receipt.clientRef = document.get("clientRef", String.class); receipt.synced = document.get("synced", Boolean.class); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java index 7339b791b..b8d1193ec 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java @@ -408,7 +408,10 @@ public Document toDocument(TestObject entity, NitriteMapper nitriteMapper) { @Override public TestObject fromDocument(Document document, NitriteMapper nitriteMapper) { - return null; + TestObject entity = new TestObject(); + entity.stringValue = document.get("stringValue", String.class); + entity.longValue = document.get("longValue", Long.class); + return entity; } } } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java index f5396fdfe..449b7ffe0 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -19,7 +19,9 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.repository.data.ClassA; +import org.dizitart.no2.integration.repository.data.ClassBConverter; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -37,6 +39,11 @@ public class NitriteTest { @Before public void setUp() { db = createDb(fileName); + SimpleDocumentMapper nitriteMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + nitriteMapper.registerEntityConverter(new ClassA.ClassAConverter()); + nitriteMapper.registerEntityConverter(new ClassBConverter()); + + NitriteCollection collection = db.getCollection("test"); assertNotNull(collection); } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java index 5c71e08d6..2843ba4c5 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/MigrationTest.java @@ -61,6 +61,8 @@ public void setUp() { SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); documentMapper.registerEntityConverter(new OldClass.Converter()); documentMapper.registerEntityConverter(new OldClass.Literature.Converter()); + documentMapper.registerEntityConverter(new NewClass.Converter()); + documentMapper.registerEntityConverter(new NewClass.Literature.Converter()); faker = new Faker(); } @@ -126,6 +128,12 @@ public void migrate(InstructionSet instruction) { .addMigrations(migration) .openOrCreate("test-user", "test-password"); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new OldClass.Converter()); + documentMapper.registerEntityConverter(new OldClass.Literature.Converter()); + documentMapper.registerEntityConverter(new NewClass.Converter()); + documentMapper.registerEntityConverter(new NewClass.Literature.Converter()); + ObjectRepository newRepo = db.getRepository(NewClass.class); assertEquals(newRepo.size(), 10); assertTrue(db.listCollectionNames().isEmpty()); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java index 7096ddaaf..e6a2a58d4 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java @@ -29,7 +29,7 @@ @Data class InternalClass { @Id - private long id; + private Long id; private String name; public static class Converter implements EntityConverter { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java index a34a91078..66a816959 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java @@ -72,7 +72,7 @@ public void close() throws IOException { deleteDb(dbPath); } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testWithCircularReference() { ObjectRepository repository = db.getRepository(WithCircularReference.class); @@ -92,7 +92,7 @@ public void testWithCircularReference() { } } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testWithCustomConstructor() { ObjectRepository repository = db.getRepository(WithCustomConstructor.class); @@ -150,7 +150,7 @@ public void testWithObjectId() { ObjectRepository repository = db.getRepository(WithObjectId.class); WithOutId id = new WithOutId(); id.setName("test"); - id.setNumber(1); + id.setNumber(1L); WithObjectId object = new WithObjectId(); object.setWithOutId(id); @@ -175,7 +175,7 @@ public void testRemoveNoId() { repository.remove(object); } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testProjectionFailedInstantiate() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); @@ -198,7 +198,7 @@ public void testGetByNullId() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); WithPublicField instance = repository.getById(null); @@ -234,7 +234,7 @@ public void testGetByWrongIdType() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 50e8969ab..ce149b146 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -137,7 +137,7 @@ public void testWithOutId() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); object.setName("test"); - object.setNumber(2); + object.setNumber(2L); repository.insert(object); for (WithOutId instance : repository.find()) { @@ -151,7 +151,7 @@ public void testWithPublicField() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); WithPublicField instance = repository.getById("test"); @@ -163,7 +163,7 @@ public void testWithPublicField() { public void testWithTransientField() { ObjectRepository repository = db.getRepository(WithTransientField.class); WithTransientField object = new WithTransientField(); - object.setNumber(2); + object.setNumber(2L); object.setName("test"); repository.insert(object); @@ -202,7 +202,7 @@ public void testWriteThousandRecords() { public void testWithPackagePrivateClass() { ObjectRepository repository = db.getRepository(InternalClass.class); InternalClass internalClass = new InternalClass(); - internalClass.setId(1); + internalClass.setId(1L); internalClass.setName("name"); repository.insert(internalClass); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 85e1dcac1..68a2371b3 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -69,7 +69,7 @@ public void testNullType() { factory.getRepository(db.getConfig(), (Class) null, "dummy"); } - @Test + @Test(expected = ValidationException.class) public void testNullCollection() { RepositoryFactory factory = new RepositoryFactory(new CollectionFactory(new LockService())); factory.getRepository(db.getConfig(), DummyCollection.class, null); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java index 487d27ff6..f12c86fdf 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java @@ -564,12 +564,12 @@ public void testDelete() { public void testUpdateObjectNotExistsUpsertTrue() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(2); + a.setId(2L); a.setName("second"); // it will insert as new object @@ -581,18 +581,18 @@ public void testUpdateObjectNotExistsUpsertTrue() { public void testUpdateObjectNotExistsUpsertFalse() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(2); + a.setId(2L); a.setName("second"); // no changes will happen to repository repo.update(a, false); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1); assertEquals(repo.find().firstOrNull().getName(), "first"); } @@ -600,18 +600,18 @@ public void testUpdateObjectNotExistsUpsertFalse() { public void testUpdateObjectExistsUpsertTrue() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("second"); // update existing object, keep id same repo.update(a, true); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1); assertEquals(repo.find().firstOrNull().getName(), "second"); } @@ -619,18 +619,18 @@ public void testUpdateObjectExistsUpsertTrue() { public void testUpdateObjectExistsUpsertFalse() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("second"); // update existing object, keep id same repo.update(a, false); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1); assertEquals(repo.find().firstOrNull().getName(), "second"); } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java index fd1b959c7..47a798519 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java @@ -366,17 +366,17 @@ public void testElemMatchFilter() { ObjectRepository repository = db.getRepository(ElemMatch.class); ElemMatch e1 = new ElemMatch(); - e1.setId(1); + e1.setId(1L); e1.setStrArray(new String[]{"a", "b"}); e1.setProductScores(new ProductScore[]{score1, score4}); ElemMatch e2 = new ElemMatch(); - e2.setId(2); + e2.setId(2L); e2.setStrArray(new String[]{"d", "e"}); e2.setProductScores(new ProductScore[]{score2, score5}); ElemMatch e3 = new ElemMatch(); - e3.setId(3); + e3.setId(3L); e3.setStrArray(new String[]{"a", "f"}); e3.setProductScores(new ProductScore[]{score3, score6}); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index 427d94b8d..124b8a441 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -56,11 +56,11 @@ public class UniversalTextTokenizerTest extends BaseObjectRepositoryTest { @Override public void setUp() { openDb(); - - textRepository = db.getRepository(TextData.class); SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); documentMapper.registerEntityConverter(new TextData.Converter()); + textRepository = db.getRepository(TextData.class); + for (int i = 0; i < 10; i++) { TextData data = new TextData(); data.id = i; @@ -187,7 +187,7 @@ public void testUniversalFullTextIndexing() { @Index(value = "text", type = IndexType.FULL_TEXT) ) public static class TextData { - public int id; + public Integer id; public String text; public static class Converter implements EntityConverter { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java index 51cc9b416..e77c8c5bf 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java @@ -27,7 +27,7 @@ class ClassB implements Comparable { @Getter @Setter - private int number; + private Integer number; @Getter @Setter private String text; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java index 3ef376e44..92c710d2e 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java @@ -30,7 +30,7 @@ */ @Data public class ElemMatch { - private long id; + private Long id; private String[] strArray; private ProductScore[] productScores; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java new file mode 100644 index 000000000..9bf6bcbf6 --- /dev/null +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.data; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class EmptyClass { + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmptyClass.class; + } + + @Override + public Document toDocument(EmptyClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument(); + } + + @Override + public EmptyClass fromDocument(Document document, NitriteMapper nitriteMapper) { + return new EmptyClass(); + } + } +} diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java index 9ebe19057..23c9d16ff 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java @@ -30,7 +30,7 @@ @Setter public class ProductScore { private String product; - private int score; + private Integer score; public ProductScore() { } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java index 9e74e8fb0..33dc06073 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java @@ -30,9 +30,9 @@ @Setter public class StressRecord { private String firstName; - private boolean processed; + private Boolean processed; private String lastName; - private boolean failed; + private Boolean failed; private String notes; public static class Converter implements EntityConverter { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java index e5ca44b78..dfac35bfa 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java @@ -32,7 +32,7 @@ public class WithNullId { @Id private String name; - private long number; + private Long number; public static class Converter implements EntityConverter { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java index 2ce03bf24..e6783a89e 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java @@ -28,11 +28,11 @@ @EqualsAndHashCode public class WithOutGetterSetter { private String name; - private long number; + private Long number; public WithOutGetterSetter() { name = "test"; - number = 2; + number = 2L; } public static class Converter implements EntityConverter { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java index 645dd0b45..4685e7726 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java @@ -30,7 +30,7 @@ @Setter public class WithOutId implements Comparable { private String name; - private long number; + private Long number; @Override public int compareTo(WithOutId o) { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java index 28febec9e..de08cf19b 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java @@ -28,14 +28,14 @@ @EqualsAndHashCode public class WithPrivateConstructor { private String name; - private long number; + private Long number; private WithPrivateConstructor() { name = "test"; - number = 2; + number = 2L; } - public static WithPrivateConstructor create(final String name, final long number) { + public static WithPrivateConstructor create(final String name, final Long number) { WithPrivateConstructor obj = new WithPrivateConstructor(); obj.number = number; obj.name = name; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java index eae21f1de..81b69a0c1 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java @@ -28,7 +28,7 @@ public class WithPublicField { @Id public String name; - public long number; + public Long number; public static class Converter implements EntityConverter { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java index 35dde5a91..76ea86243 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java @@ -32,7 +32,7 @@ public class WithTransientField { private transient String name; @Id - private long number; + private Long number; public static class Converter implements EntityConverter { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java index 208ee3654..446bd7d07 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java @@ -36,6 +36,7 @@ import org.dizitart.no2.index.IndexType; import org.dizitart.no2.integration.Retry; import org.dizitart.no2.integration.TestUtil; +import org.dizitart.no2.integration.repository.data.EmptyClass; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -83,6 +84,8 @@ public void setUp() throws ParseException { SimpleDocumentMapper simpleDocumentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); simpleDocumentMapper.registerEntityConverter(new Receipt.Converter()); + simpleDocumentMapper.registerEntityConverter(new CompatChild.Converter()); + simpleDocumentMapper.registerEntityConverter(new EmptyClass.Converter()); simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH); @@ -132,9 +135,14 @@ public void testListCollectionNames() { assertEquals(collectionNames.size(), 1); } - @Test + @Test(expected = ValidationException.class) public void testListRepositories() { db.getRepository(getClass()); + } + + @Test + public void testListRepositories2() { + db.getRepository(Receipt.class); Set repositories = db.listRepositories(); assertEquals(repositories.size(), 1); } @@ -145,10 +153,15 @@ public void testHasCollection() { assertFalse(db.hasCollection("lucene" + INTERNAL_NAME_SEPARATOR + "test")); } - @Test + @Test(expected = ValidationException.class) public void testHasRepository() { db.getRepository(getClass()); - assertTrue(db.hasRepository(getClass())); + } + + @Test + public void testHasRepository2() { + db.getRepository(Receipt.class); + assertTrue(db.hasRepository(Receipt.class)); assertFalse(db.hasRepository(String.class)); } @@ -209,20 +222,30 @@ public void testGetCollection() { assertEquals(collection.getName(), "test-collection"); } - @Test + @Test(expected = ValidationException.class) public void testGetRepository() { - ObjectRepository repository = db.getRepository(NitriteTest.class); - assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); + ObjectRepository repository = db.getRepository(EmptyClass.class); } @Test + public void testGetRepository2() { + ObjectRepository repository = db.getRepository(Receipt.class); + assertNotNull(repository); + assertEquals(repository.getType(), Receipt.class); + } + + @Test(expected = ValidationException.class) public void testGetRepositoryWithKey() { - ObjectRepository repository = db.getRepository(NitriteTest.class, "key"); + ObjectRepository repository = db.getRepository(EmptyClass.class, "key"); + } + + @Test + public void testGetRepositoryWithKey2() { + ObjectRepository repository = db.getRepository(Receipt.class, "key"); assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); - assertFalse(db.hasRepository(NitriteTest.class)); - assertTrue(db.hasRepository(NitriteTest.class, "key")); + assertEquals(repository.getType(), Receipt.class); + assertFalse(db.hasRepository(Receipt.class)); + assertTrue(db.hasRepository(Receipt.class, "key")); } @Test @@ -238,13 +261,13 @@ public void testMultipleGetCollection() { @Test public void testMultipleGetRepository() { - ObjectRepository repository = db.getRepository(NitriteTest.class); + ObjectRepository repository = db.getRepository(Receipt.class); assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); + assertEquals(repository.getType(), Receipt.class); - ObjectRepository repository2 = db.getRepository(NitriteTest.class); + ObjectRepository repository2 = db.getRepository(Receipt.class); assertNotNull(repository2); - assertEquals(repository2.getType(), NitriteTest.class); + assertEquals(repository2.getType(), Receipt.class); } @Test(expected = ValidationException.class) @@ -263,14 +286,14 @@ public void testGetCollectionNullStore() { public void testGetRepositoryNullStore() { db = Nitrite.builder().openOrCreate(); db.close(); - db.getRepository(NitriteTest.class); + db.getRepository(EmptyClass.class); } @Test(expected = NitriteIOException.class) public void testGetKeyedRepositoryNullStore() { db = Nitrite.builder().openOrCreate(); db.close(); - db.getRepository(NitriteTest.class, "key"); + db.getRepository(EmptyClass.class, "key"); } @Test(expected = NitriteIOException.class) @@ -490,10 +513,12 @@ public Receipt fromDocument(Document document, NitriteMapper nitriteMapper) { Receipt receipt = new Receipt(); if (document != null) { Object status = document.get("status"); - if (status instanceof Status) { - receipt.status = (Status) status; - } else { - receipt.status = Status.valueOf(status.toString()); + if (status != null) { + if (status instanceof Receipt.Status) { + receipt.status = (Receipt.Status) status; + } else { + receipt.status = Receipt.Status.valueOf(status.toString()); + } } receipt.clientRef = document.get("clientRef", String.class); receipt.synced = document.get("synced", Boolean.class); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java index 46c08bde1..f407e701f 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java @@ -344,7 +344,10 @@ public Document toDocument(TestObject entity, NitriteMapper nitriteMapper) { @Override public TestObject fromDocument(Document document, NitriteMapper nitriteMapper) { - return null; + TestObject entity = new TestObject(); + entity.stringValue = document.get("stringValue", String.class); + entity.longValue = document.get("longValue", Long.class); + return entity; } } } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java index f5396fdfe..20c06f02c 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -19,7 +19,9 @@ import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.NitriteCollection; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.integration.repository.data.ClassA; +import org.dizitart.no2.integration.repository.data.ClassBConverter; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -37,6 +39,10 @@ public class NitriteTest { @Before public void setUp() { db = createDb(fileName); + SimpleDocumentMapper nitriteMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + nitriteMapper.registerEntityConverter(new ClassA.ClassAConverter()); + nitriteMapper.registerEntityConverter(new ClassBConverter()); + NitriteCollection collection = db.getCollection("test"); assertNotNull(collection); } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java index 8d027bdbe..cd31138bd 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/MigrationTest.java @@ -61,6 +61,8 @@ public void setUp() { SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); documentMapper.registerEntityConverter(new OldClass.Converter()); documentMapper.registerEntityConverter(new OldClass.Literature.Converter()); + documentMapper.registerEntityConverter(new NewClass.Converter()); + documentMapper.registerEntityConverter(new NewClass.Literature.Converter()); faker = new Faker(); } @@ -125,6 +127,12 @@ public void migrate(InstructionSet instruction) { .addMigrations(migration) .openOrCreate("test-user", "test-password"); + SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); + documentMapper.registerEntityConverter(new OldClass.Converter()); + documentMapper.registerEntityConverter(new OldClass.Literature.Converter()); + documentMapper.registerEntityConverter(new NewClass.Converter()); + documentMapper.registerEntityConverter(new NewClass.Literature.Converter()); + ObjectRepository newRepo = db.getRepository(NewClass.class); assertEquals(newRepo.size(), 10); assertTrue(db.listCollectionNames().isEmpty()); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java index 7096ddaaf..e6a2a58d4 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java @@ -29,7 +29,7 @@ @Data class InternalClass { @Id - private long id; + private Long id; private String name; public static class Converter implements EntityConverter { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java index a34a91078..6d7135ef5 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java @@ -72,7 +72,7 @@ public void close() throws IOException { deleteDb(dbPath); } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testWithCircularReference() { ObjectRepository repository = db.getRepository(WithCircularReference.class); @@ -92,7 +92,7 @@ public void testWithCircularReference() { } } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testWithCustomConstructor() { ObjectRepository repository = db.getRepository(WithCustomConstructor.class); @@ -145,18 +145,6 @@ public void testFindResultRemove() { result.iterator().remove(); } - @Test(expected = IndexingException.class) - public void testWithObjectId() { - ObjectRepository repository = db.getRepository(WithObjectId.class); - WithOutId id = new WithOutId(); - id.setName("test"); - id.setNumber(1); - - WithObjectId object = new WithObjectId(); - object.setWithOutId(id); - repository.insert(object); - } - @Test(expected = NotIdentifiableException.class) public void testUpdateNoId() { ObjectRepository repository = db.getRepository(WithOutId.class); @@ -175,7 +163,7 @@ public void testRemoveNoId() { repository.remove(object); } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testProjectionFailedInstantiate() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); @@ -198,7 +186,7 @@ public void testGetByNullId() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); WithPublicField instance = repository.getById(null); @@ -234,7 +222,7 @@ public void testGetByWrongIdType() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 87adaac15..14d5c924f 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -137,7 +137,7 @@ public void testWithOutId() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); object.setName("test"); - object.setNumber(2); + object.setNumber(2L); repository.insert(object); for (WithOutId instance : repository.find()) { @@ -151,7 +151,7 @@ public void testWithPublicField() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); WithPublicField instance = repository.getById("test"); @@ -163,7 +163,7 @@ public void testWithPublicField() { public void testWithTransientField() { ObjectRepository repository = db.getRepository(WithTransientField.class); WithTransientField object = new WithTransientField(); - object.setNumber(2); + object.setNumber(2L); object.setName("test"); repository.insert(object); @@ -202,7 +202,7 @@ public void testWriteThousandRecords() { public void testWithPackagePrivateClass() { ObjectRepository repository = db.getRepository(InternalClass.class); InternalClass internalClass = new InternalClass(); - internalClass.setId(1); + internalClass.setId(1L); internalClass.setName("name"); repository.insert(internalClass); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 348ec112c..0e8e7d2bb 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -69,7 +69,7 @@ public void testNullType() { factory.getRepository(db.getConfig(), (Class) null, "dummy"); } - @Test + @Test(expected = ValidationException.class) public void testNullCollection() { RepositoryFactory factory = new RepositoryFactory(new CollectionFactory(new LockService())); factory.getRepository(db.getConfig(), DummyCollection.class, null); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java index 487d27ff6..f12c86fdf 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java @@ -564,12 +564,12 @@ public void testDelete() { public void testUpdateObjectNotExistsUpsertTrue() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(2); + a.setId(2L); a.setName("second"); // it will insert as new object @@ -581,18 +581,18 @@ public void testUpdateObjectNotExistsUpsertTrue() { public void testUpdateObjectNotExistsUpsertFalse() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(2); + a.setId(2L); a.setName("second"); // no changes will happen to repository repo.update(a, false); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1); assertEquals(repo.find().firstOrNull().getName(), "first"); } @@ -600,18 +600,18 @@ public void testUpdateObjectNotExistsUpsertFalse() { public void testUpdateObjectExistsUpsertTrue() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("second"); // update existing object, keep id same repo.update(a, true); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1); assertEquals(repo.find().firstOrNull().getName(), "second"); } @@ -619,18 +619,18 @@ public void testUpdateObjectExistsUpsertTrue() { public void testUpdateObjectExistsUpsertFalse() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("second"); // update existing object, keep id same repo.update(a, false); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1); assertEquals(repo.find().firstOrNull().getName(), "second"); } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java index 9be6cd740..61efe315b 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java @@ -366,17 +366,17 @@ public void testElemMatchFilter() { ObjectRepository repository = db.getRepository(ElemMatch.class); ElemMatch e1 = new ElemMatch(); - e1.setId(1); + e1.setId(1L); e1.setStrArray(new String[]{"a", "b"}); e1.setProductScores(new ProductScore[]{score1, score4}); ElemMatch e2 = new ElemMatch(); - e2.setId(2); + e2.setId(2L); e2.setStrArray(new String[]{"d", "e"}); e2.setProductScores(new ProductScore[]{score2, score5}); ElemMatch e3 = new ElemMatch(); - e3.setId(3); + e3.setId(3L); e3.setStrArray(new String[]{"a", "f"}); e3.setProductScores(new ProductScore[]{score3, score6}); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index 669da19f5..6d157356e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -55,10 +55,11 @@ public class UniversalTextTokenizerTest extends BaseObjectRepositoryTest { @Override public void setUp() { openDb(); - textRepository = db.getRepository(TextData.class); SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); documentMapper.registerEntityConverter(new TextData.Converter()); + textRepository = db.getRepository(TextData.class); + for (int i = 0; i < 10; i++) { TextData data = new TextData(); data.id = i; @@ -172,7 +173,7 @@ public void testUniversalFullTextIndexing() { @Index(value = "text", type = IndexType.FULL_TEXT) ) public static class TextData { - public int id; + public Integer id; public String text; public static class Converter implements EntityConverter { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java index 3ef376e44..92c710d2e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java @@ -30,7 +30,7 @@ */ @Data public class ElemMatch { - private long id; + private Long id; private String[] strArray; private ProductScore[] productScores; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java new file mode 100644 index 000000000..9bf6bcbf6 --- /dev/null +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.data; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class EmptyClass { + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmptyClass.class; + } + + @Override + public Document toDocument(EmptyClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument(); + } + + @Override + public EmptyClass fromDocument(Document document, NitriteMapper nitriteMapper) { + return new EmptyClass(); + } + } +} diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java index 9ebe19057..23c9d16ff 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java @@ -30,7 +30,7 @@ @Setter public class ProductScore { private String product; - private int score; + private Integer score; public ProductScore() { } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java index 9e74e8fb0..33dc06073 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java @@ -30,9 +30,9 @@ @Setter public class StressRecord { private String firstName; - private boolean processed; + private Boolean processed; private String lastName; - private boolean failed; + private Boolean failed; private String notes; public static class Converter implements EntityConverter { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java index e5ca44b78..dfac35bfa 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java @@ -32,7 +32,7 @@ public class WithNullId { @Id private String name; - private long number; + private Long number; public static class Converter implements EntityConverter { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java index 2ce03bf24..e6783a89e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java @@ -28,11 +28,11 @@ @EqualsAndHashCode public class WithOutGetterSetter { private String name; - private long number; + private Long number; public WithOutGetterSetter() { name = "test"; - number = 2; + number = 2L; } public static class Converter implements EntityConverter { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java index 645dd0b45..e34e41702 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java @@ -23,14 +23,16 @@ import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; +import java.io.Serializable; + /** * @author Anindya Chatterjee. */ @Getter @Setter -public class WithOutId implements Comparable { +public class WithOutId implements Comparable, Serializable { private String name; - private long number; + private Long number; @Override public int compareTo(WithOutId o) { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java index 28febec9e..de08cf19b 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java @@ -28,14 +28,14 @@ @EqualsAndHashCode public class WithPrivateConstructor { private String name; - private long number; + private Long number; private WithPrivateConstructor() { name = "test"; - number = 2; + number = 2L; } - public static WithPrivateConstructor create(final String name, final long number) { + public static WithPrivateConstructor create(final String name, final Long number) { WithPrivateConstructor obj = new WithPrivateConstructor(); obj.number = number; obj.name = name; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java index eae21f1de..81b69a0c1 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java @@ -28,7 +28,7 @@ public class WithPublicField { @Id public String name; - public long number; + public Long number; public static class Converter implements EntityConverter { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java index 35dde5a91..76ea86243 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java @@ -32,7 +32,7 @@ public class WithTransientField { private transient String name; @Id - private long number; + private Long number; public static class Converter implements EntityConverter { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/mapper/SimpleDocumentMapper.java b/nitrite/src/main/java/org/dizitart/no2/common/mapper/SimpleDocumentMapper.java index 242b797d0..c6d45b59c 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/mapper/SimpleDocumentMapper.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/mapper/SimpleDocumentMapper.java @@ -151,9 +151,7 @@ private boolean isValue(Object object) { } private void init(List> valueTypes) { - for (Class builtInType : ObjectUtils.builtInTypes()) { - this.valueTypes.add(builtInType); - } + this.valueTypes.addAll(ObjectUtils.builtInTypes()); this.valueTypes.add(Enum.class); this.valueTypes.add(NitriteId.class); diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java index 9779f202f..01d52fd5a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java @@ -193,7 +193,7 @@ public static boolean deepEquals(Object o1, Object o2) { public static T newInstance(Class type, boolean createSkeleton, NitriteMapper nitriteMapper) { try { - if (type.isPrimitive() || type.isArray() || type == String.class) { + if (type.isPrimitive() || type.isArray() || type == String.class || isBuiltInValueType(type)) { return defaultValue(type); } @@ -223,30 +223,6 @@ public static T newInstance(Class type, boolean createSkeleton, NitriteMa } } - public static boolean isValue(Object value, NitriteMapper nitriteMapper) { - try { - if (value == null) return true; // special case - - nitriteMapper.convert(value, Comparable.class); - return true; - } catch (Exception e) { - return isBuiltInValueType(value.getClass()); - } - } - - public static boolean isValueType(Class type, NitriteMapper nitriteMapper) { - try { - Object value = newInstance(type, false, nitriteMapper); - if (value != null) { - return isValue(value, nitriteMapper); - } else { - return isBuiltInValueType(type); - } - } catch (Exception e) { - return isBuiltInValueType(type); - } - } - public static boolean isBuiltInValueType(Class retType) { if (retType.isPrimitive() && retType != void.class) return true; if (Number.class.isAssignableFrom(retType)) return true; @@ -364,7 +340,7 @@ private static String getPackageName(Class clazz) { } @SuppressWarnings("unchecked") - private static T defaultValue(Class type) { + public static T defaultValue(Class type) { if (type.isPrimitive()) { switch (type.getName()) { case "boolean": diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java index d7fdcaef9..8442499e2 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java @@ -16,6 +16,8 @@ package org.dizitart.no2.common.util; +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.exceptions.IndexingException; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; @@ -23,6 +25,7 @@ import java.util.Collection; import static org.dizitart.no2.common.util.ObjectUtils.convertToObjectArray; +import static org.dizitart.no2.common.util.ObjectUtils.newInstance; import static org.dizitart.no2.common.util.StringUtils.isNullOrEmpty; /** @@ -161,6 +164,36 @@ public static void validateFilterIterableField(Iterable fieldValue, String fi } } + public static void validateProjectionType(Class type, NitriteMapper nitriteMapper) { + Object value; + try { + value = newInstance(type, false, nitriteMapper); + } catch (Exception e) { + throw new ValidationException("Invalid projection type", e); + } + + if (value == null) { + throw new ValidationException("Invalid projection type"); + } + } + + public static void validateRepositoryType(Class type, NitriteMapper nitriteMapper) { + Object value; + try { + value = newInstance(type, false, nitriteMapper); + if (value == null) { + throw new ValidationException("Cannot create new instance of type " + type); + } + + Document document = nitriteMapper.convert(value, Document.class); + if (document == null || document.size() == 0) { + throw new ValidationException("Cannot convert to document from type " + type); + } + } catch (Exception e) { + throw new ValidationException("Invalid repository type", e); + } + } + private static void validateArrayIndexItem(Object value, String field) { if (value instanceof Iterable || value.getClass().isArray()) { throw new InvalidOperationException("Nested iterables are not supported"); diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java index 4d9b9e08f..91ae7969b 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/FieldBasedFilter.java @@ -21,9 +21,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import org.dizitart.no2.common.mapper.NitriteMapper; -import org.dizitart.no2.exceptions.FilterException; -import static org.dizitart.no2.common.util.ObjectUtils.isValue; import static org.dizitart.no2.common.util.ValidationUtils.notEmpty; import static org.dizitart.no2.common.util.ValidationUtils.notNull; @@ -68,7 +66,7 @@ public Object getValue() { if (getObjectFilter()) { NitriteMapper nitriteMapper = getNitriteConfig().nitriteMapper(); validateSearchTerm(nitriteMapper, field, value); - if (isValue(value, nitriteMapper)) { + if (value instanceof Comparable) { value = nitriteMapper.convert(value, Comparable.class); } } @@ -80,11 +78,5 @@ public Object getValue() { protected void validateSearchTerm(NitriteMapper nitriteMapper, String field, Object value) { notNull(field, "field cannot be null"); notEmpty(field, "field cannot be empty"); - - if (value != null) { - if (!isValue(value, nitriteMapper) && !(value instanceof Comparable)) { - throw new FilterException("The value for field '" + field + "' is not a valid search term"); - } - } } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java b/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java index a4969e5e4..470788ba0 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/IndexValidator.java @@ -17,6 +17,7 @@ package org.dizitart.no2.repository; +import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteId; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.util.ObjectUtils; @@ -41,7 +42,7 @@ public void validate(Class fieldType, String field, NitriteMapper nitriteMapp if (fieldType.isPrimitive() || fieldType == NitriteId.class || fieldType.isInterface() - || ObjectUtils.isValueType(fieldType, nitriteMapper) +// || ObjectUtils.isValueType(fieldType, nitriteMapper) || Modifier.isAbstract(fieldType.getModifiers()) || fieldType.isArray() || Iterable.class.isAssignableFrom(fieldType)) { @@ -56,34 +57,28 @@ public void validate(Class fieldType, String field, NitriteMapper nitriteMapp public void validateId(Id id, Class fieldType, String field, NitriteMapper nitriteMapper) { if (fieldType.isPrimitive() - || fieldType == NitriteId.class - || fieldType.isInterface() - || ObjectUtils.isValueType(fieldType, nitriteMapper) - || Modifier.isAbstract(fieldType.getModifiers()) - || fieldType.isArray() - || Iterable.class.isAssignableFrom(fieldType)) { - // we will validate the solid class during insertion/update + || fieldType == NitriteId.class) { return; } - if (id.embeddedFields().length == 0) { + Object dummyValue = ObjectUtils.newInstance(fieldType, true, nitriteMapper); + Document dummyDocument = nitriteMapper.convert(dummyValue, Document.class); + + if (dummyDocument != null && dummyDocument.size() != 0 && id.embeddedFields().length == 0) { throw new IndexingException("Invalid Id field " + field); } } public void validateId(EntityId entityId, Class fieldType, String field, NitriteMapper nitriteMapper) { if (fieldType.isPrimitive() - || fieldType == NitriteId.class - || fieldType.isInterface() - || ObjectUtils.isValueType(fieldType, nitriteMapper) - || Modifier.isAbstract(fieldType.getModifiers()) - || fieldType.isArray() - || Iterable.class.isAssignableFrom(fieldType)) { - // we will validate the solid class during insertion/update + || fieldType == NitriteId.class) { return; } - if (entityId.getEmbeddedFieldNames().size() == 0) { + Object dummyValue = ObjectUtils.newInstance(fieldType, true, nitriteMapper); + Document dummyDocument = nitriteMapper.convert(dummyValue, Document.class); + + if (dummyDocument.size() != 0 && entityId.getEmbeddedFieldNames().size() == 0) { throw new IndexingException("Invalid Id field " + field); } } diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java index 92ae5f53b..e923a1db4 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/ObjectCursor.java @@ -21,17 +21,17 @@ import org.dizitart.no2.collection.FindPlan; import org.dizitart.no2.common.Lookup; import org.dizitart.no2.common.RecordStream; +import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.streams.MutatedObjectStream; -import org.dizitart.no2.common.util.ObjectUtils; import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; -import org.dizitart.no2.common.mapper.NitriteMapper; import java.lang.reflect.Modifier; import java.util.Iterator; import static org.dizitart.no2.common.util.DocumentUtils.skeletonDocument; import static org.dizitart.no2.common.util.ValidationUtils.notNull; +import static org.dizitart.no2.common.util.ValidationUtils.validateProjectionType; /** * @author Anindya Chatterjee. @@ -84,12 +84,12 @@ private Document emptyDocument(NitriteMapper nitriteMapper, Class type) { throw new ValidationException("Cannot project to array"); } else if (Modifier.isAbstract(type.getModifiers())) { throw new ValidationException("Cannot project to abstract type"); - } else if (ObjectUtils.isValueType(type, nitriteMapper)) { - throw new ValidationException("Cannot project to a value type"); } + validateProjectionType(type, nitriteMapper); + Document dummyDoc = skeletonDocument(nitriteMapper, type); - if (dummyDoc == null) { + if (dummyDoc == null || dummyDoc.size() == 0) { throw new ValidationException("Cannot project to empty type"); } else { return dummyDoc; diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java index e56a59152..f3bf6f642 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/RepositoryFactory.java @@ -19,11 +19,10 @@ import org.dizitart.no2.NitriteConfig; import org.dizitart.no2.collection.CollectionFactory; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.common.util.ObjectUtils; +import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.util.StringUtils; import org.dizitart.no2.exceptions.NitriteIOException; import org.dizitart.no2.exceptions.ValidationException; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.store.NitriteStore; import org.dizitart.no2.store.StoreCatalog; @@ -33,6 +32,7 @@ import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryName; import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryNameByDecorator; +import static org.dizitart.no2.common.util.ValidationUtils.validateRepositoryType; /** * The {@link ObjectRepository} factory. @@ -163,9 +163,8 @@ private ObjectRepository createRepository(NitriteConfig nitriteConfig, Cl String collectionName, String key) { NitriteMapper nitriteMapper = nitriteConfig.nitriteMapper(); NitriteStore store = nitriteConfig.getNitriteStore(); - if (ObjectUtils.isValueType(type, nitriteMapper)) { - throw new ValidationException("Cannot create a repository for a value type"); - } + + validateRepositoryType(type, nitriteMapper); if (store.getCollectionNames().contains(collectionName)) { throw new ValidationException("A collection with same entity name already exists"); @@ -190,9 +189,7 @@ private ObjectRepository createRepositoryByDecorator(NitriteConfig nitrit throw new ValidationException("A collection with same entity name already exists"); } - if (ObjectUtils.isValueType(entityDecorator.getEntityType(), nitriteMapper)) { - throw new ValidationException("Cannot create a repository for a value type"); - } + validateRepositoryType(entityDecorator.getEntityType(), nitriteMapper); NitriteCollection nitriteCollection = collectionFactory.getCollection(collectionName, nitriteConfig, false); diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java index 359438f86..7cca9f4f5 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/DocumentUtilsTest.java @@ -21,7 +21,6 @@ import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.mapper.SimpleDocumentMapper; -import org.dizitart.no2.exceptions.ObjectMappingException; import org.dizitart.no2.filters.ComparableFilter; import org.dizitart.no2.filters.Filter; import org.dizitart.no2.integration.Retry; @@ -88,11 +87,11 @@ public void testSkeletonDocument2() { assertEquals(0, DocumentUtils.skeletonDocument(nitriteMapper, Object.class).size()); } - @Test(expected = ObjectMappingException.class) + @Test public void testSkeletonDocument3() { SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(); Document document = DocumentUtils.skeletonDocument(nitriteMapper, Integer.class); - assertEquals(0, document.size()); + assertNull(document); } @Test diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java index 9b38d9717..0b7390751 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java @@ -39,15 +39,9 @@ import org.junit.Test; import java.io.Serializable; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.net.URI; -import java.net.URL; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; -import java.util.*; -import java.util.regex.Pattern; import static org.dizitart.no2.common.util.ObjectUtils.newInstance; import static org.junit.Assert.*; @@ -135,44 +129,44 @@ public void testNewInstance() { System.out.println(type); } - @Test - public void testIsValueType() { - SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(); - assertFalse(ObjectUtils.isValueType(Object.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Number.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Byte.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Short.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Integer.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Long.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Float.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Double.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(BigDecimal.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(BigInteger.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Boolean.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Character.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(String.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Date.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(URL.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(URI.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Currency.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Calendar.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(StringBuffer.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(StringBuilder.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Locale.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Void.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(UUID.class, simpleDocumentMapper)); - assertTrue(ObjectUtils.isValueType(Pattern.class, simpleDocumentMapper)); - assertFalse(ObjectUtils.isValueType(NitriteId.class, simpleDocumentMapper)); - assertFalse(ObjectUtils.isValueType(Document.class, simpleDocumentMapper)); - - simpleDocumentMapper.addValueType(ValidEntity5.class); - assertTrue(ObjectUtils.isValueType(ValidEntity5.class, simpleDocumentMapper)); - } - - @Test - public void testIsValue() { - assertFalse(ObjectUtils.isValueType(Object.class, new SimpleDocumentMapper())); - } +// @Test +// public void testIsValueType() { +// SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(); +// assertFalse(ObjectUtils.isValueType(Object.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Number.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Byte.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Short.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Integer.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Long.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Float.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Double.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(BigDecimal.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(BigInteger.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Boolean.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Character.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(String.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Date.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(URL.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(URI.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Currency.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Calendar.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(StringBuffer.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(StringBuilder.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Locale.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Void.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(UUID.class, simpleDocumentMapper)); +// assertTrue(ObjectUtils.isValueType(Pattern.class, simpleDocumentMapper)); +// assertFalse(ObjectUtils.isValueType(NitriteId.class, simpleDocumentMapper)); +// assertFalse(ObjectUtils.isValueType(Document.class, simpleDocumentMapper)); +// +// simpleDocumentMapper.addValueType(ValidEntity5.class); +// assertTrue(ObjectUtils.isValueType(ValidEntity5.class, simpleDocumentMapper)); +// } +// +// @Test +// public void testIsValue() { +// assertFalse(ObjectUtils.isValueType(Object.class, new SimpleDocumentMapper())); +// } @Test public void testIsCompatibleTypes() { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java index 3bb030c56..7f5f99c5f 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -35,6 +35,7 @@ import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexOptions; import org.dizitart.no2.index.IndexType; +import org.dizitart.no2.integration.repository.data.EmptyClass; import org.dizitart.no2.repository.ObjectRepository; import org.dizitart.no2.repository.annotations.Id; import org.dizitart.no2.repository.annotations.Index; @@ -78,6 +79,7 @@ public void setUp() throws ParseException { SimpleDocumentMapper nitriteMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); nitriteMapper.registerEntityConverter(new Receipt.ReceiptConverter()); nitriteMapper.registerEntityConverter(new CompatChild.CompatChildConverter()); + nitriteMapper.registerEntityConverter(new EmptyClass.Converter()); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH); @@ -126,9 +128,14 @@ public void testListCollectionNames() { assertEquals(collectionNames.size(), 1); } - @Test + @Test(expected = ValidationException.class) public void testListRepositories() { db.getRepository(getClass()); + } + + @Test + public void testListRepositories2() { + db.getRepository(Receipt.class); Set repositories = db.listRepositories(); assertEquals(repositories.size(), 1); } @@ -139,10 +146,15 @@ public void testHasCollection() { assertFalse(db.hasCollection("lucene" + INTERNAL_NAME_SEPARATOR + "test")); } - @Test + @Test(expected = ValidationException.class) public void testHasRepository() { db.getRepository(getClass()); - assertTrue(db.hasRepository(getClass())); + } + + @Test + public void testHasRepository2() { + db.getRepository(Receipt.class); + assertTrue(db.hasRepository(Receipt.class)); assertFalse(db.hasRepository(String.class)); } @@ -162,20 +174,30 @@ public void testGetCollection() { assertEquals(collection.getName(), "test-collection"); } - @Test + @Test(expected = ValidationException.class) public void testGetRepository() { - ObjectRepository repository = db.getRepository(NitriteTest.class); - assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); + ObjectRepository repository = db.getRepository(EmptyClass.class); } @Test + public void testGetRepository2() { + ObjectRepository repository = db.getRepository(Receipt.class); + assertNotNull(repository); + assertEquals(repository.getType(), Receipt.class); + } + + @Test(expected = ValidationException.class) public void testGetRepositoryWithKey() { - ObjectRepository repository = db.getRepository(NitriteTest.class, "key"); + ObjectRepository repository = db.getRepository(EmptyClass.class, "key"); + } + + @Test + public void testGetRepositoryWithKey2() { + ObjectRepository repository = db.getRepository(Receipt.class, "key"); assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); - assertFalse(db.hasRepository(NitriteTest.class)); - assertTrue(db.hasRepository(NitriteTest.class, "key")); + assertEquals(repository.getType(), Receipt.class); + assertFalse(db.hasRepository(Receipt.class)); + assertTrue(db.hasRepository(Receipt.class, "key")); } @Test @@ -191,13 +213,13 @@ public void testMultipleGetCollection() { @Test public void testMultipleGetRepository() { - ObjectRepository repository = db.getRepository(NitriteTest.class); + ObjectRepository repository = db.getRepository(Receipt.class); assertNotNull(repository); - assertEquals(repository.getType(), NitriteTest.class); + assertEquals(repository.getType(), Receipt.class); - ObjectRepository repository2 = db.getRepository(NitriteTest.class); + ObjectRepository repository2 = db.getRepository(Receipt.class); assertNotNull(repository2); - assertEquals(repository2.getType(), NitriteTest.class); + assertEquals(repository2.getType(), Receipt.class); } @Test(expected = ValidationException.class) @@ -216,14 +238,14 @@ public void testGetCollectionNullStore() { public void testGetRepositoryNullStore() { db = Nitrite.builder().openOrCreate(); db.close(); - db.getRepository(NitriteTest.class); + db.getRepository(EmptyClass.class); } @Test(expected = NitriteIOException.class) public void testGetKeyedRepositoryNullStore() { db = Nitrite.builder().openOrCreate(); db.close(); - db.getRepository(NitriteTest.class, "key"); + db.getRepository(EmptyClass.class, "key"); } @Test(expected = NitriteIOException.class) @@ -447,10 +469,12 @@ public Receipt fromDocument(Document document, NitriteMapper nitriteMapper) { if (document != null) { Object status = document.get("status"); - if (status instanceof Receipt.Status) { - receipt.status = (Receipt.Status) status; - } else { - receipt.status = Receipt.Status.valueOf(status.toString()); + if (status != null) { + if (status instanceof Receipt.Status) { + receipt.status = (Receipt.Status) status; + } else { + receipt.status = Receipt.Status.valueOf(status.toString()); + } } receipt.clientRef = document.get("clientRef", String.class); receipt.synced = document.get("synced", Boolean.class); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java index 7096ddaaf..e6a2a58d4 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/InternalClass.java @@ -29,7 +29,7 @@ @Data class InternalClass { @Id - private long id; + private Long id; private String name; public static class Converter implements EntityConverter { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java index c6825342b..a071dd90b 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryNegativeTest.java @@ -66,7 +66,7 @@ public void close() throws Exception { db = null; } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testWithCircularReference() { ObjectRepository repository = db.getRepository(WithCircularReference.class); @@ -86,7 +86,7 @@ public void testWithCircularReference() { } } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testWithCustomConstructor() { ObjectRepository repository = db.getRepository(WithCustomConstructor.class); @@ -139,18 +139,6 @@ public void testFindResultRemove() { result.iterator().remove(); } - @Test(expected = IndexingException.class) - public void testWithObjectId() { - ObjectRepository repository = db.getRepository(WithObjectId.class); - WithOutId id = new WithOutId(); - id.setName("test"); - id.setNumber(1); - - WithObjectId object = new WithObjectId(); - object.setWithOutId(id); - repository.insert(object); - } - @Test(expected = NotIdentifiableException.class) public void testUpdateNoId() { ObjectRepository repository = db.getRepository(WithOutId.class); @@ -169,7 +157,7 @@ public void testRemoveNoId() { repository.remove(object); } - @Test(expected = ObjectMappingException.class) + @Test(expected = ValidationException.class) public void testProjectionFailedInstantiate() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); @@ -192,7 +180,7 @@ public void testGetByNullId() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); WithPublicField instance = repository.getById(null); @@ -228,7 +216,7 @@ public void testGetByWrongIdType() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index a51aec703..c9a20d3bb 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -123,7 +123,7 @@ public void testWithOutId() { ObjectRepository repository = db.getRepository(WithOutId.class); WithOutId object = new WithOutId(); object.setName("test"); - object.setNumber(2); + object.setNumber(2L); repository.insert(object); for (WithOutId instance : repository.find()) { @@ -137,7 +137,7 @@ public void testWithPublicField() { ObjectRepository repository = db.getRepository(WithPublicField.class); WithPublicField object = new WithPublicField(); object.name = "test"; - object.number = 2; + object.number = 2L; repository.insert(object); WithPublicField instance = repository.getById("test"); @@ -149,7 +149,7 @@ public void testWithPublicField() { public void testWithTransientField() { ObjectRepository repository = db.getRepository(WithTransientField.class); WithTransientField object = new WithTransientField(); - object.setNumber(2); + object.setNumber(2L); object.setName("test"); repository.insert(object); @@ -187,7 +187,7 @@ public void testWriteThousandRecords() { public void testWithPackagePrivateClass() { ObjectRepository repository = db.getRepository(InternalClass.class); InternalClass internalClass = new InternalClass(); - internalClass.setId(1); + internalClass.setId(1L); internalClass.setName("name"); repository.insert(internalClass); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java index 538c20d13..46dcdbcb0 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryFactoryTest.java @@ -62,7 +62,7 @@ public void testNullType() { factory.getRepository(db.getConfig(), (Class) null, "dummy"); } - @Test + @Test(expected = ValidationException.class) public void testNullCollection() { RepositoryFactory factory = new RepositoryFactory(new CollectionFactory(new LockService())); db = TestUtil.createDb(); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java index 095e08ae6..f195d1a9d 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositoryModificationTest.java @@ -551,7 +551,7 @@ public void testDelete() { /* * Upsert Use Cases * - * 1. Object does not exists + * 1. Object does not exist * a. if upsert true, it will insert * b. if upsert false, nothing happens * 2. Object exists @@ -564,12 +564,12 @@ public void testDelete() { public void testUpdateObjectNotExistsUpsertTrue() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(2); + a.setId(2L); a.setName("second"); // it will insert as new object @@ -581,18 +581,18 @@ public void testUpdateObjectNotExistsUpsertTrue() { public void testUpdateObjectNotExistsUpsertFalse() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(2); + a.setId(2L); a.setName("second"); // no changes will happen to repository repo.update(a, false); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1L); assertEquals(repo.find().firstOrNull().getName(), "first"); } @@ -600,18 +600,18 @@ public void testUpdateObjectNotExistsUpsertFalse() { public void testUpdateObjectExistsUpsertTrue() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("second"); // update existing object, keep id same repo.update(a, true); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1L); assertEquals(repo.find().firstOrNull().getName(), "second"); } @@ -619,18 +619,18 @@ public void testUpdateObjectExistsUpsertTrue() { public void testUpdateObjectExistsUpsertFalse() { ObjectRepository repo = db.getRepository(InternalClass.class); InternalClass a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("first"); repo.insert(a); a = new InternalClass(); - a.setId(1); + a.setId(1L); a.setName("second"); // update existing object, keep id same repo.update(a, false); assertEquals(repo.size(), 1); - assertEquals(repo.find().firstOrNull().getId(), 1); + assertEquals(repo.find().firstOrNull().getId().longValue(), 1L); assertEquals(repo.find().firstOrNull().getName(), "second"); } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java index d89a0b123..70f1b39d7 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/RepositorySearchTest.java @@ -366,17 +366,17 @@ public void testElemMatchFilter() { ObjectRepository repository = db.getRepository(ElemMatch.class); ElemMatch e1 = new ElemMatch(); - e1.setId(1); + e1.setId(1L); e1.setStrArray(new String[]{"a", "b"}); e1.setProductScores(new ProductScore[]{score1, score4}); ElemMatch e2 = new ElemMatch(); - e2.setId(2); + e2.setId(2L); e2.setStrArray(new String[]{"d", "e"}); e2.setProductScores(new ProductScore[]{score2, score5}); ElemMatch e3 = new ElemMatch(); - e3.setId(3); + e3.setId(3L); e3.setStrArray(new String[]{"a", "f"}); e3.setProductScores(new ProductScore[]{score3, score6}); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index 1dc5bf294..a0ac5a6bb 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -51,11 +51,11 @@ public class UniversalTextTokenizerTest extends BaseObjectRepositoryTest { @Override public void setUp() { openDb(); - - textRepository = db.getRepository(TextData.class); SimpleDocumentMapper documentMapper = (SimpleDocumentMapper) db.getConfig().nitriteMapper(); documentMapper.registerEntityConverter(new TextData.Converter()); + textRepository = db.getRepository(TextData.class); + for (int i = 0; i < 10; i++) { TextData data = new TextData(); data.id = i; @@ -161,7 +161,7 @@ public void testUniversalFullTextIndexing() { @Index(value = "text", type = IndexType.FULL_TEXT) ) public static class TextData { - public int id; + public Integer id; public String text; public static class Converter implements EntityConverter { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java index 3ef376e44..92c710d2e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ElemMatch.java @@ -30,7 +30,7 @@ */ @Data public class ElemMatch { - private long id; + private Long id; private String[] strArray; private ProductScore[] productScores; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java new file mode 100644 index 000000000..9bf6bcbf6 --- /dev/null +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/EmptyClass.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.integration.repository.data; + +import org.dizitart.no2.collection.Document; +import org.dizitart.no2.common.mapper.EntityConverter; +import org.dizitart.no2.common.mapper.NitriteMapper; + +public class EmptyClass { + public static class Converter implements EntityConverter { + + @Override + public Class getEntityType() { + return EmptyClass.class; + } + + @Override + public Document toDocument(EmptyClass entity, NitriteMapper nitriteMapper) { + return Document.createDocument(); + } + + @Override + public EmptyClass fromDocument(Document document, NitriteMapper nitriteMapper) { + return new EmptyClass(); + } + } +} diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java index 9ebe19057..23c9d16ff 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ProductScore.java @@ -30,7 +30,7 @@ @Setter public class ProductScore { private String product; - private int score; + private Integer score; public ProductScore() { } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java index 9e74e8fb0..33dc06073 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/StressRecord.java @@ -30,9 +30,9 @@ @Setter public class StressRecord { private String firstName; - private boolean processed; + private Boolean processed; private String lastName; - private boolean failed; + private Boolean failed; private String notes; public static class Converter implements EntityConverter { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java index e5ca44b78..dfac35bfa 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithNullId.java @@ -32,7 +32,7 @@ public class WithNullId { @Id private String name; - private long number; + private Long number; public static class Converter implements EntityConverter { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java index 2ce03bf24..e6783a89e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutGetterSetter.java @@ -28,11 +28,11 @@ @EqualsAndHashCode public class WithOutGetterSetter { private String name; - private long number; + private Long number; public WithOutGetterSetter() { name = "test"; - number = 2; + number = 2L; } public static class Converter implements EntityConverter { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java index 645dd0b45..4685e7726 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithOutId.java @@ -30,7 +30,7 @@ @Setter public class WithOutId implements Comparable { private String name; - private long number; + private Long number; @Override public int compareTo(WithOutId o) { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java index 28febec9e..de08cf19b 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPrivateConstructor.java @@ -28,14 +28,14 @@ @EqualsAndHashCode public class WithPrivateConstructor { private String name; - private long number; + private Long number; private WithPrivateConstructor() { name = "test"; - number = 2; + number = 2L; } - public static WithPrivateConstructor create(final String name, final long number) { + public static WithPrivateConstructor create(final String name, final Long number) { WithPrivateConstructor obj = new WithPrivateConstructor(); obj.number = number; obj.name = name; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java index eae21f1de..81b69a0c1 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithPublicField.java @@ -28,7 +28,7 @@ public class WithPublicField { @Id public String name; - public long number; + public Long number; public static class Converter implements EntityConverter { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java index 35dde5a91..76ea86243 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/WithTransientField.java @@ -32,7 +32,7 @@ public class WithTransientField { private transient String name; @Id - private long number; + private Long number; public static class Converter implements EntityConverter { diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java index ed2ca0aa5..04ef99fbb 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java @@ -20,10 +20,10 @@ import lombok.Data; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.NitriteCollection; -import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.integration.repository.data.ClassA; +import org.dizitart.no2.integration.repository.data.ClassBConverter; import org.junit.Before; import org.junit.Test; @@ -40,7 +40,9 @@ public class EntityDecoratorScannerTest { @Before public void setUp() { - NitriteMapper nitriteMapper = new SimpleDocumentMapper(); + SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(); + nitriteMapper.registerEntityConverter(new ClassA.ClassAConverter()); + nitriteMapper.registerEntityConverter(new ClassBConverter()); collection = Nitrite.builder().openOrCreate().getCollection("test"); reader = new EntityDecoratorScanner(new EntityADecorator(), collection, nitriteMapper); } diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java index 8f8957ccc..4ec228f17 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/ObjectCursorTest.java @@ -81,9 +81,7 @@ public void testProject() { @Test public void testProject3() { - Class forNameResult = Object.class; - Class forNameResult1 = Object.class; - SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(forNameResult, forNameResult1, Object.class); + SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(); RecordStream> recordStream = (RecordStream>) mock( RecordStream.class); DocumentStream cursor = new DocumentStream(recordStream, new ProcessorChain()); From 13ab4141dc9d15c03afd5d3a0119e3e3b03b00f8 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 19 Oct 2022 00:00:16 +0530 Subject: [PATCH 68/78] potassium nitrite test fix --- .../no2/common/util/ValidationUtils.java | 6 ++++ .../org/dizitart/kno2/BackportJavaTimeTest.kt | 2 +- .../kotlin/org/dizitart/kno2/FilterTest.kt | 2 +- .../kotlin/org/dizitart/kno2/NitriteTest.kt | 28 +++++++++---------- .../org/dizitart/kno2/ObjectFilterTest.kt | 12 ++++---- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java index 8442499e2..12e3a5aa2 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java @@ -22,6 +22,7 @@ import org.dizitart.no2.exceptions.InvalidOperationException; import org.dizitart.no2.exceptions.ValidationException; +import java.lang.reflect.Modifier; import java.util.Collection; import static org.dizitart.no2.common.util.ObjectUtils.convertToObjectArray; @@ -180,6 +181,11 @@ public static void validateProjectionType(Class type, NitriteMapper nitriteMa public static void validateRepositoryType(Class type, NitriteMapper nitriteMapper) { Object value; try { + if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) { + // defer validation during insertion + return; + } + value = newInstance(type, false, nitriteMapper); if (value == null) { throw new ValidationException("Cannot create new instance of type " + type); diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt index 9bd9eda24..4a07b7cfc 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt @@ -41,7 +41,7 @@ class BackportJavaTimeTest { @Index(value = ["time"], type = IndexType.NON_UNIQUE) data class TestData( @Id val id: String = UUID.randomUUID().toString(), - val time: LocalDateTime + val time: LocalDateTime = LocalDateTime.now() ) class ThreeTenAbpModule : SimpleModule() { diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/FilterTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/FilterTest.kt index 3286e8f67..48fcf26ef 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/FilterTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/FilterTest.kt @@ -322,7 +322,7 @@ class FilterTest : BaseTest() { @Test fun testBetweenFilter() { @Getter - class TestData(private val age: Date) + class TestData(private val age: Date = Date()) val data1 = TestData(GregorianCalendar(2020, Calendar.JANUARY, 11).time) val data2 = TestData(GregorianCalendar(2021, Calendar.FEBRUARY, 12).time) diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/NitriteTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/NitriteTest.kt index de873a1b9..18026fc7a 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/NitriteTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/NitriteTest.kt @@ -201,29 +201,29 @@ abstract class SomeAbsClass( @InheritIndices class MyClass( - override val id: UUID, - override val name: String, - override val checked: Boolean) : SomeAbsClass(id, name) + override val id: UUID = UUID.randomUUID(), + override val name: String = "", + override val checked: Boolean = false) : SomeAbsClass(id, name) @InheritIndices class MyClass2( - override val id: UUID, - override val name: String, - override val checked: Boolean, - val importance: Int + override val id: UUID = UUID.randomUUID(), + override val name: String = "", + override val checked: Boolean = false, + val importance: Int = 0 ) : SomeAbsClass(id, name) data class CaObject( - @Id val localId: UUID, - val name: String + @Id val localId: UUID = UUID.randomUUID(), + val name: String = "" ) @Indices(value = [(Index(value = ["time"], type = IndexType.UNIQUE))]) data class ClassWithLocalDateTime( - val name: String, - val time: LocalDateTime + val name: String = "", + val time: LocalDateTime = LocalDateTime.now() ) -data class NestedObjects(var ob1: String, @Id val id: String, val list: List) -data class TempObject(val name: String, val aga: Int, val add: LevelUnder) -data class LevelUnder(val street: String, val number: Int) \ No newline at end of file +data class NestedObjects(var ob1: String = "", @Id val id: String = "", val list: List = listOf()) +data class TempObject(val name: String = "", val aga: Int = 0, val add: LevelUnder = LevelUnder()) +data class LevelUnder(val street: String = "", val number: Int = 0) \ No newline at end of file diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/ObjectFilterTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/ObjectFilterTest.kt index 0aa0b6990..0de1fde18 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/ObjectFilterTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/ObjectFilterTest.kt @@ -322,18 +322,18 @@ class ObjectFilterTest : BaseTest() { } @Indices(Index(value = ["text"], type = IndexType.FULL_TEXT)) -data class TestData(@Id val id: Int, val text: String, val list: List = listOf()) +data class TestData(@Id val id: Int = 0, val text: String = "", val list: List = listOf()) -class ListData(val name: String, val score: Int) +class ListData(val name: String = "", val score: Int = 0) data class SimpleObject( - @Id val id: UUID, - val value: Boolean + @Id val id: UUID = UUID.randomUUID(), + val value: Boolean = false ) @Index(value = ["geometry"], type = SpatialIndexer.SPATIAL_INDEX) data class SpatialData( - @Id val id: Long, - val geometry: Geometry + @Id val id: Long = 0, + val geometry: Geometry? = null ) From fc2ad8669446d2efb64bfa8a7edbef35045b9513 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 19 Oct 2022 12:22:04 +0530 Subject: [PATCH 69/78] test cases added --- .../no2/common/utils/ObjectUtilsTest.java | 70 ++++++++++++++++ .../dizitart/no2/common/util/ObjectUtils.java | 6 +- .../no2/common/util/ObjectUtilsTest.java | 79 +++++++++---------- 3 files changed, 114 insertions(+), 41 deletions(-) create mode 100644 nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/utils/ObjectUtilsTest.java diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/utils/ObjectUtilsTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/utils/ObjectUtilsTest.java new file mode 100644 index 000000000..ae62377b6 --- /dev/null +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/common/utils/ObjectUtilsTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017-2022 Nitrite author or authors. + * + * 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 org.dizitart.no2.common.utils; + +import org.dizitart.no2.common.mapper.JacksonMapper; +import org.dizitart.no2.integration.repository.data.Book; +import org.junit.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; +import java.util.*; +import java.util.regex.Pattern; + +import static org.dizitart.no2.common.util.ObjectUtils.newInstance; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +public class ObjectUtilsTest { + @Test + public void testNewInstance() { + JacksonMapper mapper = new JacksonMapper(); + + assertNotNull(newInstance(Object.class, false, mapper)); + assertNotNull(newInstance(Book.class, false, mapper)); + + assertNull(newInstance(byte[].class, false, mapper)); + assertNull(newInstance(Number.class, false, mapper)); + assertNull(newInstance(Byte.class, false, mapper)); + assertNull(newInstance(Short.class, false, mapper)); + assertNull(newInstance(Integer.class, false, mapper)); + assertNull(newInstance(Long.class, false, mapper)); + assertNull(newInstance(Float.class, false, mapper)); + assertNull(newInstance(Double.class, false, mapper)); + assertNull(newInstance(BigDecimal.class, false, mapper)); + assertNull(newInstance(BigInteger.class, false, mapper)); + assertNull(newInstance(Boolean.class, false, mapper)); + assertNull(newInstance(Character.class, false, mapper)); + assertNull(newInstance(String.class, false, mapper)); + assertNull(newInstance(Date.class, false, mapper)); + assertNull(newInstance(URL.class, false, mapper)); + assertNull(newInstance(URI.class, false, mapper)); + assertNull(newInstance(Currency.class, false, mapper)); + assertNull(newInstance(Calendar.class, false, mapper)); + assertNull(newInstance(StringBuffer.class, false, mapper)); + assertNull(newInstance(StringBuilder.class, false, mapper)); + assertNull(newInstance(Locale.class, false, mapper)); + assertNull(newInstance(Void.class, false, mapper)); + assertNull(newInstance(UUID.class, false, mapper)); + assertNull(newInstance(Pattern.class, false, mapper)); + + assertNull(newInstance(GregorianCalendar.class, false, mapper)); + } +} diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java index 01d52fd5a..1385617d3 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ObjectUtils.java @@ -228,7 +228,11 @@ public static boolean isBuiltInValueType(Class retType) { if (Number.class.isAssignableFrom(retType)) return true; if (byte[].class.isAssignableFrom(retType)) return true; if (Enum.class.isAssignableFrom(retType)) return true; - return builtInTypes().contains(retType); + if (builtInTypes().contains(retType)) return true; + for (Class builtInType : builtInTypes()) { + if (builtInType.isAssignableFrom(retType)) return true; + } + return false; } public static List> builtInTypes() { diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java index 0b7390751..d692de838 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java @@ -26,7 +26,9 @@ import org.dizitart.no2.common.mapper.EntityConverter; import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.mapper.SimpleDocumentMapper; +import org.dizitart.no2.exceptions.ObjectMappingException; import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.integration.NitriteTest; import org.dizitart.no2.integration.repository.data.ChildClass; import org.dizitart.no2.integration.repository.data.Company; import org.dizitart.no2.integration.repository.data.Employee; @@ -39,9 +41,15 @@ import org.junit.Test; import java.io.Serializable; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.net.URL; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.*; +import java.util.regex.Pattern; import static org.dizitart.no2.common.util.ObjectUtils.newInstance; import static org.junit.Assert.*; @@ -126,48 +134,39 @@ public void testNewInstance() { mapper.registerEntityConverter(new Note.NoteConverter()); EnclosingType type = newInstance(EnclosingType.class, true, mapper); - System.out.println(type); + assertNotNull(type); + + assertThrows(ObjectMappingException.class, () -> newInstance(Object.class, false, mapper)); + assertThrows(ObjectMappingException.class, () -> newInstance(NitriteTest.Receipt.class, false, mapper)); + + assertNull(newInstance(byte[].class, false, mapper)); + assertNull(newInstance(Number.class, false, mapper)); + assertNull(newInstance(Byte.class, false, mapper)); + assertNull(newInstance(Short.class, false, mapper)); + assertNull(newInstance(Integer.class, false, mapper)); + assertNull(newInstance(Long.class, false, mapper)); + assertNull(newInstance(Float.class, false, mapper)); + assertNull(newInstance(Double.class, false, mapper)); + assertNull(newInstance(BigDecimal.class, false, mapper)); + assertNull(newInstance(BigInteger.class, false, mapper)); + assertNull(newInstance(Boolean.class, false, mapper)); + assertNull(newInstance(Character.class, false, mapper)); + assertNull(newInstance(String.class, false, mapper)); + assertNull(newInstance(Date.class, false, mapper)); + assertNull(newInstance(URL.class, false, mapper)); + assertNull(newInstance(URI.class, false, mapper)); + assertNull(newInstance(Currency.class, false, mapper)); + assertNull(newInstance(Calendar.class, false, mapper)); + assertNull(newInstance(StringBuffer.class, false, mapper)); + assertNull(newInstance(StringBuilder.class, false, mapper)); + assertNull(newInstance(Locale.class, false, mapper)); + assertNull(newInstance(Void.class, false, mapper)); + assertNull(newInstance(UUID.class, false, mapper)); + assertNull(newInstance(Pattern.class, false, mapper)); + + assertNull(newInstance(GregorianCalendar.class, false, mapper)); } -// @Test -// public void testIsValueType() { -// SimpleDocumentMapper simpleDocumentMapper = new SimpleDocumentMapper(); -// assertFalse(ObjectUtils.isValueType(Object.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Number.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Byte.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Short.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Integer.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Long.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Float.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Double.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(BigDecimal.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(BigInteger.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Boolean.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Character.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(String.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Date.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(URL.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(URI.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Currency.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Calendar.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(StringBuffer.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(StringBuilder.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Locale.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Void.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(UUID.class, simpleDocumentMapper)); -// assertTrue(ObjectUtils.isValueType(Pattern.class, simpleDocumentMapper)); -// assertFalse(ObjectUtils.isValueType(NitriteId.class, simpleDocumentMapper)); -// assertFalse(ObjectUtils.isValueType(Document.class, simpleDocumentMapper)); -// -// simpleDocumentMapper.addValueType(ValidEntity5.class); -// assertTrue(ObjectUtils.isValueType(ValidEntity5.class, simpleDocumentMapper)); -// } -// -// @Test -// public void testIsValue() { -// assertFalse(ObjectUtils.isValueType(Object.class, new SimpleDocumentMapper())); -// } - @Test public void testIsCompatibleTypes() { Class type1 = Object.class; From dc0ee969c39c02f456247c69dd946805059f3be3 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 19 Oct 2022 13:36:01 +0530 Subject: [PATCH 70/78] validation refined --- .../no2/common/util/ValidationUtils.java | 10 ++-- .../no2/common/util/ValidationUtilsTest.java | 47 +++++++++++++++++-- .../integration/repository/data/ClassB.java | 1 + 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java index 12e3a5aa2..f6e3c7ceb 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/ValidationUtils.java @@ -25,8 +25,7 @@ import java.lang.reflect.Modifier; import java.util.Collection; -import static org.dizitart.no2.common.util.ObjectUtils.convertToObjectArray; -import static org.dizitart.no2.common.util.ObjectUtils.newInstance; +import static org.dizitart.no2.common.util.ObjectUtils.*; import static org.dizitart.no2.common.util.StringUtils.isNullOrEmpty; /** @@ -176,12 +175,17 @@ public static void validateProjectionType(Class type, NitriteMapper nitriteMa if (value == null) { throw new ValidationException("Invalid projection type"); } + + Document document = nitriteMapper.convert(value, Document.class); + if (document == null || document.size() == 0) { + throw new ValidationException("Cannot project to empty type " + type); + } } public static void validateRepositoryType(Class type, NitriteMapper nitriteMapper) { Object value; try { - if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) { + if (type.isInterface() || (Modifier.isAbstract(type.getModifiers()) && !isBuiltInValueType(type))) { // defer validation during insertion return; } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ValidationUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ValidationUtilsTest.java index cd7721924..10c373a75 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ValidationUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ValidationUtilsTest.java @@ -16,15 +16,18 @@ package org.dizitart.no2.common.util; -import org.dizitart.no2.integration.Retry; +import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.exceptions.ValidationException; +import org.dizitart.no2.integration.Retry; +import org.dizitart.no2.integration.repository.data.ClassA; +import org.dizitart.no2.integration.repository.data.ClassBConverter; +import org.dizitart.no2.integration.repository.data.ClassC; +import org.dizitart.no2.integration.repository.data.EmptyClass; import org.junit.Rule; import org.junit.Test; -import static org.dizitart.no2.common.util.ValidationUtils.notEmpty; -import static org.dizitart.no2.common.util.ValidationUtils.notNull; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.dizitart.no2.common.util.ValidationUtils.*; +import static org.junit.Assert.*; /** * @author Anindya Chatterjee. @@ -73,4 +76,38 @@ public void testCharSequenceNotEmpty() { assertTrue(invalid); } } + + @Test + public void testValidateProjectionType() { + SimpleDocumentMapper documentMapper = new SimpleDocumentMapper(); + documentMapper.registerEntityConverter(new ClassA.ClassAConverter()); + documentMapper.registerEntityConverter(new ClassBConverter()); + documentMapper.registerEntityConverter(new EmptyClass.Converter()); + + validateProjectionType(ClassA.class, documentMapper); + + assertThrows(ValidationException.class, () -> validateProjectionType(EmptyClass.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateProjectionType(ClassC.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateProjectionType(String.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateProjectionType(Number.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateProjectionType(Integer.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateProjectionType(Object.class, documentMapper)); + } + + @Test + public void testValidateRepositoryType() { + SimpleDocumentMapper documentMapper = new SimpleDocumentMapper(); + documentMapper.registerEntityConverter(new ClassA.ClassAConverter()); + documentMapper.registerEntityConverter(new ClassBConverter()); + documentMapper.registerEntityConverter(new EmptyClass.Converter()); + + validateRepositoryType(ClassA.class, documentMapper); + + assertThrows(ValidationException.class, () -> validateRepositoryType(EmptyClass.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateRepositoryType(ClassC.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateRepositoryType(String.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateRepositoryType(Number.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateRepositoryType(Integer.class, documentMapper)); + assertThrows(ValidationException.class, () -> validateRepositoryType(Object.class, documentMapper)); + } } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java index 51cc9b416..db398e31a 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ClassB.java @@ -28,6 +28,7 @@ class ClassB implements Comparable { @Getter @Setter private int number; + @Getter @Setter private String text; From a0ce0b88382f21c01ab7523c7e16c414ad969e7b Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 8 Nov 2022 12:03:48 +0530 Subject: [PATCH 71/78] reafctoring --- .../dizitart/no2/integration/NitriteTest.java | 2 +- .../no2/integration/migrate/NewClass.java | 6 +++--- .../no2/integration/migrate/OldClass.java | 8 ++++---- .../repository/CustomFieldSeparatorTest.java | 6 +++--- .../repository/ObjectRepositoryTest.java | 4 ++-- .../repository/UniversalTextTokenizerTest.java | 2 +- .../no2/integration/repository/data/Book.java | 6 +++--- .../no2/integration/repository/data/Company.java | 2 +- .../integration/repository/data/Employee.java | 6 +++--- .../integration/repository/data/ParentClass.java | 2 +- .../repository/data/PersonEntity.java | 4 ++-- .../repository/data/RepeatableIndexTest.java | 6 +++--- .../repository/data/SuperDuperClass.java | 2 +- .../test/java/org/dizitart/no2/NitriteTest.java | 2 +- .../no2/integration/NitriteBuilderTest.java | 4 ++-- .../no2/integration/NitriteStressTest.java | 6 +++--- .../no2/integration/migration/NewClass.java | 6 +++--- .../no2/integration/migration/OldClass.java | 8 ++++---- .../repository/CustomFieldSeparatorTest.java | 6 +++--- .../repository/ObjectRepositoryTest.java | 4 ++-- .../repository/UniversalTextTokenizerTest.java | 2 +- .../no2/integration/repository/data/Book.java | 6 +++--- .../no2/integration/repository/data/Company.java | 2 +- .../integration/repository/data/Employee.java | 6 +++--- .../integration/repository/data/ParentClass.java | 2 +- .../repository/data/PersonEntity.java | 4 ++-- .../repository/data/RepeatableIndexTest.java | 6 +++--- .../repository/data/SuperDuperClass.java | 2 +- .../test/java/org/dizitart/no2/NitriteTest.java | 2 +- .../no2/integration/NitriteBuilderTest.java | 4 ++-- .../no2/integration/NitriteStressTest.java | 6 +++--- .../no2/integration/migrate/NewClass.java | 6 +++--- .../no2/integration/migrate/OldClass.java | 8 ++++---- .../repository/CustomFieldSeparatorTest.java | 6 +++--- .../repository/ObjectRepositoryTest.java | 4 ++-- .../repository/UniversalTextTokenizerTest.java | 2 +- .../no2/integration/repository/data/Book.java | 6 +++--- .../no2/integration/repository/data/Company.java | 2 +- .../integration/repository/data/Employee.java | 6 +++--- .../integration/repository/data/ParentClass.java | 2 +- .../repository/data/PersonEntity.java | 4 ++-- .../repository/data/RepeatableIndexTest.java | 6 +++--- .../repository/data/SuperDuperClass.java | 2 +- .../org/dizitart/no2/spatial/SpatialData.java | 2 +- .../java/org/dizitart/no2/support/Company.java | 2 +- .../java/org/dizitart/no2/support/Employee.java | 6 +++--- .../no2/repository/AnnotationScanner.java | 4 ++-- .../no2/repository/annotations/Index.java | 2 +- .../no2/common/util/ObjectUtilsTest.java | 2 +- .../dizitart/no2/integration/NitriteTest.java | 2 +- .../org/dizitart/no2/integration/StressTest.java | 6 +++--- .../collection/CollectionFindTest.java | 16 ++++++++++++++++ .../repository/CustomFieldSeparatorTest.java | 6 +++--- .../repository/ObjectRepositoryTest.java | 4 ++-- .../repository/UniversalTextTokenizerTest.java | 2 +- .../no2/integration/repository/data/Book.java | 6 +++--- .../no2/integration/repository/data/Company.java | 2 +- .../integration/repository/data/Employee.java | 6 +++--- .../integration/repository/data/ParentClass.java | 2 +- .../repository/data/PersonEntity.java | 4 ++-- .../repository/data/RepeatableIndexTest.java | 6 +++--- .../repository/data/SuperDuperClass.java | 2 +- .../org/dizitart/kno2/BackportJavaTimeTest.kt | 2 +- .../test/kotlin/org/dizitart/kno2/NitriteTest.kt | 6 ++---- .../kotlin/org/dizitart/kno2/ObjectFilterTest.kt | 4 ++-- 65 files changed, 148 insertions(+), 134 deletions(-) diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/NitriteTest.java index 8dffef69d..2f86eafda 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/NitriteTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -246,7 +246,7 @@ public void testGetCollectionInvalidName() { @NoArgsConstructor @AllArgsConstructor @Indices({ - @Index(value = "synced", type = IndexType.NON_UNIQUE) + @Index(fields = "synced", type = IndexType.NON_UNIQUE) }) public static class Receipt { @Id diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java index 28f0f15db..d5cae58ad 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java @@ -28,9 +28,9 @@ */ @Data @Entity(value = "new", indices = { - @Index(value = "familyName", type = IndexType.NON_UNIQUE), - @Index(value = "fullName", type = IndexType.NON_UNIQUE), - @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), + @Index(fields = "familyName", type = IndexType.NON_UNIQUE), + @Index(fields = "fullName", type = IndexType.NON_UNIQUE), + @Index(fields = "literature.ratings", type = IndexType.NON_UNIQUE), }) public class NewClass { @Id diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java index 32a687c53..82044f93a 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java @@ -28,10 +28,10 @@ */ @Data @Entity(value = "old", indices = { - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "lastName", type = IndexType.NON_UNIQUE), - @Index(value = "literature.text", type = IndexType.FULL_TEXT), - @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "lastName", type = IndexType.NON_UNIQUE), + @Index(fields = "literature.text", type = IndexType.FULL_TEXT), + @Index(fields = "literature.ratings", type = IndexType.NON_UNIQUE), }) public class OldClass { @Id diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java index 5a60bb6ec..760b19067 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java @@ -117,9 +117,9 @@ public void testFindByEmbeddedField() { @ToString @EqualsAndHashCode @Indices({ - @Index(value = "joinDate", type = IndexType.NON_UNIQUE), - @Index(value = "address", type = IndexType.FULL_TEXT), - @Index(value = "employeeNote:text", type = IndexType.FULL_TEXT) + @Index(fields = "joinDate", type = IndexType.NON_UNIQUE), + @Index(fields = "address", type = IndexType.FULL_TEXT), + @Index(fields = "employeeNote:text", type = IndexType.FULL_TEXT) }) public static class EmployeeForCustomSeparator implements Serializable { @Id diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 5239a9228..e0a510606 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -331,8 +331,8 @@ public void testIssue217() { @Data @Entity(value = "entity.employee", indices = { - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "lastName", type = IndexType.NON_UNIQUE), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "lastName", type = IndexType.NON_UNIQUE), }) private static class EmployeeEntity { private static final Faker faker = new Faker(); diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index fac81c611..566dc82b0 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -181,7 +181,7 @@ public void testUniversalFullTextIndexing() { } @Indices( - @Index(value = "text", type = IndexType.FULL_TEXT) + @Index(fields = "text", type = IndexType.FULL_TEXT) ) public static class TextData { public int id; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index 9d21f72e8..e14af920b 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -31,9 +31,9 @@ */ @Data @Entity(value = "books", indices = { - @Index(value = "tags", type = IndexType.NON_UNIQUE), - @Index(value = "description", type = IndexType.FULL_TEXT), - @Index(value = { "price", "publisher" }) + @Index(fields = "tags", type = IndexType.NON_UNIQUE), + @Index(fields = "description", type = IndexType.FULL_TEXT), + @Index(fields = { "price", "publisher" }) }) public class Book { @JsonProperty("book_id") diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Company.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Company.java index 4a85b5156..3a6721f73 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Company.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Company.java @@ -39,7 +39,7 @@ @ToString @EqualsAndHashCode @Indices({ - @Index(value = "companyName") + @Index(fields = "companyName") }) public class Company implements Serializable { @Id(fieldName = "company_id") diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java index ed048f2e6..fa84714f9 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java @@ -33,9 +33,9 @@ */ @ToString @EqualsAndHashCode -@Index(value = "joinDate", type = IndexType.NON_UNIQUE) -@Index(value = "address", type = IndexType.FULL_TEXT) -@Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) +@Index(fields = "joinDate", type = IndexType.NON_UNIQUE) +@Index(fields = "address", type = IndexType.FULL_TEXT) +@Index(fields = "employeeNote.text", type = IndexType.FULL_TEXT) public class Employee implements Serializable { @Id @Getter diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java index 844e39223..f27a3f869 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java @@ -29,7 +29,7 @@ */ @Getter @Setter -@Index(value = "date") +@Index(fields = "date") public class ParentClass extends SuperDuperClass { @Id protected Long id; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java index cc871a260..cf6d420fa 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java @@ -31,8 +31,8 @@ */ @Data @Entity(value = "MyPerson", indices = { - @Index(value = "name", type = IndexType.FULL_TEXT), - @Index(value = "status", type = IndexType.NON_UNIQUE) + @Index(fields = "name", type = IndexType.FULL_TEXT), + @Index(fields = "status", type = IndexType.NON_UNIQUE) }) public class PersonEntity { @Id diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java index a56a63b56..5d5423a1b 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java @@ -25,9 +25,9 @@ * @author Anindya Chatterjee */ @Data -@Index(value = "firstName") -@Index(value = "age", type = IndexType.NON_UNIQUE) -@Index(value = "lastName", type = IndexType.FULL_TEXT) +@Index(fields = "firstName") +@Index(fields = "age", type = IndexType.NON_UNIQUE) +@Index(fields = "lastName", type = IndexType.FULL_TEXT) public class RepeatableIndexTest { private String firstName; private Integer age; diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java index d39ddf146..523cebb87 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java @@ -27,7 +27,7 @@ */ @Getter @Setter -@Index(value = "text", type = IndexType.FULL_TEXT) +@Index(fields = "text", type = IndexType.FULL_TEXT) public class SuperDuperClass { private String text; } diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java index c8cfe9234..a38e8fdba 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/NitriteTest.java @@ -625,7 +625,7 @@ public CompatChild fromDocument(Document document, NitriteMapper nitriteMapper) @NoArgsConstructor @AllArgsConstructor @Indices({ - @Index(value = "synced", type = IndexType.NON_UNIQUE) + @Index(fields = "synced", type = IndexType.NON_UNIQUE) }) public static class Receipt { @Id diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java index b8d1193ec..4565edd6c 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java @@ -379,7 +379,7 @@ public void initialize(NitriteConfig nitriteConfig) { } } - @Index(value = "longValue") + @Index(fields = "longValue") private static class TestObject { private String stringValue; private Long longValue; @@ -416,7 +416,7 @@ public TestObject fromDocument(Document document, NitriteMapper nitriteMapper) { } } - @Index(value = "longValue") + @Index(fields = "longValue") private static class TestObject2 { private String stringValue; private Long longValue; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java index 887419d44..f556c9c8c 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java @@ -347,9 +347,9 @@ public PerfTest fromDocument(Document document, NitriteMapper nitriteMapper) { } @Indices({ - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "age", type = IndexType.NON_UNIQUE), - @Index(value = "text", type = IndexType.FULL_TEXT), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "age", type = IndexType.NON_UNIQUE), + @Index(fields = "text", type = IndexType.FULL_TEXT), }) private static class PerfTestIndexed extends PerfTest { diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java index 1de57ca4f..177023c41 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/NewClass.java @@ -31,9 +31,9 @@ */ @Data @Entity(value = "new", indices = { - @Index(value = "familyName", type = IndexType.NON_UNIQUE), - @Index(value = "fullName", type = IndexType.NON_UNIQUE), - @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), + @Index(fields = "familyName", type = IndexType.NON_UNIQUE), + @Index(fields = "fullName", type = IndexType.NON_UNIQUE), + @Index(fields = "literature.ratings", type = IndexType.NON_UNIQUE), }) public class NewClass { @Id diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java index 8c1142508..cd7104dd3 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/migration/OldClass.java @@ -31,10 +31,10 @@ */ @Data @Entity(value = "old", indices = { - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "lastName", type = IndexType.NON_UNIQUE), - @Index(value = "literature.text", type = IndexType.FULL_TEXT), - @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "lastName", type = IndexType.NON_UNIQUE), + @Index(fields = "literature.text", type = IndexType.FULL_TEXT), + @Index(fields = "literature.ratings", type = IndexType.NON_UNIQUE), }) public class OldClass { @Id diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java index 54fc77286..42c80d4c2 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java @@ -124,9 +124,9 @@ public void testFindByEmbeddedField() { @ToString @EqualsAndHashCode @Indices({ - @Index(value = "joinDate", type = IndexType.NON_UNIQUE), - @Index(value = "address", type = IndexType.FULL_TEXT), - @Index(value = "employeeNote:text", type = IndexType.FULL_TEXT) + @Index(fields = "joinDate", type = IndexType.NON_UNIQUE), + @Index(fields = "address", type = IndexType.FULL_TEXT), + @Index(fields = "employeeNote:text", type = IndexType.FULL_TEXT) }) public static class EmployeeForCustomSeparator implements Serializable { @Id diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index ce149b146..d66ef2d9c 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -369,8 +369,8 @@ public void testIssue217() { @Data @Entity(value = "entity.employee", indices = { - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "lastName", type = IndexType.NON_UNIQUE), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "lastName", type = IndexType.NON_UNIQUE), }) private static class EmployeeEntity { private static final Faker faker = new Faker(); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index 124b8a441..59d6681ba 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -184,7 +184,7 @@ public void testUniversalFullTextIndexing() { } @Indices( - @Index(value = "text", type = IndexType.FULL_TEXT) + @Index(fields = "text", type = IndexType.FULL_TEXT) ) public static class TextData { public Integer id; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index cb3c972ca..c27993fc4 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -35,9 +35,9 @@ */ @Data @Entity(value = "books", indices = { - @Index(value = "tags", type = IndexType.NON_UNIQUE), - @Index(value = "description", type = IndexType.FULL_TEXT), - @Index(value = { "price", "publisher" }) + @Index(fields = "tags", type = IndexType.NON_UNIQUE), + @Index(fields = "description", type = IndexType.FULL_TEXT), + @Index(fields = { "price", "publisher" }) }) public class Book { @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java index 54c1acf94..649053261 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java @@ -37,7 +37,7 @@ */ @EqualsAndHashCode @Indices({ - @Index(value = "companyName") + @Index(fields = "companyName") }) public class Company implements Serializable { @Id(fieldName = "company_id") diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java index dbba83965..f89b6ecc9 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java @@ -36,9 +36,9 @@ */ @ToString @EqualsAndHashCode -@Index(value = "joinDate", type = IndexType.NON_UNIQUE) -@Index(value = "address", type = IndexType.FULL_TEXT) -@Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) +@Index(fields = "joinDate", type = IndexType.NON_UNIQUE) +@Index(fields = "address", type = IndexType.FULL_TEXT) +@Index(fields = "employeeNote.text", type = IndexType.FULL_TEXT) public class Employee implements Serializable { @Id @Getter diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java index 844e39223..f27a3f869 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java @@ -29,7 +29,7 @@ */ @Getter @Setter -@Index(value = "date") +@Index(fields = "date") public class ParentClass extends SuperDuperClass { @Id protected Long id; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java index b036118c7..b6dcd03a3 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java @@ -34,8 +34,8 @@ */ @Data @Entity(value = "MyPerson", indices = { - @Index(value = "name", type = IndexType.FULL_TEXT), - @Index(value = "status", type = IndexType.NON_UNIQUE) + @Index(fields = "name", type = IndexType.FULL_TEXT), + @Index(fields = "status", type = IndexType.NON_UNIQUE) }) public class PersonEntity { @Id diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java index 67f77060e..5d9208b86 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java @@ -28,9 +28,9 @@ * @author Anindya Chatterjee */ @Data -@Index(value = "firstName") -@Index(value = "age", type = IndexType.NON_UNIQUE) -@Index(value = "lastName", type = IndexType.FULL_TEXT) +@Index(fields = "firstName") +@Index(fields = "age", type = IndexType.NON_UNIQUE) +@Index(fields = "lastName", type = IndexType.FULL_TEXT) public class RepeatableIndexTest { private String firstName; private Integer age; diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java index d39ddf146..523cebb87 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java @@ -27,7 +27,7 @@ */ @Getter @Setter -@Index(value = "text", type = IndexType.FULL_TEXT) +@Index(fields = "text", type = IndexType.FULL_TEXT) public class SuperDuperClass { private String text; } diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java index 446bd7d07..a3fcf76dd 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/NitriteTest.java @@ -484,7 +484,7 @@ public CompatChild fromDocument(Document document, NitriteMapper nitriteMapper) @NoArgsConstructor @AllArgsConstructor @Indices({ - @Index(value = "synced", type = IndexType.NON_UNIQUE) + @Index(fields = "synced", type = IndexType.NON_UNIQUE) }) public static class Receipt { @Id diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java index f407e701f..eeb05035e 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteBuilderTest.java @@ -315,7 +315,7 @@ public void initialize(NitriteConfig nitriteConfig) { } } - @Index(value = "longValue") + @Index(fields = "longValue") private static class TestObject { private String stringValue; private Long longValue; @@ -352,7 +352,7 @@ public TestObject fromDocument(Document document, NitriteMapper nitriteMapper) { } } - @Index(value = "longValue") + @Index(fields = "longValue") private static class TestObject2 { private String stringValue; private Long longValue; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java index 5184d6f03..ff2d6dd9d 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/NitriteStressTest.java @@ -345,9 +345,9 @@ public PerfTest fromDocument(Document document, NitriteMapper nitriteMapper) { } @Indices({ - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "age", type = IndexType.NON_UNIQUE), - @Index(value = "text", type = IndexType.FULL_TEXT), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "age", type = IndexType.NON_UNIQUE), + @Index(fields = "text", type = IndexType.FULL_TEXT), }) private static class PerfTestIndexed extends PerfTest { public static class Converter implements EntityConverter { diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java index daa662f3e..49da00aa2 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/NewClass.java @@ -31,9 +31,9 @@ */ @Data @Entity(value = "new", indices = { - @Index(value = "familyName", type = IndexType.NON_UNIQUE), - @Index(value = "fullName", type = IndexType.NON_UNIQUE), - @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), + @Index(fields = "familyName", type = IndexType.NON_UNIQUE), + @Index(fields = "fullName", type = IndexType.NON_UNIQUE), + @Index(fields = "literature.ratings", type = IndexType.NON_UNIQUE), }) public class NewClass { @Id diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java index bc3a078d9..c4395b7ba 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/migrate/OldClass.java @@ -31,10 +31,10 @@ */ @Data @Entity(value = "old", indices = { - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "lastName", type = IndexType.NON_UNIQUE), - @Index(value = "literature.text", type = IndexType.FULL_TEXT), - @Index(value = "literature.ratings", type = IndexType.NON_UNIQUE), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "lastName", type = IndexType.NON_UNIQUE), + @Index(fields = "literature.text", type = IndexType.FULL_TEXT), + @Index(fields = "literature.ratings", type = IndexType.NON_UNIQUE), }) public class OldClass { @Id diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java index be25597bf..a096fdfb9 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java @@ -123,9 +123,9 @@ public void testFindByEmbeddedField() { @ToString @EqualsAndHashCode @Indices({ - @Index(value = "joinDate", type = IndexType.NON_UNIQUE), - @Index(value = "address", type = IndexType.FULL_TEXT), - @Index(value = "employeeNote:text", type = IndexType.FULL_TEXT) + @Index(fields = "joinDate", type = IndexType.NON_UNIQUE), + @Index(fields = "address", type = IndexType.FULL_TEXT), + @Index(fields = "employeeNote:text", type = IndexType.FULL_TEXT) }) public static class EmployeeForCustomSeparator implements Serializable { @Id diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 14d5c924f..360ee228d 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -369,8 +369,8 @@ public void testIssue217() { @Data @Entity(value = "entity.employee", indices = { - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "lastName", type = IndexType.NON_UNIQUE), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "lastName", type = IndexType.NON_UNIQUE), }) private static class EmployeeEntity { private static final Faker faker = new Faker(); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index 6d157356e..d509adc70 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -170,7 +170,7 @@ public void testUniversalFullTextIndexing() { } @Indices( - @Index(value = "text", type = IndexType.FULL_TEXT) + @Index(fields = "text", type = IndexType.FULL_TEXT) ) public static class TextData { public Integer id; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index cb3c972ca..c27993fc4 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -35,9 +35,9 @@ */ @Data @Entity(value = "books", indices = { - @Index(value = "tags", type = IndexType.NON_UNIQUE), - @Index(value = "description", type = IndexType.FULL_TEXT), - @Index(value = { "price", "publisher" }) + @Index(fields = "tags", type = IndexType.NON_UNIQUE), + @Index(fields = "description", type = IndexType.FULL_TEXT), + @Index(fields = { "price", "publisher" }) }) public class Book { @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java index 54c1acf94..649053261 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Company.java @@ -37,7 +37,7 @@ */ @EqualsAndHashCode @Indices({ - @Index(value = "companyName") + @Index(fields = "companyName") }) public class Company implements Serializable { @Id(fieldName = "company_id") diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java index dbba83965..f89b6ecc9 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java @@ -36,9 +36,9 @@ */ @ToString @EqualsAndHashCode -@Index(value = "joinDate", type = IndexType.NON_UNIQUE) -@Index(value = "address", type = IndexType.FULL_TEXT) -@Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) +@Index(fields = "joinDate", type = IndexType.NON_UNIQUE) +@Index(fields = "address", type = IndexType.FULL_TEXT) +@Index(fields = "employeeNote.text", type = IndexType.FULL_TEXT) public class Employee implements Serializable { @Id @Getter diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java index 844e39223..f27a3f869 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java @@ -29,7 +29,7 @@ */ @Getter @Setter -@Index(value = "date") +@Index(fields = "date") public class ParentClass extends SuperDuperClass { @Id protected Long id; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java index b036118c7..b6dcd03a3 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java @@ -34,8 +34,8 @@ */ @Data @Entity(value = "MyPerson", indices = { - @Index(value = "name", type = IndexType.FULL_TEXT), - @Index(value = "status", type = IndexType.NON_UNIQUE) + @Index(fields = "name", type = IndexType.FULL_TEXT), + @Index(fields = "status", type = IndexType.NON_UNIQUE) }) public class PersonEntity { @Id diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java index 67f77060e..5d9208b86 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java @@ -28,9 +28,9 @@ * @author Anindya Chatterjee */ @Data -@Index(value = "firstName") -@Index(value = "age", type = IndexType.NON_UNIQUE) -@Index(value = "lastName", type = IndexType.FULL_TEXT) +@Index(fields = "firstName") +@Index(fields = "age", type = IndexType.NON_UNIQUE) +@Index(fields = "lastName", type = IndexType.FULL_TEXT) public class RepeatableIndexTest { private String firstName; private Integer age; diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java index d39ddf146..523cebb87 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java @@ -27,7 +27,7 @@ */ @Getter @Setter -@Index(value = "text", type = IndexType.FULL_TEXT) +@Index(fields = "text", type = IndexType.FULL_TEXT) public class SuperDuperClass { private String text; } diff --git a/nitrite-spatial/src/test/java/org/dizitart/no2/spatial/SpatialData.java b/nitrite-spatial/src/test/java/org/dizitart/no2/spatial/SpatialData.java index 620fc2874..5ed1a120e 100644 --- a/nitrite-spatial/src/test/java/org/dizitart/no2/spatial/SpatialData.java +++ b/nitrite-spatial/src/test/java/org/dizitart/no2/spatial/SpatialData.java @@ -27,7 +27,7 @@ * @author Anindya Chatterjee */ @Data -@Index(value = "geometry", type = SPATIAL_INDEX) +@Index(fields = "geometry", type = SPATIAL_INDEX) public class SpatialData { @Id private Long id; diff --git a/nitrite-support/src/test/java/org/dizitart/no2/support/Company.java b/nitrite-support/src/test/java/org/dizitart/no2/support/Company.java index 2f855757b..d66f8254f 100644 --- a/nitrite-support/src/test/java/org/dizitart/no2/support/Company.java +++ b/nitrite-support/src/test/java/org/dizitart/no2/support/Company.java @@ -34,7 +34,7 @@ */ @Data @Indices({ - @Index(value = "companyName") + @Index(fields = "companyName") }) public class Company implements Serializable { @Id(fieldName = "company_id") diff --git a/nitrite-support/src/test/java/org/dizitart/no2/support/Employee.java b/nitrite-support/src/test/java/org/dizitart/no2/support/Employee.java index 9ad3af137..c9841a7ea 100644 --- a/nitrite-support/src/test/java/org/dizitart/no2/support/Employee.java +++ b/nitrite-support/src/test/java/org/dizitart/no2/support/Employee.java @@ -37,9 +37,9 @@ @ToString @EqualsAndHashCode @Indices({ - @Index(value = "joinDate", type = IndexType.NON_UNIQUE), - @Index(value = "address", type = IndexType.FULL_TEXT), - @Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) + @Index(fields = "joinDate", type = IndexType.NON_UNIQUE), + @Index(fields = "address", type = IndexType.FULL_TEXT), + @Index(fields = "employeeNote.text", type = IndexType.FULL_TEXT) }) public class Employee implements Serializable { @Id diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java b/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java index 29ee27be9..03f85f4ea 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/AnnotationScanner.java @@ -54,7 +54,7 @@ public AnnotationScanner(Class type, NitriteCollection collection, NitriteMap public void createIndices() { for (Index index : indices) { - String[] fields = index.value(); + String[] fields = index.fields(); if (!collection.hasIndex(fields)) { collection.createIndex(indexOptions(index.type()), fields); } @@ -154,7 +154,7 @@ private void scanIdAnnotation() { private void populateIndex(List indexList) { for (Index index : indexList) { - String[] names = index.value(); + String[] names = index.fields(); List entityFields = new ArrayList<>(); for (String name : names) { diff --git a/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Index.java b/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Index.java index 635e5fd33..5364fcda5 100644 --- a/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Index.java +++ b/nitrite/src/main/java/org/dizitart/no2/repository/annotations/Index.java @@ -35,7 +35,7 @@ * * @return the field name */ - String[] value(); + String[] fields(); /** * Type of the index. diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java index d692de838..c82b52df1 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ObjectUtilsTest.java @@ -296,7 +296,7 @@ private static class ValidEntity4 { @Data @Entity(indices = { - @Index(value = "value") + @Index(fields = "value") }) private static class ValidEntity5 { private String value; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java index 7f5f99c5f..a67921789 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/NitriteTest.java @@ -434,7 +434,7 @@ public CompatChild fromDocument(Document document, NitriteMapper nitriteMapper) @NoArgsConstructor @AllArgsConstructor @Indices({ - @Index(value = "synced", type = IndexType.NON_UNIQUE) + @Index(fields = "synced", type = IndexType.NON_UNIQUE) }) public static class Receipt { @Id diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/StressTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/StressTest.java index 1f9eabec3..437b293c9 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/StressTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/StressTest.java @@ -223,9 +223,9 @@ public PerfTest fromDocument(Document document, NitriteMapper nitriteMapper) { } @Indices({ - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "age", type = IndexType.NON_UNIQUE), - @Index(value = "text", type = IndexType.FULL_TEXT), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "age", type = IndexType.NON_UNIQUE), + @Index(fields = "text", type = IndexType.FULL_TEXT), }) private static class PerfTestIndexed extends PerfTest { public static class PerfTestIndexedConverter implements EntityConverter { diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java index ac38940b7..0f0e7507d 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java @@ -274,6 +274,12 @@ public void testFindSortOnNonExistingField() { insert(); DocumentCursor cursor = collection.find(orderBy("my-value", SortOrder.Descending)); assertEquals(cursor.size(), 3); + + List dateList = new ArrayList<>(); + for (Document document : cursor) { + dateList.add(document.get("birthDay", Date.class)); + } + assertFalse(isSorted(dateList, true)); } @Test @@ -281,6 +287,12 @@ public void testFindInvalidField() { insert(); DocumentCursor cursor = collection.find(where("myField").eq("myData")); assertEquals(cursor.size(), 0); + + cursor = collection.find(where("myField").notEq(null)); + assertEquals(cursor.size(), 0); + + cursor = collection.find(where("myField").eq(null)); + assertEquals(cursor.size(), 3); } @Test @@ -305,6 +317,9 @@ public void testGetById() { assertNull(document); document = collection.find().firstOrNull(); + id = document.getId(); + + document = collection.getById(id); assertEquals(document.get(DOC_ID), document.getId().getIdValue()); assertEquals(document.get("firstName"), "fn1"); @@ -359,6 +374,7 @@ public void testProject() { } assertEquals(iteration, 3); } + @Test public void testProjection() { Document doc1 = Document.createDocument("name", "John") diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java index 014e57f0b..1920e9c3c 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/CustomFieldSeparatorTest.java @@ -113,9 +113,9 @@ public void testFindByEmbeddedField() { @ToString @EqualsAndHashCode @Indices({ - @Index(value = "joinDate", type = IndexType.NON_UNIQUE), - @Index(value = "address", type = IndexType.FULL_TEXT), - @Index(value = "employeeNote:text", type = IndexType.FULL_TEXT) + @Index(fields = "joinDate", type = IndexType.NON_UNIQUE), + @Index(fields = "address", type = IndexType.FULL_TEXT), + @Index(fields = "employeeNote:text", type = IndexType.FULL_TEXT) }) public static class EmployeeForCustomSeparator implements Serializable { @Id diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index c9a20d3bb..1118056bc 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -494,8 +494,8 @@ public void testHasRepository() { @Data @Entity(value = "entity.employee", indices = { - @Index(value = "firstName", type = IndexType.NON_UNIQUE), - @Index(value = "lastName", type = IndexType.NON_UNIQUE), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "lastName", type = IndexType.NON_UNIQUE), }) private static class EmployeeEntity { private static final Faker faker = new Faker(); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java index a0ac5a6bb..c5524dfee 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/UniversalTextTokenizerTest.java @@ -158,7 +158,7 @@ public void testUniversalFullTextIndexing() { } @Indices( - @Index(value = "text", type = IndexType.FULL_TEXT) + @Index(fields = "text", type = IndexType.FULL_TEXT) ) public static class TextData { public Integer id; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java index cb3c972ca..c27993fc4 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Book.java @@ -35,9 +35,9 @@ */ @Data @Entity(value = "books", indices = { - @Index(value = "tags", type = IndexType.NON_UNIQUE), - @Index(value = "description", type = IndexType.FULL_TEXT), - @Index(value = { "price", "publisher" }) + @Index(fields = "tags", type = IndexType.NON_UNIQUE), + @Index(fields = "description", type = IndexType.FULL_TEXT), + @Index(fields = { "price", "publisher" }) }) public class Book { @Id(fieldName = "book_id", embeddedFields = { "isbn", "book_name" }) diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Company.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Company.java index 54c1acf94..649053261 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Company.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Company.java @@ -37,7 +37,7 @@ */ @EqualsAndHashCode @Indices({ - @Index(value = "companyName") + @Index(fields = "companyName") }) public class Company implements Serializable { @Id(fieldName = "company_id") diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java index dbba83965..f89b6ecc9 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/Employee.java @@ -36,9 +36,9 @@ */ @ToString @EqualsAndHashCode -@Index(value = "joinDate", type = IndexType.NON_UNIQUE) -@Index(value = "address", type = IndexType.FULL_TEXT) -@Index(value = "employeeNote.text", type = IndexType.FULL_TEXT) +@Index(fields = "joinDate", type = IndexType.NON_UNIQUE) +@Index(fields = "address", type = IndexType.FULL_TEXT) +@Index(fields = "employeeNote.text", type = IndexType.FULL_TEXT) public class Employee implements Serializable { @Id @Getter diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java index 844e39223..f27a3f869 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/ParentClass.java @@ -29,7 +29,7 @@ */ @Getter @Setter -@Index(value = "date") +@Index(fields = "date") public class ParentClass extends SuperDuperClass { @Id protected Long id; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java index b036118c7..b6dcd03a3 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/PersonEntity.java @@ -34,8 +34,8 @@ */ @Data @Entity(value = "MyPerson", indices = { - @Index(value = "name", type = IndexType.FULL_TEXT), - @Index(value = "status", type = IndexType.NON_UNIQUE) + @Index(fields = "name", type = IndexType.FULL_TEXT), + @Index(fields = "status", type = IndexType.NON_UNIQUE) }) public class PersonEntity { @Id diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java index 67f77060e..5d9208b86 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/RepeatableIndexTest.java @@ -28,9 +28,9 @@ * @author Anindya Chatterjee */ @Data -@Index(value = "firstName") -@Index(value = "age", type = IndexType.NON_UNIQUE) -@Index(value = "lastName", type = IndexType.FULL_TEXT) +@Index(fields = "firstName") +@Index(fields = "age", type = IndexType.NON_UNIQUE) +@Index(fields = "lastName", type = IndexType.FULL_TEXT) public class RepeatableIndexTest { private String firstName; private Integer age; diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java index d39ddf146..523cebb87 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/data/SuperDuperClass.java @@ -27,7 +27,7 @@ */ @Getter @Setter -@Index(value = "text", type = IndexType.FULL_TEXT) +@Index(fields = "text", type = IndexType.FULL_TEXT) public class SuperDuperClass { private String text; } diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt index 4a07b7cfc..58c8888dd 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/BackportJavaTimeTest.kt @@ -38,7 +38,7 @@ import java.util.* class BackportJavaTimeTest { private val dbPath = getRandomTempDbFile() - @Index(value = ["time"], type = IndexType.NON_UNIQUE) + @Index(fields = ["time"], type = IndexType.NON_UNIQUE) data class TestData( @Id val id: String = UUID.randomUUID().toString(), val time: LocalDateTime = LocalDateTime.now() diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/NitriteTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/NitriteTest.kt index 18026fc7a..fc56686f5 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/NitriteTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/NitriteTest.kt @@ -17,12 +17,10 @@ package org.dizitart.kno2 import org.dizitart.kno2.filters.text -import org.dizitart.no2.collection.FindOptions import org.dizitart.no2.collection.FindOptions.orderBy import org.dizitart.no2.collection.FindOptions.skipBy import org.dizitart.no2.common.SortOrder import org.dizitart.no2.exceptions.UniqueConstraintException -import org.dizitart.no2.index.IndexOptions import org.dizitart.no2.index.IndexOptions.indexOptions import org.dizitart.no2.index.IndexType import org.dizitart.no2.mvstore.MVStoreModule @@ -191,7 +189,7 @@ interface MyInterface { val id: UUID } -@Indices(value = [(Index(value = ["name"], type = IndexType.NON_UNIQUE))]) +@Indices(value = [(Index(fields = ["name"], type = IndexType.NON_UNIQUE))]) abstract class SomeAbsClass( @Id override val id: UUID = UUID.randomUUID(), open val name: String = "abcd" @@ -218,7 +216,7 @@ data class CaObject( val name: String = "" ) -@Indices(value = [(Index(value = ["time"], type = IndexType.UNIQUE))]) +@Indices(value = [(Index(fields = ["time"], type = IndexType.UNIQUE))]) data class ClassWithLocalDateTime( val name: String = "", val time: LocalDateTime = LocalDateTime.now() diff --git a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/ObjectFilterTest.kt b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/ObjectFilterTest.kt index 0de1fde18..fabb10706 100644 --- a/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/ObjectFilterTest.kt +++ b/potassium-nitrite/src/test/kotlin/org/dizitart/kno2/ObjectFilterTest.kt @@ -321,7 +321,7 @@ class ObjectFilterTest : BaseTest() { } } -@Indices(Index(value = ["text"], type = IndexType.FULL_TEXT)) +@Indices(Index(fields = ["text"], type = IndexType.FULL_TEXT)) data class TestData(@Id val id: Int = 0, val text: String = "", val list: List = listOf()) class ListData(val name: String = "", val score: Int = 0) @@ -331,7 +331,7 @@ data class SimpleObject( val value: Boolean = false ) -@Index(value = ["geometry"], type = SpatialIndexer.SPATIAL_INDEX) +@Index(fields = ["geometry"], type = SpatialIndexer.SPATIAL_INDEX) data class SpatialData( @Id val id: Long = 0, val geometry: Geometry? = null From 5ca088ebcff28737326d5fa4181652d64aa50389 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Thu, 10 Nov 2022 11:38:39 +0530 Subject: [PATCH 72/78] Update CollectionFindTest.java --- .../dizitart/no2/integration/collection/CollectionFindTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java index 0f0e7507d..96c55ee3f 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionFindTest.java @@ -782,7 +782,7 @@ public void testIdSet() { assertEquals(cursor.size(), 1); Document byId = cursor.iterator().next(); - assertEquals(byId.get("lastName"), "ln1"); + assertEquals(collection.getById(byId.getId()).get("lastName"), "ln1"); } @Test From c5c140f181b442fcb6b19ad43bc485eaf4708c6b Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Tue, 15 Nov 2022 11:46:05 +0530 Subject: [PATCH 73/78] few fixes --- .../no2/collection/CollectionFactory.java | 4 +++- .../dizitart/no2/common/util/Comparables.java | 4 +++- .../org/dizitart/no2/common/util/Numbers.java | 21 ------------------- .../CollectionCompoundIndexTest.java | 6 +++--- .../collection/CollectionDeleteTest.java | 2 +- 5 files changed, 10 insertions(+), 27 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java index d9fa5bf47..ea2973230 100644 --- a/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java +++ b/nitrite/src/main/java/org/dizitart/no2/collection/CollectionFactory.java @@ -117,7 +117,9 @@ public void clear() { try { lock.lock(); for (NitriteCollection collection : collectionMap.values()) { - collection.close(); + if (collection.isOpen()) { + collection.close(); + } } collectionMap.clear(); } catch (Exception e) { diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/Comparables.java b/nitrite/src/main/java/org/dizitart/no2/common/util/Comparables.java index 0c13bcd20..d96c1ca87 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/Comparables.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/Comparables.java @@ -23,7 +23,9 @@ public static int compare(Comparable first, Comparable second) { Number number2 = (Number) second; int result = Numbers.compare(number1, number2); if (!first.getClass().equals(second.getClass())) { - if (result == 0) return 1; + if (result == 0) { + return first.toString().compareTo(second.toString()); + } } return result; } diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/Numbers.java b/nitrite/src/main/java/org/dizitart/no2/common/util/Numbers.java index 1d242a212..fb5cee5ad 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/Numbers.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/Numbers.java @@ -48,27 +48,6 @@ public static int compare(Number x, Number y) { } } - public static Object castNumber(Object value, Class type) { - if (value instanceof Number && Number.class.isAssignableFrom(type)) { - Number number = (Number) value; - if (type.equals(Short.class) || type.equals(short.class)) { - return number.shortValue(); - } else if (type.equals(Byte.class) || type.equals(byte.class)) { - return number.byteValue(); - } else if (type.equals(Double.class) || type.equals(double.class)) { - return number.doubleValue(); - } else if (type.equals(Float.class) || type.equals(float.class)) { - return number.floatValue(); - } else if (type.equals(Integer.class) || type.equals(int.class)) { - return number.intValue(); - } else if (type.equals(Long.class) || type.equals(long.class)) { - return number.longValue(); - } - } - throw new ValidationException("Cannot cast number of type " + value.getClass().getName() - + " to " + type.getName()); - } - private static boolean isSpecial(Number number) { boolean specialDouble = number instanceof Double && (Double.isNaN((Double) number) || Double.isInfinite((Double) number)); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionCompoundIndexTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionCompoundIndexTest.java index e0ce0646a..e2a4e4279 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionCompoundIndexTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionCompoundIndexTest.java @@ -177,9 +177,9 @@ public void testNullValuesInIndexedFields() { insert(); collection.insert(document); - DocumentCursor cursor = collection.find(where("fistName").eq(null)); + DocumentCursor cursor = collection.find(where("firstName").eq(null)); assertEquals("ln1", cursor.firstOrNull().get("lastName", String.class)); - assertNull(cursor.firstOrNull().get("fistName", String.class)); + assertNull(cursor.firstOrNull().get("firstName", String.class)); document = createDocument("firstName", "fn4") .put("lastName", null) @@ -195,7 +195,7 @@ public void testNullValuesInIndexedFields() { cursor = collection.find(where("lastName").eq(null)); assertEquals("fn4", cursor.firstOrNull().get("firstName", String.class)); - assertNull(cursor.firstOrNull().get("fistName", String.class)); + assertNull(cursor.firstOrNull().get("lastName", String.class)); cursor = collection.find(and(where("lastName").eq(null), where("birthDay").eq(null))); assertNull(cursor.firstOrNull().get("lastName", String.class)); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionDeleteTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionDeleteTest.java index ca1660599..541621ced 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionDeleteTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionDeleteTest.java @@ -90,7 +90,7 @@ public void testClear() { assertTrue(uniqueError); } - collection.remove(Filter.ALL); + collection.clear(); cursor = collection.find(); assertEquals(cursor.size(), 0); From efd8205b629bab1e1de2377612b5934100585956 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 28 Dec 2022 11:42:11 +0530 Subject: [PATCH 74/78] test fixed --- .../collection/CollectionTest.java | 7 +-- .../repository/ObjectRepositoryTest.java | 52 ++++++++++++++----- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java index 4da9d6ae6..abdfc0d17 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/collection/CollectionTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** * @author Anindya Chatterjee @@ -38,7 +39,7 @@ public void testGetName() { public void testDropCollection() { // check if collection exists // the collection is noty opened yet - db.hasCollection("test"); + assertTrue(db.hasCollection("test")); // destroy the collection collection.drop(); @@ -50,9 +51,9 @@ public void testDropCollection() { } @Test - public void testCloseConnection() { + public void testCloseCollection() { + assertTrue(collection.isOpen()); collection.close(); - assertFalse(collection.isOpen()); } diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 1118056bc..61febbf48 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -26,6 +26,7 @@ import org.dizitart.no2.common.mapper.NitriteMapper; import org.dizitart.no2.common.mapper.SimpleDocumentMapper; import org.dizitart.no2.common.meta.Attributes; +import org.dizitart.no2.exceptions.UniqueConstraintException; import org.dizitart.no2.exceptions.ValidationException; import org.dizitart.no2.index.IndexType; import org.dizitart.no2.integration.Retry; @@ -82,9 +83,9 @@ public void setUp() { mapper.registerEntityConverter(new MiniProduct.Converter()); db = Nitrite.builder() - .loadModule(module(mapper)) - .fieldSeparator(".") - .openOrCreate(); + .loadModule(module(mapper)) + .fieldSeparator(".") + .openOrCreate(); } @After @@ -175,8 +176,7 @@ public void testWriteThousandRecords() { repository.insert(record); } - Cursor cursor - = repository.find(where("failed").eq(false)); + Cursor cursor = repository.find(where("failed").eq(false)); for (StressRecord record : cursor) { record.setProcessed(true); repository.update(where("firstName").eq(record.getFirstName()), record); @@ -198,8 +198,7 @@ public void testWithPackagePrivateClass() { @Test public void testWithPrivateConstructor() { - ObjectRepository repository = - db.getRepository(WithPrivateConstructor.class); + ObjectRepository repository = db.getRepository(WithPrivateConstructor.class); WithPrivateConstructor object = WithPrivateConstructor.create("test", 2L); repository.insert(object); @@ -223,9 +222,9 @@ public void testWithDateAsId() { repository.insert(object2); assertEquals(repository.find(where("id").eq(new Date(1482773634L))) - .firstOrNull(), object1); + .firstOrNull(), object1); assertEquals(repository.find(where("id").eq(new Date(1482773720L))) - .firstOrNull(), object2); + .firstOrNull(), object2); } @Test @@ -363,7 +362,7 @@ public void testRepositoryName() { assertEquals(productRepository.getDocumentCollection().getName(), "product"); assertEquals(upcomingProductRepository.getDocumentCollection().getName(), - "product+upcoming"); + "product+upcoming"); assertEquals(manufacturerRepository.getDocumentCollection().getName(), Manufacturer.class.getName()); assertEquals(exManufacturerRepository.getDocumentCollection().getName(), Manufacturer.class.getName() + "+ex"); assertEquals(employeeRepository.getDocumentCollection().getName(), Employee.class.getName()); @@ -492,10 +491,35 @@ public void testHasRepository() { assertFalse(db.hasRepository(new ProductDecorator(), "ex")); } + @Test + public void testIssue767() { + ObjectRepository companyRepository = db.getRepository(Company.class); + Company company1 = new Company(); + company1.setCompanyId(1L); + company1.setCompanyName("ABCD"); + company1.setDateCreated(new Date()); + companyRepository.insert(company1); + + Company company2 = new Company(); + company2.setCompanyId(2L); + company2.setCompanyName("ABCD"); + + boolean uniqueConstraintError = false; + try { + companyRepository.insert(company2); + } catch (UniqueConstraintException e) { + uniqueConstraintError = true; + } finally { + assertTrue(uniqueConstraintError); + } + + assertEquals(companyRepository.find().size(), 1); + } + @Data @Entity(value = "entity.employee", indices = { - @Index(fields = "firstName", type = IndexType.NON_UNIQUE), - @Index(fields = "lastName", type = IndexType.NON_UNIQUE), + @Index(fields = "firstName", type = IndexType.NON_UNIQUE), + @Index(fields = "lastName", type = IndexType.NON_UNIQUE), }) private static class EmployeeEntity { private static final Faker faker = new Faker(); @@ -521,8 +545,8 @@ public Class getEntityType() { @Override public Document toDocument(EmployeeEntity entity, NitriteMapper nitriteMapper) { return Document.createDocument("id", entity.id) - .put("firstName", entity.firstName) - .put("lastName", entity.lastName); + .put("firstName", entity.firstName) + .put("lastName", entity.lastName); } @Override From ea3cb357a0c4b067826d482dc856271e58d1a4ab Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 28 Dec 2022 13:40:46 +0530 Subject: [PATCH 75/78] test fix --- .../main/java/org/dizitart/no2/common/util/Comparables.java | 2 +- .../src/main/java/org/dizitart/no2/filters/EqualsFilter.java | 4 ++++ .../java/org/dizitart/no2/common/util/ComparablesTest.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/nitrite/src/main/java/org/dizitart/no2/common/util/Comparables.java b/nitrite/src/main/java/org/dizitart/no2/common/util/Comparables.java index d96c1ca87..3cf45de25 100644 --- a/nitrite/src/main/java/org/dizitart/no2/common/util/Comparables.java +++ b/nitrite/src/main/java/org/dizitart/no2/common/util/Comparables.java @@ -24,7 +24,7 @@ public static int compare(Comparable first, Comparable second) { int result = Numbers.compare(number1, number2); if (!first.getClass().equals(second.getClass())) { if (result == 0) { - return first.toString().compareTo(second.toString()); + return first.toString().compareTo(second.toString()) < 0 ? -1 : 1; } } return result; diff --git a/nitrite/src/main/java/org/dizitart/no2/filters/EqualsFilter.java b/nitrite/src/main/java/org/dizitart/no2/filters/EqualsFilter.java index c290f4896..20ad2a85a 100644 --- a/nitrite/src/main/java/org/dizitart/no2/filters/EqualsFilter.java +++ b/nitrite/src/main/java/org/dizitart/no2/filters/EqualsFilter.java @@ -44,6 +44,10 @@ public boolean apply(Pair element) { @Override public List applyOnIndex(IndexMap indexMap) { Object value = indexMap.get((Comparable) getValue()); + if (value == null) { + return new ArrayList<>(); + } + if (value instanceof List) { return ((List) value); } diff --git a/nitrite/src/test/java/org/dizitart/no2/common/util/ComparablesTest.java b/nitrite/src/test/java/org/dizitart/no2/common/util/ComparablesTest.java index 7f853ad39..77ce794c4 100644 --- a/nitrite/src/test/java/org/dizitart/no2/common/util/ComparablesTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/common/util/ComparablesTest.java @@ -29,7 +29,7 @@ public void testCompare3() { @Test public void testCompare4() { MutableByte first = new MutableByte(); - assertEquals(1, Comparables.compare(first, new MutableDouble())); + assertEquals(-1, Comparables.compare(first, new MutableDouble())); } } From 26afed87f20c2a12353a90dcb5569f8b63d51d95 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Wed, 28 Dec 2022 14:33:07 +0530 Subject: [PATCH 76/78] Update EntityDecoratorScannerTest.java --- .../org/dizitart/no2/repository/EntityDecoratorScannerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java b/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java index 04ef99fbb..5d6b6ccc0 100644 --- a/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/repository/EntityDecoratorScannerTest.java @@ -43,7 +43,7 @@ public void setUp() { SimpleDocumentMapper nitriteMapper = new SimpleDocumentMapper(); nitriteMapper.registerEntityConverter(new ClassA.ClassAConverter()); nitriteMapper.registerEntityConverter(new ClassBConverter()); - collection = Nitrite.builder().openOrCreate().getCollection("test"); + collection = Nitrite.builder().fieldSeparator(".").openOrCreate().getCollection("test"); reader = new EntityDecoratorScanner(new EntityADecorator(), collection, nitriteMapper); } From 7924c430bed42badd412d4f5a92979be817ca57c Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 6 Jan 2023 11:11:45 +0530 Subject: [PATCH 77/78] test fix --- .../no2/integration/repository/NitriteIdAsIdTest.java | 4 ++-- .../no2/integration/repository/NitriteIdAsIdTest.java | 4 ++-- .../no2/integration/repository/NitriteIdAsIdTest.java | 4 ++-- .../no2/integration/repository/NitriteIdAsIdTest.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java index a7a42681c..4296bcdcf 100644 --- a/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java +++ b/nitrite-jackson-mapper/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java @@ -94,7 +94,7 @@ public void testNitriteIdField() { } @Test(expected = InvalidIdException.class) - public void setIdDuringInsert() { + public void testSetIdDuringInsert() { WithNitriteId item1 = new WithNitriteId(); item1.name = "first"; item1.idField = NitriteId.newId(); @@ -103,7 +103,7 @@ public void setIdDuringInsert() { } @Test - public void changeIdDuringUpdate() { + public void testChangeIdDuringUpdate() { WithNitriteId item2 = new WithNitriteId(); item2.name = "second"; WriteResult result = repo.insert(item2); diff --git a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java index 278977722..4b776ea17 100644 --- a/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java +++ b/nitrite-mvstore-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java @@ -94,7 +94,7 @@ public void testNitriteIdField() { } @Test(expected = InvalidIdException.class) - public void setIdDuringInsert() { + public void testSetIdDuringInsert() { WithNitriteId item1 = new WithNitriteId(); item1.name = "first"; item1.idField = NitriteId.newId(); @@ -103,7 +103,7 @@ public void setIdDuringInsert() { } @Test - public void changeIdDuringUpdate() { + public void testChangeIdDuringUpdate() { WithNitriteId item2 = new WithNitriteId(); item2.name = "second"; WriteResult result = repo.insert(item2); diff --git a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java index 278977722..4b776ea17 100644 --- a/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java +++ b/nitrite-rocksdb-adapter/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java @@ -94,7 +94,7 @@ public void testNitriteIdField() { } @Test(expected = InvalidIdException.class) - public void setIdDuringInsert() { + public void testSetIdDuringInsert() { WithNitriteId item1 = new WithNitriteId(); item1.name = "first"; item1.idField = NitriteId.newId(); @@ -103,7 +103,7 @@ public void setIdDuringInsert() { } @Test - public void changeIdDuringUpdate() { + public void testChangeIdDuringUpdate() { WithNitriteId item2 = new WithNitriteId(); item2.name = "second"; WriteResult result = repo.insert(item2); diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java index c92f7b6a3..53273f3dc 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/NitriteIdAsIdTest.java @@ -88,7 +88,7 @@ public void testNitriteIdField() { } @Test(expected = InvalidIdException.class) - public void setIdDuringInsert() { + public void testSetIdDuringInsert() { WithNitriteId item1 = new WithNitriteId(); item1.name = "first"; item1.idField = NitriteId.newId(); @@ -97,7 +97,7 @@ public void setIdDuringInsert() { } @Test - public void changeIdDuringUpdate() { + public void testChangeIdDuringUpdate() { WithNitriteId item2 = new WithNitriteId(); item2.name = "second"; WriteResult result = repo.insert(item2); From 072909efb2df6771722d8eb5878c73ed6e314687 Mon Sep 17 00:00:00 2001 From: Anindya Chatterjee Date: Fri, 13 Jan 2023 10:56:51 +0530 Subject: [PATCH 78/78] Update ObjectRepositoryTest.java --- .../integration/repository/ObjectRepositoryTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java index 61febbf48..98759627e 100644 --- a/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java +++ b/nitrite/src/test/java/org/dizitart/no2/integration/repository/ObjectRepositoryTest.java @@ -19,6 +19,8 @@ import com.github.javafaker.Faker; import lombok.Data; + +import org.apache.commons.lang3.time.StopWatch; import org.dizitart.no2.Nitrite; import org.dizitart.no2.collection.Document; import org.dizitart.no2.collection.NitriteCollection; @@ -164,6 +166,8 @@ public void testWithTransientField() { public void testWriteThousandRecords() { int count = 5000; + StopWatch sw = new StopWatch(); + sw.start(); ObjectRepository repository = db.getRepository(StressRecord.class); for (int i = 0; i < count; i++) { @@ -181,6 +185,8 @@ public void testWriteThousandRecords() { record.setProcessed(true); repository.update(where("firstName").eq(record.getFirstName()), record); } + sw.stop(); + System.out.println("Sequential Time (s) - " + sw.getTime()); } @Test @@ -242,7 +248,7 @@ public void testWithIdInheritance() { repository.insert(childClass); childClass = new ChildClass(); - childClass.setName("seconds"); + childClass.setName("second"); childClass.setDate(new Date(100001L)); childClass.setId(2L); childClass.setText("I am second class"); @@ -333,7 +339,7 @@ public void testEntityRepository() { assertTrue(managerRepo.hasIndex("firstName")); assertTrue(managerRepo.hasIndex("lastName")); - assertTrue(employeeRepo.hasIndex("lastName")); + assertTrue(employeeRepo.hasIndex("firstName")); assertTrue(employeeRepo.hasIndex("lastName")); managerRepo.drop();