All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mindoo.domino.jna.CollectionDataCache Maven / Gradle / Ivy

There is a newer version: 0.9.53
Show newest version
package com.mindoo.domino.jna;

import java.io.Serializable;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import com.mindoo.domino.jna.NotesCollection.ViewLookupCallback;
import com.mindoo.domino.jna.NotesViewEntryData.CacheableViewEntryData;
import com.mindoo.domino.jna.constants.ReadMask;

/**
 * LRU cache class to be returned in {@link ViewLookupCallback#createDataCache()} in order to let NIF
 * improve lookup performance by skipping already known collection data.
*
* Please note that according to IBM dev, this optimized view reading (differential view reads) does * only work in views that are not permuted (where documents do not appear multiple times, because * "Show multiple values as separate entries" has been set on any view column). * * @author Karsten Lehmann */ public class CollectionDataCache implements Serializable { private static final long serialVersionUID = 522152090817358117L; private int m_maxSize; private Map m_cacheEntries; private NotesTimeDate m_diffTime; private ReentrantReadWriteLock m_rwLock = new ReentrantReadWriteLock(); private EnumSet m_readMask; private static ThreadLocal m_cacheUseCounter = new ThreadLocal(); /** * Creates a new instance of an unbounded cache */ public CollectionDataCache() { this(Integer.MAX_VALUE); } /** * Creates a new instance * * @param maxSize maximum number of entries in the LRU cache */ public CollectionDataCache(final int maxSize) { if (maxSize <= 0) throw new IllegalArgumentException("Max size must be greater than 0: "+maxSize); m_maxSize = maxSize; //set up LRU cache map m_cacheEntries = Collections.synchronizedMap(new LinkedHashMap(16, 0.75f, true) { @Override protected boolean removeEldestEntry(java.util.Map.Entry eldest) { if (size() > maxSize) { return true; } else { return false; } } }); } /** * Returns the maximum number of entries in the cache * * @return maximum number */ public int getMaxCacheSize() { return m_maxSize; } /** * Returns the current number of entries in the cache * * @return size */ public int size() { m_rwLock.readLock().lock(); try { return m_cacheEntries.size(); } finally { m_rwLock.readLock().unlock(); } } /** * Enables taking cache usage stats for the current thread */ public void enableUsageStats() { Long counter = m_cacheUseCounter.get(); if (counter==null) { m_cacheUseCounter.set(Long.valueOf(0)); } } /** * Disables taking cache usage stats for the current thread */ public void disableUsageStats() { m_cacheUseCounter.set(null); } /** * Method to check whether taking cache usage stats for the * current thread is enabled * * @return true if enabled */ public boolean isUsageStatsEnabled() { return m_cacheUseCounter.get() != null; } /** * Returns a statistic value with the number of view entries where we could use the cache data * * @return count or -1 if logging stats are not enabled */ public long getCacheUsageStats() { Long counter = m_cacheUseCounter.get(); if (counter!=null) { return counter.longValue(); } return -1; } /** * Sets the cache usage stats to 0 */ public void resetCacheUsageStats() { Long counter = m_cacheUseCounter.get(); if (counter!=null) { m_cacheUseCounter.set(Long.valueOf(0)); } } /** * Removes all data from the cache */ public void flush() { m_rwLock.writeLock().lock(); try { m_diffTime = null; m_readMask = null; m_cacheEntries.clear(); } finally { m_rwLock.writeLock().unlock(); } } /** * Method to fill the cache with data read from the collection * * @param diffTime diff time returned from the read operation * @param entries collection entries read */ void addCacheValues(EnumSet readMask, NotesTimeDate diffTime, List entries) { m_rwLock.writeLock().lock(); try { boolean flush = false; if (m_diffTime!=null && !m_diffTime.equals(diffTime)) { flush = true; } else if (m_readMask!=null && !m_readMask.equals(readMask)) { flush = true; } if (flush) { m_cacheEntries.clear(); cacheFlushed(); } m_readMask = readMask; m_diffTime = diffTime; for (NotesViewEntryData currEntry : entries) { if (currEntry.hasAnyColumnValues()) { CacheableViewEntryData cacheableData = currEntry.getCacheableData(); m_cacheEntries.put(currEntry.getNoteId(), cacheableData); } } } finally { m_rwLock.writeLock().unlock(); } } /** * Called when the cache needed to be flushed because of view index changes. * Method is empty by default, can be overriden, e.g. to write a log entry. */ protected void cacheFlushed() { // } /** * For every {@link NotesViewEntryData} in the specified list, this method checks whether * NIF returned any column data. If not, the entry was skipped by NIF, because it already exists * in the cache. We can then copy the data of our current cache object. * * @param entries entries to scan */ void populateEntryStubsWithData(List entries) { boolean hasAnyMissingData = false; for (NotesViewEntryData currEntry : entries) { if (!currEntry.hasAnyColumnValues()) { hasAnyMissingData = true; break; } } Long usageStats = m_cacheUseCounter.get(); long usageStatsPrim = usageStats==null ? -1 : usageStats.longValue(); if (hasAnyMissingData) { m_rwLock.readLock().lock(); try { for (NotesViewEntryData currEntry : entries) { if (!currEntry.hasAnyColumnValues()) { CacheableViewEntryData cacheData = m_cacheEntries.get(currEntry.getNoteId()); if (cacheData!=null) { //updating data of stub entry from cache currEntry.updateFromCache(cacheData); if (usageStatsPrim!=-1) { usageStatsPrim++; } } } } if (usageStatsPrim!=-1) { m_cacheUseCounter.set(usageStatsPrim); } } finally { m_rwLock.readLock().unlock(); } } } /** * Copies the current state of the cache * * @return state */ CacheState getCacheState() { m_rwLock.readLock().lock(); try { return new CacheState(m_readMask, m_diffTime, new HashMap(m_cacheEntries)); } finally { m_rwLock.readLock().unlock(); } } /** * Data object with cache state values * * @author Karsten Lehmann */ static class CacheState { private NotesTimeDate m_diffTime; private Map m_cacheEntries; private EnumSet m_readMask; private CacheState(EnumSet readMask, NotesTimeDate diffTime, Map cacheEntries) { m_readMask = readMask; m_diffTime = diffTime; m_cacheEntries = cacheEntries; } public EnumSet getReadMask() { return m_readMask; } public NotesTimeDate getDiffTime() { return m_diffTime; } public Map getCacheEntries() { return m_cacheEntries; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy