itemListener : itemListeners) {
+ result.add(itemListener);
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/main/java/com/codeforces/inmemo/Inmemo.java b/src/main/java/com/codeforces/inmemo/Inmemo.java
index 39ec00d..7daa37a 100644
--- a/src/main/java/com/codeforces/inmemo/Inmemo.java
+++ b/src/main/java/com/codeforces/inmemo/Inmemo.java
@@ -1,608 +1,608 @@
-package com.codeforces.inmemo;
-
-import net.sf.cglib.beans.BeanCopier;
-import org.apache.log4j.Logger;
-import org.jacuzzi.core.Row;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.sql.DataSource;
-import java.io.IOException;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- *
- * Inmemo is a framework to store entities in-memory and update their state in memory automatically on each update
- * in database. Each entity should have `indicatorField` which updates to greater value on each change. The best choice
- * is `updateTime` field of MySQL type `TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`. It
- * supports indices (based on hashmaps) and queries to find list of entities or counts. Refer to examples for
- * clarifications.
- *
- *
- * The implementation is friendly for class-reloading magic like in Nocturne
- * framework.
- *
- */
-@SuppressWarnings({"WeakerAccess", "unused"})
-public final class Inmemo {
- private static final Logger logger = Logger.getLogger(Inmemo.class);
- private static final Map> tables = new ConcurrentHashMap<>();
- private static final Lock tablesLock = new ReentrantLock();
- private static final ConcurrentMap beanCopiers = new ConcurrentHashMap<>();
- private static volatile boolean debug;
- private static final Set> noSizeSupportClasses = new HashSet<>();
- private static final Set noJournalSupportTableClassNames
- = Collections.newSetFromMap(new ConcurrentHashMap<>());
-
- private Inmemo() {
- // No operations.
- }
-
- static boolean isDebug() {
- return debug;
- }
-
- public static void setDebug(boolean debug) {
- Inmemo.debug = debug;
- }
-
- private static BeanCopier getBeanCopier(Class> sourceClass, Class> targetClass) {
- ClassPair classPair = new ClassPair(sourceClass, targetClass);
- BeanCopier beanCopier = beanCopiers.get(classPair);
- if (beanCopier == null) {
- BeanCopier copier = BeanCopier.create(sourceClass, targetClass, false);
- logger.info("Created BeanCopier(" + sourceClass + ',' + targetClass + ").");
- beanCopiers.putIfAbsent(classPair, copier);
- return beanCopiers.get(classPair);
- } else {
- return beanCopier;
- }
- }
-
- @SuppressWarnings("UnusedDeclaration")
- public static Matcher acceptAnyMatcher() {
- return tableItem -> true;
- }
-
- @SuppressWarnings("UnusedDeclaration")
- public static boolean isPreloaded() {
- tablesLock.lock();
- try {
- for (Table extends HasId> table : tables.values()) {
- if (!table.isPreloaded()) {
- return false;
- }
- }
- } finally {
- tablesLock.unlock();
- }
-
- return true;
- }
-
- /**
- * Table maintains ids set to support size() operation. Use this function
- * to refuse support of size() to reduce memory footprint.
- *
- * @param clazz Table item class.
- */
- public static void unsetSizeSupport(@Nonnull Class> clazz) {
- noSizeSupportClasses.add(clazz);
- }
-
- /**
- * Disables journal support for a table class. The first call for a class
- * must be made before createTable; repeated calls are always no-op.
- *
- * @param clazz Table item class.
- */
- public static void unsetJournalSupport(@Nonnull Class> clazz) {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
- tablesLock.lock();
- try {
- if (noJournalSupportTableClassNames.contains(tableClassName)) {
- return;
- }
- if (tables.containsKey(tableClassName)) {
- throw new IllegalStateException("Inmemo.unsetJournalSupport(clazz) must be called"
- + " before Inmemo.createTable [clazz=" + tableClassName + "].");
- }
- noJournalSupportTableClassNames.add(tableClassName);
- } finally {
- tablesLock.unlock();
- }
- }
-
- static Set> getNoSizeSupportClasses() {
- return noSizeSupportClasses;
- }
-
- static boolean isJournalSupportUnset(@Nonnull Class> clazz) {
- return noJournalSupportTableClassNames.contains(ReflectionUtil.getTableClassName(clazz));
- }
-
- static void putTableForUnsetJournalSupportTestOnly(Class clazz) {
- tablesLock.lock();
- try {
- tables.put(ReflectionUtil.getTableClassName(clazz), new Table<>(clazz, "id", null));
- } finally {
- tablesLock.unlock();
- }
- }
-
- static void removeTableForUnsetJournalSupportTestOnly(Class clazz) {
- tablesLock.lock();
- try {
- tables.remove(ReflectionUtil.getTableClassName(clazz));
- } finally {
- tablesLock.unlock();
- }
- }
-
- /**
- * Creates new table, if there is already table for compatible class then doing nothing.
- *
- * @param clazz Table item class.
- * @param indicatorField Item field which will be monitored to increase on each change. Good idea to make it
- * 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'.
- * @param initialIndicatorValue Initial value of indicator, will be loaded only items with at least
- * {@code initialIndicatorValue}. Use {@code null} to load all the items.
- * @param indices Indices built with Indices.Builder.
- * @param Item class.
- */
- @SuppressWarnings("UnusedDeclaration")
- public static void createTable(
- @Nonnull Class clazz,
- @Nonnull String indicatorField,
- @Nullable Object initialIndicatorValue,
- @Nonnull Indices indices,
- boolean waitForPreload) {
- createTable(clazz, indicatorField, initialIndicatorValue, indices, null, waitForPreload);
- }
-
- /**
- * Creates new table, if there is already table for compatible class then doing nothing.
- *
- * @param clazz Table item class.
- * @param indicatorField Item field which will be monitored to increase on each change. Good idea to make it
- * 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'.
- * @param initialIndicatorValue Initial value of indicator, will be loaded only items with at least
- * {@code initialIndicatorValue}. Use {@code null} to load all the items.
- * @param indices Indices built with Indices.Builder.
- * @param rowFilter Filter predicate for rows to be processed from DB
- * @param Item class.
- */
- @SuppressWarnings("UnusedDeclaration")
- public static void createTable(
- @Nonnull Class clazz,
- @Nonnull String indicatorField,
- @Nullable Object initialIndicatorValue,
- @Nonnull Indices indices,
- @Nullable Filter rowFilter,
- boolean waitForPreload) {
- tablesLock.lock();
-
- try {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
-
- Table extends HasId> table = tables.get(tableClassName);
- if (table == null) {
- renewTable(clazz, indicatorField, initialIndicatorValue, indices, rowFilter);
-
- table = tables.get(tableClassName);
- if (waitForPreload) {
- while (!table.isPreloaded()) {
- try {
- Thread.sleep(100L);
- } catch (InterruptedException ignored) {
- break;
- }
- }
- logger.info("Inmemo completed to wait for preload " + tableClassName + '.');
- }
- } else {
- // Exactly the same class?
- if (table.getClazz().equals(clazz)) {
- // logger.info("Exactly the same class [class=" + clazz + "].");
- return;
- }
-
- String clazzSpec = ReflectionUtil.getTableClassSpec(clazz);
- // Compatible classes?
- if (table.getClazzSpec().equals(clazzSpec)) {
- logger.info("Compatible classes " + tableClassName + " [class=" + clazz + "].");
- return;
- }
-
- renewTable(clazz, indicatorField, initialIndicatorValue, indices, rowFilter);
-
- table = tables.get(tableClassName);
- if (waitForPreload) {
- while (!table.isPreloaded()) {
- try {
- Thread.sleep(100L);
- } catch (InterruptedException ignored) {
- break;
- }
- }
- logger.info("Inmemo completed to wait for preload " + tableClassName + '.');
- }
- }
- } finally {
- tablesLock.unlock();
- }
- }
-
- /**
- * Drops table or doing nothing.
- *
- * @param clazz Table class.
- * @param Entity class.
- */
- public static void dropTableIfExists(@Nonnull Class clazz) {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
- Table extends HasId> table = tables.get(tableClassName);
-
- if (table != null) {
- tablesLock.lock();
- try {
- tables.remove(tableClassName);
- } finally {
- tablesLock.unlock();
- }
- }
- }
-
- public static int size(@Nonnull Class clazz) {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
- Table extends HasId> table = tables.get(tableClassName);
-
- if (table != null) {
- return table.size();
- } else {
- throw new InmemoException("Unable to find table for class name `" + tableClassName + "`.");
- }
- }
-
- @SuppressWarnings("UnusedDeclaration")
- public static void waitForPreload(@Nonnull Class clazz) {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
- Table extends HasId> table = tables.get(tableClassName);
-
- if (table != null) {
- boolean preloadedNow = false;
- while (!table.isPreloaded()) {
- preloadedNow = true;
- try {
- Thread.sleep(100L);
- } catch (InterruptedException ignored) {
- break;
- }
- }
- if (preloadedNow) {
- logger.info("Inmemo completed to wait for preload " + tableClassName + '.');
- }
- } else {
- throw new InmemoException("Unable to find table for class name `" + tableClassName + "`.");
- }
- }
-
- private static void renewTable(Class clazz,
- String indicatorField, Object initialIndicatorValue,
- Indices indices,
- Filter rowFilter) {
- Table table = new Table<>(clazz, indicatorField, rowFilter);
- table.createUpdater(initialIndicatorValue);
-
- for (Index index : indices.getIndices()) {
- table.add(index);
- }
-
- for (RowListener rowListener : indices.getRowListeners()) {
- table.add(rowListener);
- }
-
- for (ItemListener itemListener : indices.getItemListeners()) {
- table.add(itemListener);
- }
-
- table.runUpdater();
- tables.put(ReflectionUtil.getTableClassName(clazz), table);
- }
-
- /**
- * @param clazz Table item class.
- * @param indexConstraint Index to use in search, index value.
- * @param Items class.
- * @return List of _copies_ of satisfying indexConstraint items.
- */
- @SuppressWarnings("UnusedDeclaration")
- public static List find(
- @Nonnull Class clazz,
- @Nonnull IndexConstraint> indexConstraint) {
- return find(clazz, indexConstraint, acceptAnyMatcher());
- }
-
- /**
- * @param clazz Table item class.
- * @param indexConstraint Index to use in search, index value.
- * @param matcher Predicate to choose items.
- * @param Items class.
- * @return List of _copies_ of matched items.
- */
- @SuppressWarnings("UnusedDeclaration")
- public static List find(
- @Nonnull Class clazz,
- @Nonnull IndexConstraint> indexConstraint,
- @Nonnull Matcher matcher) {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
-
- Table extends HasId> table = tables.get(tableClassName);
- if (table == null) {
- throw new InmemoException("Unable to find table for class name `" + tableClassName + "`.");
- }
-
- if (!table.isCompatibleItemClass(clazz)) {
- throw new InmemoException("Table class is incompatible with the given [tableClass=" + table.getClazz()
- + ", clazz=" + clazz + "].");
- }
-
- //noinspection rawtypes
- Matcher tableMatcher = table.convertMatcher(clazz, matcher);
-
- //noinspection unchecked
- List extends HasId> result = table.find(indexConstraint, tableMatcher);
-
- if (result.isEmpty()) {
- return Collections.emptyList();
- } else {
- boolean sameClass = true;
-
- for (HasId hasId : result) {
- if (hasId != null) {
- if (hasId.getClass() != clazz) {
- sameClass = false;
- }
- break;
- }
- }
-
- if (false && sameClass) {
- //This case does not work with multiple classloaders.
- BeanCopier beanCopier = getBeanCopier(clazz, clazz);
- List tableClassResult = new ArrayList<>(result.size());
-
- for (HasId tableItem : result) {
- T item = ReflectionUtil.newInstance(clazz);
- beanCopier.copy(tableItem, item, null);
- tableClassResult.add(item);
- }
-
- return Collections.unmodifiableList(tableClassResult);
- } else {
- List tableClassResult = new ArrayList<>(result.size());
-
- for (HasId tableItem : result) {
- T item = ReflectionUtil.newInstance(clazz);
- ReflectionUtil.copyProperties(tableItem, item);
- tableClassResult.add(item);
- }
-
- return Collections.unmodifiableList(tableClassResult);
- }
- }
- }
-
- /**
- * @param throwOnNotUnique Throw exception if resulting item is not unique.
- * @param clazz Table item class.
- * @param indexConstraint Index to use in search, index value.
- * @param Items class.
- * @return List of _copies_ of satisfying indexConstraint items.
- */
- @SuppressWarnings("UnusedDeclaration")
- public static T findOnly(
- boolean throwOnNotUnique,
- @Nonnull Class clazz,
- @Nonnull IndexConstraint> indexConstraint) {
- return findOnly(throwOnNotUnique, clazz, indexConstraint, acceptAnyMatcher());
- }
-
- /**
- * @param throwOnNotUnique Throw exception if resulting item is not unique.
- * @param clazz Table item class.
- * @param indexConstraint Index to use in search, index value.
- * @param matcher Predicate to choose items.
- * @param Items class.
- * @return List of _copies_ of matched items.
- */
- @SuppressWarnings("UnusedDeclaration")
- public static T findOnly(
- boolean throwOnNotUnique,
- @Nonnull Class clazz,
- @Nonnull IndexConstraint> indexConstraint,
- @Nonnull Matcher matcher) {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
-
- Table extends HasId> table = tables.get(tableClassName);
- if (table == null) {
- throw new InmemoException("Unable to find table for class name `" + tableClassName + "`.");
- }
-
- if (!table.isCompatibleItemClass(clazz)) {
- throw new InmemoException("Table class is incompatible with the given [tableClass=" + table.getClazz()
- + ", clazz=" + clazz + "].");
- }
-
- //noinspection rawtypes
- Matcher tableMatcher = table.convertMatcher(clazz, matcher);
-
- //noinspection unchecked
- HasId result = table.findOnly(throwOnNotUnique, indexConstraint, tableMatcher);
-
- if (result == null) {
- return null;
- }
-
- if (false && result.getClass() == clazz) {
- //This case does not work with multiple classloaders.
- T item = ReflectionUtil.newInstance(clazz);
- getBeanCopier(clazz, clazz).copy(result, item, null);
- return item;
- } else {
- T item = ReflectionUtil.newInstance(clazz);
- ReflectionUtil.copyProperties(result, item);
- return item;
- }
- }
-
- /**
- * @param clazz Table item class.
- * @param indexConstraint Index to use in search, index value.
- * @param Items class.
- * @return Number of satisfying indexConstraint items.
- */
- @SuppressWarnings("UnusedDeclaration")
- public static long findCount(
- @Nonnull Class clazz,
- @Nonnull IndexConstraint> indexConstraint) {
- return findCount(clazz, indexConstraint, acceptAnyMatcher());
- }
-
- /**
- * @param clazz Table item class.
- * @param indexConstraint Index to use in search, index value.
- * @param matcher Predicate to choose items.
- * @param Items class.
- * @return Number of matched items.
- */
- @SuppressWarnings("UnusedDeclaration")
- public static long findCount(
- @Nonnull Class clazz,
- @Nonnull IndexConstraint> indexConstraint,
- @Nonnull Matcher matcher) {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
-
- Table extends HasId> table = tables.get(tableClassName);
- if (table == null) {
- throw new InmemoException("Unable to find table for class name `" + tableClassName + "`.");
- }
-
- if (!table.isCompatibleItemClass(clazz)) {
- throw new InmemoException("Table class is incompatible with the given [tableClass=" + table.getClazz()
- + ", clazz=" + clazz + "].");
- }
-
- //noinspection rawtypes
- Matcher tableMatcher = table.convertMatcher(clazz, matcher);
-
- //noinspection unchecked
- return table.findCount(indexConstraint, tableMatcher);
- }
-
- private static Table extends HasId> getTableByClass(Class clazz) {
- String tableClassName = ReflectionUtil.getTableClassName(clazz);
- Table extends HasId> table = tables.get(tableClassName);
- if (table == null) {
- throw new InmemoException("Unable to find table for class name `" + tableClassName + "`.");
- }
- return table;
- }
-
- @SuppressWarnings("UnusedDeclaration")
- public static void insertOrUpdate(@Nullable T object) {
- if (object == null) {
- return;
- }
-
- getTableByClass(object.getClass()).insertOrUpdate(object, null);
- }
-
- public static void update(Class clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("Illegal arguments for Inmemo#update: clazz = ");
- }
-
- getTableByClass(clazz).update();
- }
-
- public static void insertOrUpdateByIds(Class clazz, Long... ids) {
- if (clazz == null) {
- throw new IllegalArgumentException("Illegal arguments for Inmemo#insertOrUpdateByIds: clazz = ");
- }
- if (ids == null) {
- throw new IllegalArgumentException("Illegal arguments for Inmemo#insertOrUpdateByIds: ids = ");
- }
-
- if (ids.length == 0) {
- return;
- }
-
- getTableByClass(clazz).insertOrUpdateByIds(ids);
- }
-
- /**
- * Sends shutdown signal to all Inmemo background threads. There is no guarantee, that all threads are completely
- * stopped, when this method finishes.
- */
- public static void stopAllThreads() {
- TableUpdater.stop();
- }
-
- @SuppressWarnings("UnusedDeclaration")
- public static void setDataSource(@Nonnull DataSource dataSource) {
- TableUpdater.setDataSource(dataSource);
- }
-
- public static void setSpecificDataSource(Class> clazz, @Nonnull DataSource dataSource) {
- TableUpdater.setSpecificDataSource(clazz, dataSource);
- }
-
- public static void deleteJournal(Class clazz) throws IOException {
- if (clazz == null) {
- throw new IllegalArgumentException("Illegal arguments for Inmemo#deleteJournal: clazz = ");
- }
-
- getTableByClass(clazz).deleteJournal();
- }
-
- public interface Filter {
- boolean testRow(Row row);
- boolean testItem(T item);
- }
-
- private static final class ClassPair {
- private final Class> classA;
- private final Class> classB;
-
- private ClassPair(Class> classA, Class> classB) {
- this.classA = classA;
- this.classB = classB;
- }
-
- @SuppressWarnings("RedundantIfStatement")
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- ClassPair classPair = (ClassPair) o;
-
- if (!classA.equals(classPair.classA)) return false;
- if (!classB.equals(classPair.classB)) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = classA.hashCode();
- result = 31 * result + classB.hashCode();
- return result;
- }
- }
-}
+package com.codeforces.inmemo;
+
+import net.sf.cglib.beans.BeanCopier;
+import org.apache.log4j.Logger;
+import org.jacuzzi.core.Row;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ *
+ * Inmemo is a framework to store entities in-memory and update their state in memory automatically on each update
+ * in database. Each entity should have `indicatorField` which updates to greater value on each change. The best choice
+ * is `updateTime` field of MySQL type `TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`. It
+ * supports indices (based on hashmaps) and queries to find list of entities or counts. Refer to examples for
+ * clarifications.
+ *
+ *
+ * The implementation is friendly for class-reloading magic like in Nocturne
+ * framework.
+ *
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+public final class Inmemo {
+ private static final Logger logger = Logger.getLogger(Inmemo.class);
+ private static final Map> tables = new ConcurrentHashMap<>();
+ private static final Lock tablesLock = new ReentrantLock();
+ private static final ConcurrentMap beanCopiers = new ConcurrentHashMap<>();
+ private static volatile boolean debug;
+ private static final Set> noSizeSupportClasses = new HashSet<>();
+ private static final Set noJournalSupportTableClassNames
+ = Collections.newSetFromMap(new ConcurrentHashMap<>());
+
+ private Inmemo() {
+ // No operations.
+ }
+
+ static boolean isDebug() {
+ return debug;
+ }
+
+ public static void setDebug(boolean debug) {
+ Inmemo.debug = debug;
+ }
+
+ private static BeanCopier getBeanCopier(Class> sourceClass, Class> targetClass) {
+ ClassPair classPair = new ClassPair(sourceClass, targetClass);
+ BeanCopier beanCopier = beanCopiers.get(classPair);
+ if (beanCopier == null) {
+ BeanCopier copier = BeanCopier.create(sourceClass, targetClass, false);
+ logger.info("Created BeanCopier(" + sourceClass + ',' + targetClass + ").");
+ beanCopiers.putIfAbsent(classPair, copier);
+ return beanCopiers.get(classPair);
+ } else {
+ return beanCopier;
+ }
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public static Matcher acceptAnyMatcher() {
+ return tableItem -> true;
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public static boolean isPreloaded() {
+ tablesLock.lock();
+ try {
+ for (Table extends HasId> table : tables.values()) {
+ if (!table.isPreloaded()) {
+ return false;
+ }
+ }
+ } finally {
+ tablesLock.unlock();
+ }
+
+ return true;
+ }
+
+ /**
+ * Table maintains ids set to support size() operation. Use this function
+ * to refuse support of size() to reduce memory footprint.
+ *
+ * @param clazz Table item class.
+ */
+ public static void unsetSizeSupport(@Nonnull Class> clazz) {
+ noSizeSupportClasses.add(clazz);
+ }
+
+ /**
+ * Disables journal support for a table class. The first call for a class
+ * must be made before createTable; repeated calls are always no-op.
+ *
+ * @param clazz Table item class.
+ */
+ public static void unsetJournalSupport(@Nonnull Class> clazz) {
+ String tableClassName = ReflectionUtil.getTableClassName(clazz);
+ tablesLock.lock();
+ try {
+ if (noJournalSupportTableClassNames.contains(tableClassName)) {
+ return;
+ }
+ if (tables.containsKey(tableClassName)) {
+ throw new IllegalStateException("Inmemo.unsetJournalSupport(clazz) must be called"
+ + " before Inmemo.createTable [clazz=" + tableClassName + "].");
+ }
+ noJournalSupportTableClassNames.add(tableClassName);
+ } finally {
+ tablesLock.unlock();
+ }
+ }
+
+ static Set> getNoSizeSupportClasses() {
+ return noSizeSupportClasses;
+ }
+
+ static boolean isJournalSupportUnset(@Nonnull Class> clazz) {
+ return noJournalSupportTableClassNames.contains(ReflectionUtil.getTableClassName(clazz));
+ }
+
+ static void putTableForUnsetJournalSupportTestOnly(Class clazz) {
+ tablesLock.lock();
+ try {
+ tables.put(ReflectionUtil.getTableClassName(clazz), new Table<>(clazz, "id", null));
+ } finally {
+ tablesLock.unlock();
+ }
+ }
+
+ static void removeTableForUnsetJournalSupportTestOnly(Class clazz) {
+ tablesLock.lock();
+ try {
+ tables.remove(ReflectionUtil.getTableClassName(clazz));
+ } finally {
+ tablesLock.unlock();
+ }
+ }
+
+ /**
+ * Creates new table, if there is already table for compatible class then doing nothing.
+ *
+ * @param clazz Table item class.
+ * @param indicatorField Item field which will be monitored to increase on each change. Good idea to make it
+ * 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'.
+ * @param initialIndicatorValue Initial value of indicator, will be loaded only items with at least
+ * {@code initialIndicatorValue}. Use {@code null} to load all the items.
+ * @param indices Indices built with Indices.Builder.
+ * @param Item class.
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static void createTable(
+ @Nonnull Class clazz,
+ @Nonnull String indicatorField,
+ @Nullable Object initialIndicatorValue,
+ @Nonnull Indices indices,
+ boolean waitForPreload) {
+ createTable(clazz, indicatorField, initialIndicatorValue, indices, null, waitForPreload);
+ }
+
+ /**
+ * Creates new table, if there is already table for compatible class then doing nothing.
+ *
+ * @param clazz Table item class.
+ * @param indicatorField Item field which will be monitored to increase on each change. Good idea to make it
+ * 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'.
+ * @param initialIndicatorValue Initial value of indicator, will be loaded only items with at least
+ * {@code initialIndicatorValue}. Use {@code null} to load all the items.
+ * @param indices Indices built with Indices.Builder.
+ * @param rowFilter Filter predicate for rows to be processed from DB
+ * @param Item class.
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static void createTable(
+ @Nonnull Class clazz,
+ @Nonnull String indicatorField,
+ @Nullable Object initialIndicatorValue,
+ @Nonnull Indices indices,
+ @Nullable Filter rowFilter,
+ boolean waitForPreload) {
+ tablesLock.lock();
+
+ try {
+ String tableClassName = ReflectionUtil.getTableClassName(clazz);
+
+ Table extends HasId> table = tables.get(tableClassName);
+ if (table == null) {
+ renewTable(clazz, indicatorField, initialIndicatorValue, indices, rowFilter);
+
+ table = tables.get(tableClassName);
+ if (waitForPreload) {
+ while (!table.isPreloaded()) {
+ try {
+ Thread.sleep(100L);
+ } catch (InterruptedException ignored) {
+ break;
+ }
+ }
+ logger.info("Inmemo completed to wait for preload " + tableClassName + '.');
+ }
+ } else {
+ // Exactly the same class?
+ if (table.getClazz().equals(clazz)) {
+ // logger.info("Exactly the same class [class=" + clazz + "].");
+ return;
+ }
+
+ String clazzSpec = ReflectionUtil.getTableClassSpec(clazz);
+ // Compatible classes?
+ if (table.getClazzSpec().equals(clazzSpec)) {
+ logger.info("Compatible classes " + tableClassName + " [class=" + clazz + "].");
+ return;
+ }
+
+ renewTable(clazz, indicatorField, initialIndicatorValue, indices, rowFilter);
+
+ table = tables.get(tableClassName);
+ if (waitForPreload) {
+ while (!table.isPreloaded()) {
+ try {
+ Thread.sleep(100L);
+ } catch (InterruptedException ignored) {
+ break;
+ }
+ }
+ logger.info("Inmemo completed to wait for preload " + tableClassName + '.');
+ }
+ }
+ } finally {
+ tablesLock.unlock();
+ }
+ }
+
+ /**
+ * Drops table or doing nothing.
+ *
+ * @param clazz Table class.
+ * @param Entity class.
+ */
+ public static void dropTableIfExists(@Nonnull Class clazz) {
+ String tableClassName = ReflectionUtil.getTableClassName(clazz);
+ Table extends HasId> table = tables.get(tableClassName);
+
+ if (table != null) {
+ tablesLock.lock();
+ try {
+ tables.remove(tableClassName);
+ } finally {
+ tablesLock.unlock();
+ }
+ }
+ }
+
+ public static int size(@Nonnull Class clazz) {
+ String tableClassName = ReflectionUtil.getTableClassName(clazz);
+ Table extends HasId> table = tables.get(tableClassName);
+
+ if (table != null) {
+ return table.size();
+ } else {
+ throw new InmemoException("Unable to find table for class name `" + tableClassName + "`.");
+ }
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ public static void waitForPreload(@Nonnull Class clazz) {
+ String tableClassName = ReflectionUtil.getTableClassName(clazz);
+ Table extends HasId> table = tables.get(tableClassName);
+
+ if (table != null) {
+ boolean preloadedNow = false;
+ while (!table.isPreloaded()) {
+ preloadedNow = true;
+ try {
+ Thread.sleep(100L);
+ } catch (InterruptedException ignored) {
+ break;
+ }
+ }
+ if (preloadedNow) {
+ logger.info("Inmemo completed to wait for preload " + tableClassName + '.');
+ }
+ } else {
+ throw new InmemoException("Unable to find table for class name `" + tableClassName + "`.");
+ }
+ }
+
+ private static void renewTable(Class clazz,
+ String indicatorField, Object initialIndicatorValue,
+ Indices indices,
+ Filter rowFilter) {
+ Table