package rocks.inspectit.shared.cs.indexing.indexer.impl; import java.sql.Timestamp; import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; import rocks.inspectit.shared.all.communication.DefaultData; import rocks.inspectit.shared.all.indexing.IIndexQuery; import rocks.inspectit.shared.cs.indexing.indexer.IBranchIndexer; /** * {@link IBranchIndexer} that indexes on the timestamp of the {@link DefaultData}. The index is * calculated in the way that a key in made for each {@link #indexingPeriod/1000} seconds of time.2 * * @author Ivan Senic * * @param <E> */ public class TimestampIndexer<E extends DefaultData> implements IBranchIndexer<E> { /** * Constant for empty keys. */ private static final Object[] EMPTY_KEYS = new Object[0]; /** * Indexing period. Value is {@value #INDEXING_PERIOD} milliseconds. * <p> * ISE: Increased to 15 minutes, because it s not necessary to have such a strict limit. */ private static final long INDEXING_PERIOD = 15 * 60 * 1000; /** * To make this class serializable and support concurrency we have to serialize the map, and can * not use set because there is not concurrent set. The java.util.Collections$SetFromMap is not * being able to be serialized because it has no no-arg constructor. */ private ConcurrentHashMap<Long, Boolean> createdKeysMap = new ConcurrentHashMap<>(8, 0.75f, 1); /** * Min created key. Used for providing keys for queries. */ private long minCreatedKey = Long.MAX_VALUE; /** * Max created key. Used for providing keys for queries. */ private long maxCreatedKey = 0; /** * {@inheritDoc} */ @Override public Object getKey(E element) { if (null == element.getTimeStamp()) { return null; } long key = getKey(element.getTimeStamp()); createdKeysMap.put(Long.valueOf(key), Boolean.TRUE); if (key < minCreatedKey) { minCreatedKey = key; } if (key > maxCreatedKey) { maxCreatedKey = key; } return key; } /** * {@inheritDoc} */ @Override public Object[] getKeys(IIndexQuery query) { if (!query.isIntervalSet()) { return EMPTY_KEYS; // NOPMD } long startKey = 0; if (null != query.getFromDate()) { startKey = getKey(query.getFromDate()); } if (startKey < minCreatedKey) { startKey = minCreatedKey; } long endKey = Long.MAX_VALUE; if (null != query.getToDate()) { endKey = getKey(query.getToDate()); } if (endKey > maxCreatedKey) { endKey = maxCreatedKey; } int size = (int) (((endKey - startKey) / INDEXING_PERIOD) + 1); ArrayList<Object> keysList = new ArrayList<>(); for (int i = 0; i < size; i++) { long key = startKey + (i * INDEXING_PERIOD); if (createdKeysMap.containsKey(key)) { keysList.add(key); } } return keysList.toArray(new Object[keysList.size()]); } /** * Returns proper key for given timestamp. * * @param timestamp * Timestamp to map. * @return Mapping key. */ private long getKey(Timestamp timestamp) { return timestamp.getTime() - (timestamp.getTime() % INDEXING_PERIOD); } /** * {@inheritDoc} */ @Override public boolean sharedInstance() { return false; } /** * {@inheritDoc} */ @Override public IBranchIndexer<E> getNewInstance() { return new TimestampIndexer<>(); } /** * {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + ((createdKeysMap == null) ? 0 : createdKeysMap.hashCode()); result = (prime * result) + (int) (maxCreatedKey ^ (maxCreatedKey >>> 32)); result = (prime * result) + (int) (minCreatedKey ^ (minCreatedKey >>> 32)); return result; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } TimestampIndexer<E> other = (TimestampIndexer<E>) obj; if (createdKeysMap == null) { if (other.createdKeysMap != null) { return false; } } else if (!createdKeysMap.equals(other.createdKeysMap)) { return false; } if (maxCreatedKey != other.maxCreatedKey) { return false; } if (minCreatedKey != other.minCreatedKey) { return false; } return true; } }