From 33511da0736d3475b9dac80117ef0bc3b70c88a6 Mon Sep 17 00:00:00 2001 From: Damianofds Date: Wed, 4 Jun 2014 11:54:03 +0200 Subject: [PATCH] Fixed ConcurrentTileCacheMultiMap and removed other unused caches --- concurrent-tile-cache/pom.xml | 16 - .../concurrent/ConcurrentTileCache.java | 3 - .../ConcurrentTileCacheMultiMap.java | 351 ++++++++----- .../concurrentlinked/ConcurrentCache.java | 440 ---------------- .../ConcurrentLinkedCache.java | 486 ------------------ .../ConcurrentNonBlockingCache.java | 411 --------------- .../concurrencytest/CacheCoberturaTest.java | 2 +- .../concurrencytest/ConcurrentCacheTest.java | 40 +- .../ConcurrentMultiMapTest.java | 155 ++++++ .../concurrencytest/test-data/world.tiff | Bin 0 -> 52550 bytes .../geosolutions/jaiext/range/RangeTest.java | 220 ++++---- pom.xml | 2 +- 12 files changed, 504 insertions(+), 1622 deletions(-) delete mode 100644 concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentCache.java delete mode 100644 concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentLinkedCache.java delete mode 100644 concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentNonBlockingCache.java create mode 100644 concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/ConcurrentMultiMapTest.java create mode 100644 concurrent-tile-cache/src/test/resources/it/geosolutions/concurrencytest/test-data/world.tiff diff --git a/concurrent-tile-cache/pom.xml b/concurrent-tile-cache/pom.xml index c6f6061d..8e9ca15c 100644 --- a/concurrent-tile-cache/pom.xml +++ b/concurrent-tile-cache/pom.xml @@ -17,26 +17,10 @@ gt-coverage ${geotools.version} - - com.googlecode.concurrentlinkedhashmap - concurrentlinkedhashmap-lru - 1.3.2 - - - com.boundary - high-scale-lib - 1.0.3 - com.google.guava guava ${guava.version} - - - boundary-site - http://maven.boundary.com/artifactory/repo - - diff --git a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrent/ConcurrentTileCache.java b/concurrent-tile-cache/src/main/java/it/geosolutions/concurrent/ConcurrentTileCache.java index dee91556..c00eead9 100644 --- a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrent/ConcurrentTileCache.java +++ b/concurrent-tile-cache/src/main/java/it/geosolutions/concurrent/ConcurrentTileCache.java @@ -105,9 +105,6 @@ public int weigh(Object o, CachedTileImpl cti) { return (int) cti.getTileSize(); } }); - if (diagnosticEnabled) { - builder.removalListener(listener).recordStats(); - } return builder.build(); diff --git a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrent/ConcurrentTileCacheMultiMap.java b/concurrent-tile-cache/src/main/java/it/geosolutions/concurrent/ConcurrentTileCacheMultiMap.java index 559416ae..0f76eaa8 100644 --- a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrent/ConcurrentTileCacheMultiMap.java +++ b/concurrent-tile-cache/src/main/java/it/geosolutions/concurrent/ConcurrentTileCacheMultiMap.java @@ -7,20 +7,36 @@ import java.awt.image.RenderedImage; import java.util.Comparator; import java.util.Iterator; +import java.util.Map; import java.util.Observable; import java.util.Set; import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.media.jai.TileCache; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalCause; import com.google.common.cache.RemovalListener; import com.google.common.cache.RemovalNotification; import com.google.common.cache.Weigher; import com.sun.media.jai.util.CacheDiagnostics; +/** + * This implementation of the TileCache class uses a Guava Cache and a multimap in order to provide a better concurrency handling. The first object + * contains all the cached tiles while the second one contains the mapping of the tile keys for each image. This class implements + * {@link CacheDiagnostics} in order to get the statistics associated to the {@link TileCache}. The user can define the cache memory capacity, the + * concurrency level (which indicates in how many segments the cache must be divided), the threshold of the total memory to use and a boolean + * indicating if the diagnostic must be enabled. + * + * @author Nicola Lagomarsini GeoSolutions S.A.S. + * + */ public class ConcurrentTileCacheMultiMap extends Observable implements TileCache, CacheDiagnostics { /** The default memory threshold of the cache. */ @@ -43,11 +59,14 @@ public class ConcurrentTileCacheMultiMap extends Observable implements TileCache /** * A concurrent multimap used for mapping the tile keys for each image */ - private Cache> multimap; + private Map> multimap; /** The memory capacity of the cache. */ private long memoryCacheCapacity; + /** The current memory capacity of the cache. */ + private AtomicLong currentCacheCapacity; + /** The concurrency level of the cache. */ private int concurrencyLevel; @@ -58,9 +77,10 @@ public class ConcurrentTileCacheMultiMap extends Observable implements TileCache private volatile boolean diagnosticEnabled = DEFAULT_DIAGNOSTIC; /** - * The listener is used for receiving notification about the removal of a tile + * Logger to use for reporting the informations about the TileCache operations. */ - private final RemovalListener listener; + private final static Logger LOGGER = Logger.getLogger(ConcurrentTileCacheMultiMap.class + .toString()); public ConcurrentTileCacheMultiMap() { this(DEFAULT_MEMORY_CACHE, DEFAULT_DIAGNOSTIC, DEFAULT_MEMORY_THRESHOLD, @@ -77,14 +97,11 @@ public ConcurrentTileCacheMultiMap(long memoryCacheCapacity, boolean diagnostic, this.memoryCacheCapacity = memoryCacheCapacity; this.concurrencyLevel = concurrencyLevel; - // Listener creation - listener = createListener(diagnostic); - // cache creation cacheObject = buildCache(); // multimap creation - multimap = CacheBuilder.newBuilder().concurrencyLevel(concurrencyLevel).build(); + multimap = new ConcurrentHashMap>(); } /** Add a new tile to the cache */ @@ -96,35 +113,42 @@ public void add(RenderedImage owner, int tileX, int tileY, Raster data) { public void add(RenderedImage owner, int tileX, int tileY, Raster data, Object tileCacheMetric) { // This tile is not in the cache; create a new CachedTileImpl. // else just update. - Object key = CachedTileImpl.hashKey(owner, tileX, tileY); + // Key associated to the image Object imageKey = CachedTileImpl.hashKey(owner); + // old tile CachedTileImpl cti; // create a new tile CachedTileImpl cti_new = new CachedTileImpl(owner, tileX, tileY, data, tileCacheMetric); - // if the tile is already cached if (diagnosticEnabled) { - cti = (CachedTileImpl) cacheObject.asMap().put(key, cti_new); - synchronized (this) { + // if the tile is already cached + cti = (CachedTileImpl) cacheObject.asMap().putIfAbsent(cti_new.key, cti_new); + synchronized (cacheObject) { if (cti != null) { cti.updateTileTimeStamp(); cti.setAction(Actions.SUBSTITUTION_FROM_ADD); setChanged(); notifyObservers(cti); } + // Update Cache Memory Size + currentCacheCapacity.addAndGet(cti_new.getTileSize()); + // Update the tile action in order to notify it to the observers cti_new.setAction(Actions.ADDITION); setChanged(); notifyObservers(cti_new); - updateMultiMap(key, imageKey); + updateMultiMap(cti_new.key, imageKey); } } else { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Added new Tile Image key " + imageKey); + } // new tile insertion - cacheObject.put(key, cti_new); + cacheObject.asMap().putIfAbsent(cti_new.key, cti_new); // Atomically adds a new Map if needed and then adds a new tile inside the MultiMap. - updateMultiMap(key, imageKey); + updateMultiMap(cti_new.key, imageKey); } } @@ -133,7 +157,7 @@ public void remove(RenderedImage owner, int tileX, int tileY) { // Calculation of the tile key Object key = CachedTileImpl.hashKey(owner, tileX, tileY); // remove operation - removeTileFromKey(key); + removeTileByKey(key); } /** Retrieves the selected tile from the cache */ @@ -155,14 +179,20 @@ public Raster[] getTiles(RenderedImage owner) { // Calculation of the key associated to the image Object imageKey = CachedTileImpl.hashKey(owner); - Set keys = multimap.getIfPresent(imageKey); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Getting image Tiles Image key " + imageKey); + } + // Selection of the tile keys for the image + Set keys = multimap.get(imageKey); + // If no key is found then a null object is returned if (keys == null || keys.isEmpty()) { return tilesData; } + // Else it is created an iterator on the tile keys Iterator it = keys.iterator(); - + // Another check on the iterator if (it.hasNext()) { // arbitrarily set a temporary vector size Vector tempData = new Vector(10, 20); @@ -170,6 +200,7 @@ public Raster[] getTiles(RenderedImage owner) { // cache... while (it.hasNext()) { Object key = it.next(); + // get the tile from the key Raster rasterTile = getTileFromKey(key); // ...then add to the vector if present @@ -177,7 +208,7 @@ public Raster[] getTiles(RenderedImage owner) { tempData.add(rasterTile); } } - + // Vector size int tmpsize = tempData.size(); if (tmpsize > 0) { tilesData = (Raster[]) tempData.toArray(new Raster[tmpsize]); @@ -195,23 +226,25 @@ public void removeTiles(RenderedImage owner) { Object imageKey = CachedTileImpl.hashKey(owner); if (diagnosticEnabled) { - synchronized (this) { - Set keys = multimap.getIfPresent(imageKey); + synchronized (cacheObject) { + // Selection of the keys associated to the image and removal of each of them + Set keys = multimap.get(imageKey); if (keys != null) { Iterator it = keys.iterator(); while (it.hasNext()) { Object key = it.next(); - removeTileFromKey(key); + removeTileByKey(key); } } } } else { // Get the keys associated to the image and remove them - Set keys = multimap.getIfPresent(imageKey); + Set keys = multimap.get(imageKey); if (keys != null) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Removing image Tiles Image key " + imageKey); + } cacheObject.invalidateAll(keys); - // Then remove the multimap - multimap.invalidate(imageKey); } } } @@ -221,6 +254,9 @@ public void removeTiles(RenderedImage owner) { */ public void addTiles(RenderedImage owner, Point[] tileIndices, Raster[] tiles, Object tileCacheMetric) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Addeding Tiles"); + } // cycle through the array for adding tiles for (int i = 0; i < tileIndices.length; i++) { int tileX = tileIndices[i].x; @@ -237,6 +273,10 @@ public void addTiles(RenderedImage owner, Point[] tileIndices, Raster[] tiles, public Raster[] getTiles(RenderedImage owner, Point[] tileIndices) { // instantiation of the array Raster[] tilesData = new Raster[tileIndices.length]; + + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Getting Tiles at the selected positions"); + } // cycle through the array for getting tiles for (int i = 0; i < tilesData.length; i++) { int tileX = tileIndices[i].x; @@ -257,31 +297,38 @@ public Raster[] getTiles(RenderedImage owner, Point[] tileIndices) { } /** Removes all tiles present in the cache without checking for the image owner */ - public synchronized void flush() { - // It is necessary to clear all the elements - // from the old cache. - if (diagnosticEnabled) { - // Creation of an iterator for accessing to every tile in the cache - Iterator keys = cacheObject.asMap().keySet().iterator(); - // cycle across the cache for removing and updating every tile - while (keys.hasNext()) { - Object key = keys.next(); - CachedTileImpl cti = (CachedTileImpl) cacheObject.asMap().remove(key); + public void flush() { + synchronized (cacheObject) { + // It is necessary to clear all the elements + // from the old cache. + if (diagnosticEnabled) { + // Creation of an iterator for accessing to every tile in the cache + Iterator keys = cacheObject.asMap().keySet().iterator(); + // cycle across the cache for removing and updating every tile + while (keys.hasNext()) { + Object key = keys.next(); + CachedTileImpl cti = (CachedTileImpl) cacheObject.asMap().remove(key); - // diagnosticEnabled + // diagnosticEnabled - cti.setAction(Actions.REMOVAL_FROM_FLUSH); - setChanged(); - notifyObservers(cti); + cti.setAction(Actions.REMOVAL_FROM_FLUSH); + setChanged(); + notifyObservers(cti); + } + } else { + // Invalidation of all the keys of the cache + cacheObject.invalidateAll(); } - } else { - cacheObject.invalidateAll(); - } - // multimap creation - multimap = CacheBuilder.newBuilder().concurrencyLevel(concurrencyLevel).build(); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Flushing cache"); + } - cacheObject = buildCache(); + // Cache creation + cacheObject = buildCache(); + // multimap creation + multimap = new ConcurrentHashMap>(); + } } /** @@ -291,7 +338,6 @@ public synchronized void flush() { */ public void memoryControl() { throw new UnsupportedOperationException("Memory Control not supported"); - } /** @@ -313,15 +359,16 @@ public int getTileCapacity() { } /** Sets the cache memory capacity and then flush and rebuild the cache */ - public synchronized void setMemoryCapacity(long memoryCacheCapacity) { - if (memoryCacheCapacity < 0) { - throw new IllegalArgumentException("Memory capacity too small"); - } else { - this.memoryCacheCapacity = memoryCacheCapacity; - flush(); - + public void setMemoryCapacity(long memoryCacheCapacity) { + synchronized (cacheObject) { + if (memoryCacheCapacity < 0) { + throw new IllegalArgumentException("Memory capacity too small"); + } else { + this.memoryCacheCapacity = memoryCacheCapacity; + // The flush is done in order to rebuild the cache with the new settings + flush(); + } } - } /** Retrieve the cache memory capacity */ @@ -330,15 +377,17 @@ public long getMemoryCapacity() { } /** Sets the cache memory threshold and then flush and rebuild the cache */ - public synchronized void setMemoryThreshold(float mt) { - if (mt < 0.0F || mt > 1.0F) { - throw new IllegalArgumentException("Memory threshold should be between 0 and 1"); - } else { - memoryCacheThreshold = mt; - flush(); + public void setMemoryThreshold(float mt) { + synchronized (cacheObject) { + if (mt < 0.0F || mt > 1.0F) { + throw new IllegalArgumentException("Memory threshold should be between 0 and 1"); + } else { + memoryCacheThreshold = mt; + // The flush is done in order to rebuild the cache with the new settings + flush(); + } } - } /** Retrieve the cache memory threshold */ @@ -347,15 +396,17 @@ public float getMemoryThreshold() { } /** Sets the cache ConcurrencyLevel and then flush and rebuild the cache */ - public synchronized void setConcurrencyLevel(int concurrency) { - if (concurrency < 1) { - throw new IllegalArgumentException("ConcurrencyLevel must be at least 1"); - } else { - concurrencyLevel = concurrency; - flush(); + public void setConcurrencyLevel(int concurrency) { + synchronized (cacheObject) { + if (concurrency < 1) { + throw new IllegalArgumentException("ConcurrencyLevel must be at least 1"); + } else { + concurrencyLevel = concurrency; + // The flush is done in order to rebuild the cache with the new settings + flush(); + } } - } /** Retrieve the cache concurrency level */ @@ -383,17 +434,21 @@ public Comparator getTileComparator() { } /** Disables diagnosticEnabled for the observers */ - public synchronized void disableDiagnostics() { - diagnosticEnabled = false; - flush(); - + public void disableDiagnostics() { + synchronized (cacheObject) { + diagnosticEnabled = false; + // The flush is done in order to rebuild the cache with the new settings + flush(); + } } /** Enables diagnosticEnabled for the observers */ - public synchronized void enableDiagnostics() { - diagnosticEnabled = true; - flush(); - + public void enableDiagnostics() { + synchronized (cacheObject) { + diagnosticEnabled = true; + // The flush is done in order to rebuild the cache with the new settings + flush(); + } } /** Retrieves the hit count from the cache statistics */ @@ -405,27 +460,8 @@ public long getCacheHitCount() { } /** Retrieves the current memory size of the cache */ - public synchronized long getCacheMemoryUsed() { - Iterator keys = multimap.asMap().keySet().iterator(); - long memoryUsed = 0; - while (keys.hasNext()) { - Object keyImage = keys.next(); - Set tileKeys = multimap.getIfPresent(keyImage); - if (tileKeys != null) { - int numTiles = tileKeys.size(); - Iterator iterator = tileKeys.iterator(); - if (numTiles > 0) { - CachedTileImpl cti = null; - while (cti == null && iterator.hasNext()) { - cti = cacheObject.getIfPresent(iterator.next()); - } - if (cti != null) { - memoryUsed += (cti.getTileSize() * numTiles); - } - } - } - } - return memoryUsed; + public long getCacheMemoryUsed() { + return currentCacheCapacity.get(); } /** Retrieves the miss count from the cache statistics */ @@ -450,6 +486,12 @@ public void resetCounts() { throw new UnsupportedOperationException("Operation not supported"); } + /** + * Creation of a listener to use for handling the removed tiles + * + * @param diagnostic + * @return + */ private RemovalListener createListener(final boolean diagnostic) { return new RemovalListener() { public void onRemoval(RemovalNotification n) { @@ -458,33 +500,64 @@ public void onRemoval(RemovalNotification n) { // the remove() method if (diagnostic) { - synchronized (this) { + synchronized (cacheObject) { + CachedTileImpl cti = n.getValue(); + // Update of the tile action if (n.wasEvicted()) { - CachedTileImpl cti = n.getValue(); cti.setAction(Actions.REMOVAL_FROM_EVICTION); - removeFromMultiMap(cti); - setChanged(); - notifyObservers(cti); + } else { + cti.setAction(Actions.MANUAL_REMOVAL); } + // Update Cache Memory Size + currentCacheCapacity.addAndGet(-cti.getTileSize()); + // Removal from the multimap + removeTileFromMultiMap(cti); + setChanged(); + notifyObservers(cti); } } else { - if (n.wasEvicted()) { - CachedTileImpl cti = n.getValue(); - removeFromMultiMap(cti); + CachedTileImpl cti = n.getValue(); + if (n.getCause() == RemovalCause.SIZE) { + // Logging if the tile is removed because the size is exceeded + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Removing from MultiMap for size"); + } } + removeTileFromMultiMap(cti); } } }; } - private void removeFromMultiMap(CachedTileImpl cti) { + /** + * Method for removing the tile keys from the multimap. If the KeySet associated to the image is empty, it is removed from the multimap. + * + * @param cti + */ + private void removeTileFromMultiMap(CachedTileImpl cti) { + // Tile key Object key = cti.getKey(); + // Image key Object imageKey = cti.getImageKey(); - Set tileKeys = multimap.getIfPresent(imageKey); + // KeySet associated to the image + Set tileKeys = multimap.get(imageKey); + + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Removing tile from MultiMap Image key " + imageKey); + } + if (tileKeys != null) { + // Removal of the keys tileKeys.remove(key); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Removed Tile Image key " + imageKey); + } + // If the KeySet is empty then it is removed from the multimap if (tileKeys.isEmpty()) { - multimap.invalidate(imageKey); + multimap.remove(imageKey); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Removed image SET Image key " + imageKey); + } } } } @@ -498,58 +571,99 @@ public int weigh(Object o, CachedTileImpl cti) { return (int) cti.getTileSize(); } }); - builder.removalListener(listener); - if (diagnosticEnabled) { - builder.recordStats(); + // Setting of the listener + builder.removalListener(createListener(diagnosticEnabled)); + + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Building Cache"); } + // Update of the Memory cache size + currentCacheCapacity = new AtomicLong(0); return builder.build(); } + /** + * Update of the multimap when a tile is added. + * + * @param key + * @param imageKey + */ private void updateMultiMap(Object key, Object imageKey) { - Set tileKeys = multimap.getIfPresent(imageKey); - synchronized (this) { + Set tileKeys = null; + synchronized (cacheObject) { + // Check if the multimap contains the keys for the image + tileKeys = multimap.get(imageKey); if (tileKeys == null) { + // If no key is present then a new KeySet is created and then added to the multimap tileKeys = new ConcurrentSkipListSet(); multimap.put(imageKey, tileKeys); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Created new Set for the image Image key " + imageKey); + } } } + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Added Tile to the set Image key " + imageKey); + } + // Finally the tile key is added. tileKeys.add(key); } - private void removeTileFromKey(Object key) { + /** + * Removes the tile associated to the key. + * + * @param key + */ + private void removeTileByKey(Object key) { // check if the tile is still in cache CachedTileImpl cti = (CachedTileImpl) cacheObject.getIfPresent(key); // if so the tile is deleted (even if another thread write on it) if (cti != null) { if (diagnosticEnabled) { - synchronized (this) { + synchronized (cacheObject) { + // Upgrade the tile action cti.setAction(Actions.ABOUT_TO_REMOVAL); setChanged(); notifyObservers(cti); - + // Removal of the tile cti = (CachedTileImpl) cacheObject.asMap().remove(key); if (cti != null) { + // Upgrade the tile action cti.setAction(Actions.MANUAL_REMOVAL); setChanged(); notifyObservers(cti); } } } else { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Removed Tile Image key " + cti.getImageKey()); + } + // Discard the tile from the cache cacheObject.invalidate(key); } } } + /** + * Gets the tile associated to the key. + * + * @param key + * @return + */ private Raster getTileFromKey(Object key) { Raster tileData = null; // check if the tile is present - CachedTileImpl cti = (CachedTileImpl) cacheObject.getIfPresent(key); + CachedTileImpl cti = (CachedTileImpl) cacheObject.asMap().get(key); + // If not tile is found, null is returned if (cti == null) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Null Tile returned"); + } return null; } if (diagnosticEnabled) { - synchronized (this) { + synchronized (cacheObject) { // Update last-access time for diagnosticEnabled cti.updateTileTimeStamp(); @@ -558,6 +672,9 @@ private Raster getTileFromKey(Object key) { notifyObservers(cti); } } + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Get the selected tile Image key " + cti.getImageKey()); + } // return the selected tile tileData = cti.getTile(); return tileData; diff --git a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentCache.java b/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentCache.java deleted file mode 100644 index 30bfb171..00000000 --- a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentCache.java +++ /dev/null @@ -1,440 +0,0 @@ -package it.geosolutions.concurrentlinked; - -import it.geosolutions.concurrent.CachedTileImpl; -import it.geosolutions.concurrent.ConcurrentTileCache.Actions; -import java.awt.Point; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.Observable; -import java.util.concurrent.ConcurrentHashMap; -import javax.media.jai.TileCache; -import com.sun.media.jai.util.CacheDiagnostics; - -public class ConcurrentCache extends Observable implements TileCache, - CacheDiagnostics { - -/** Default value for memory threshold */ -public static final float DEFAULT_MEMORY_THRESHOLD = 0.75F; - -/** Default value for cache memory */ -public static final long DEFAULT_MEMORY_CACHE = 16L * 1024L * 1024L; - -/** Default boolean for diagnostic mode */ -public static final boolean DEFAULT_DIAGNOSTIC = false; - -/** Default ConcurrentHashMap concurrency level */ -public static final int DEFAULT_CONCURRENCY_LEVEL = 16; - -/** Cache memory capacity */ -private long memoryCacheCapacity = DEFAULT_MEMORY_CACHE; - -/** concurrency level of the ConcurrentHashMap */ -private int concurrencyLevel = DEFAULT_CONCURRENCY_LEVEL; - -/** Cache memory threshold (typically 0.75F) */ -private float memoryCacheThreshold = DEFAULT_MEMORY_THRESHOLD; - -/** The real cache */ -private ConcurrentHashMap cacheObject = new ConcurrentHashMap( - 1000); - -/** - * Current diagnostic mode (enabled or not). This variable is set to volatile - * for visibility - */ -private volatile boolean diagnosticEnabled = DEFAULT_DIAGNOSTIC; - -/** Current number of cache miss */ -private int missNumber; - -/** Current number of cache hits */ -private int hitNumber; - -/* Simple constructor */ -public ConcurrentCache() { - this(DEFAULT_MEMORY_CACHE, DEFAULT_CONCURRENCY_LEVEL, - DEFAULT_MEMORY_THRESHOLD, DEFAULT_DIAGNOSTIC); -} - -/* Parameterized constructor */ -public ConcurrentCache(long memoryCacheCapacity, int concurrencyLevel, - float memoryCacheThreshold, boolean diagnosticEnabled) { - this.concurrencyLevel = concurrencyLevel; - this.memoryCacheCapacity = memoryCacheCapacity; - this.diagnosticEnabled = diagnosticEnabled; - this.memoryCacheThreshold = memoryCacheThreshold; - /* the counters are set to 0 */ - hitNumber = 0; - missNumber = 0; - System.err.println("Using ConcurrentCache"); -} - -public void add(RenderedImage image, int xTile, int yTile, Raster dataTile) { - this.add(image, xTile, yTile, dataTile, null); -} - -/** - * This method adds a new tile in cache and, if diagnostic is enabled, notify - * observers - */ -public void add(RenderedImage image, int xTile, int yTile, Raster dataTile, - Object tileMetric) { - /* Tile key calculation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - /* New tile creation */ - CachedTileImpl newValue = new CachedTileImpl(image, xTile, yTile, dataTile, - tileMetric); - /* If diagnostic is enabled the tile status is changed */ - if (diagnosticEnabled) { - synchronized (this) { - /* Updates the new tile status and notifies the observers */ - newValue.setAction(Actions.ADDITION); - setChanged(); - notifyObservers(newValue); - /* Puts the new value in cache and takes the old one */ - CachedTileImpl oldValue = cacheObject.put(key, newValue); - if (oldValue != null) { - hitNumber++; - /* Update the old tile status and notify the observers */ - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.SUBSTITUTION_FROM_ADD); - setChanged(); - notifyObservers(oldValue); - return; - } - } - } else { - /* Simply put the value in cache */ - cacheObject.put(key, newValue); - } -} - -/** This method add an array of tiles at given positions */ -public void addTiles(RenderedImage image, Point[] positions, - Raster[] dataTiles, Object tileMetric) { - for (int i = 0; i < positions.length; i++) { - int xIndex = positions[i].x; - int yIndex = positions[i].y; - add(image, xIndex, yIndex, dataTiles[i], tileMetric); - } -} - -/** - * This method flushes the cache and, if diagnostic is enabled, reset the - * counters and, for every tile, update tile status - */ -public synchronized void flush() { - if (diagnosticEnabled) { - /* - * An iterator of all the key in the cache is used for updating every - * tile and the removing it - */ - CachedTileImpl oldValue; - Iterator iter = cacheObject.keySet().iterator(); - while (iter.hasNext()) { - oldValue = cacheObject.remove(iter.next()); - oldValue.setAction(Actions.REMOVAL_FROM_FLUSH); - oldValue.updateTileTimeStamp(); - setChanged(); - notifyObservers(oldValue); - } - /* Counter reset */ - hitNumber = 0; - missNumber = 0; - } else { - /* Simple cache clearing */ - cacheObject.clear(); - } - -} - -/** This method gets the current cache memory capacity */ -public long getMemoryCapacity() { - return memoryCacheCapacity; -} - -/** This method gets the current cache memory threshold */ -public float getMemoryThreshold() { - return memoryCacheThreshold; -} - -/** - * This method gets a tile raster from his (x,y) tile coordinates and the image - * reference - */ -public Raster getTile(RenderedImage image, int xTile, int yTile) { - /* Key creation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - if (diagnosticEnabled) { - synchronized (this) { - /* - * In diagnostic mode the oldvalue, if present, is updated and - * retrieved - */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - /* if the tile is present the hit number is increased */ - hitNumber++; - oldValue.setAction(Actions.UPDATING_TILE_FROM_GETTILE); - oldValue.updateTileTimeStamp(); - /* Observers notifications */ - setChanged(); - notifyObservers(oldValue); - return oldValue.getTile(); - } else { - /* if the tile is not present the miss number is increased */ - missNumber++; - return null; - } - } - } else { - /* The tile is returned if present, else null returned */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - return oldValue.getTile(); - } else { - return null; - } - } -} - -/** All the tile of the specific image are returned */ -public Raster[] getTiles(RenderedImage image) { - // instantiation of the result array - Raster[] tilesData = null; - // total number of tiles present in the cache - int tileCount = (int) cacheObject.size(); - - int size = Math.min(image.getNumXTiles() * image.getNumYTiles(), tileCount); - - if (size > 0) { - int minTx = image.getMinTileX(); - int minTy = image.getMinTileY(); - int maxTx = minTx + image.getNumXTiles(); - int maxTy = minTy + image.getNumYTiles(); - - // creates a temporary data arrayList - ArrayList tempData = new ArrayList(); - // every not-null raster is added to the array - for (int y = minTy; y < maxTy; y++) { - for (int x = minTx; x < maxTx; x++) { - - Raster rasterTile = getTile(image, x, y); - - if (rasterTile != null) { - tempData.add(rasterTile); - } - } - } - // the arrayList is then changed in an array - int tmpsize = tempData.size(); - if (tmpsize > 0) { - tilesData = (Raster[]) tempData.toArray(new Raster[tmpsize]); - } - } - - return tilesData; -} - -/** This method returns an array of tiles at the given positions */ -public Raster[] getTiles(RenderedImage image, Point[] positions) { - /* Initialization of an array of rasters */ - Raster[] tileData = new Raster[positions.length]; - if (diagnosticEnabled) { - synchronized (this) { - /* - * If the diagnostic mode is enabled, the tiles are returned from - * the method getTile which updates the tile status - */ - for (int j = 0; j < positions.length; j++) { - int xTile = positions[j].x; - int yTile = positions[j].y; - tileData[j] = getTile(image, xTile, yTile); - } - } - } else { - /* - * Else, they are simply returned by the ConcurrentHashMap.get() - * method - */ - for (int j = 0; j < positions.length; j++) { - int xTile = positions[j].x; - int yTile = positions[j].y; - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - tileData[j] = cacheObject.get(key).getTile(); - } - } - return tileData; -} - -/** This method removes the specified tile and notify it to the observers */ -public void remove(RenderedImage image, int xTile, int yTile) { - /* Tile key calculation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - if (diagnosticEnabled) { - synchronized (this) { - /* - * In diagnostic mode this method check if the old tile was present - * and if so update its status and notify it to the observers, and - * removes it - */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.ABOUT_TO_REMOVAL); - setChanged(); - notifyObservers(oldValue); - cacheObject.remove(key); - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.MANUAL_REMOVAL); - setChanged(); - notifyObservers(oldValue); - } - } - } else { - /* The tile is removed without checking if it is present or not */ - cacheObject.remove(key); - } -} - -/** This method removes all the tiles that belong to the specified image */ -public void removeTiles(RenderedImage image) { - /* Image tile coordinates */ - int minTx = image.getMinTileX(); - int minTy = image.getMinTileY(); - int maxTx = minTx + image.getNumXTiles(); - int maxTy = minTy + image.getNumYTiles(); - if (diagnosticEnabled) { - synchronized (this) { - /* - * This method is the same for both the diagnostic or non-diagnostic - * mode the difference is the sincronized block - */ - removeAllImageTiles(image, minTx, maxTx, minTy, maxTy); - } - } else { - removeAllImageTiles(image, minTx, maxTx, minTy, maxTy); - } -} - -/** This method cycles through the image eliminating all of its tiles */ -private void removeAllImageTiles(RenderedImage image, int minX, int maxX, - int minY, int maxY) { - for (int y = minY; y < maxY; y++) { - for (int x = minX; x < maxX; x++) { - remove(image, x, y); - } - } -} - -/** This method sets the memory capacity, then flush and rebuild the cache */ -public synchronized void setMemoryCapacity(long memoryCacheCapacity) { - if (memoryCacheCapacity < 0) { - throw new IllegalArgumentException("Memory capacity too small"); - } else { - this.memoryCacheCapacity = memoryCacheCapacity; - flush(); - } - -} - -/** This method sets the memory threshold, then flush and rebuild the cache */ -public synchronized void setMemoryThreshold(float memoryCacheThreshold) { - if (memoryCacheThreshold < 0 || memoryCacheThreshold > 1) { - throw new IllegalArgumentException( - "Memory threshold must be between 0 and 1"); - } else { - this.memoryCacheThreshold = memoryCacheThreshold; - flush(); - } - -} - -/** - * This method sets the cache ConcurrencyLevel and then flush and rebuild the - * cache - */ -public synchronized void setConcurrencyLevel(int concurrency) { - if (concurrency < 1) { - throw new IllegalArgumentException( - "ConcurrencyLevel must be at least 1"); - } else { - concurrencyLevel = concurrency; - flush(); - - } - -} - -/** Diagnostic is disabled, then the cache is flushed and rebuilt */ -public synchronized void disableDiagnostics() { - this.diagnosticEnabled = false; - flush(); -} - -/** Diagnostic is enabled, then the cache is flushed and rebuilt */ -public synchronized void enableDiagnostics() { - this.diagnosticEnabled = true; - flush(); -} - -/** The counters are set to 0 when the cache is flushed */ -public synchronized void resetCounts() { - flush(); -} - -/** This method returns the number of cache hits */ -public long getCacheHitCount() { - return hitNumber; -} - -/** This method returns the cache weighed size */ -public long getCacheMemoryUsed() { - return -1; -} - -/** This method returns the number of cache miss */ -public long getCacheMissCount() { - return missNumber; -} - -/** This method returns the number of tile present in the cache */ -public long getCacheTileCount() { - return cacheObject.size(); -} - -/** This method returns the cache concurrency level */ -public int getConcurrencyLevel() { - return concurrencyLevel; -} - -/** Not supported */ -public void setTileCapacity(int arg0) { - throw new UnsupportedOperationException("Deprecated Operation"); - -} - -/** Not supported */ -public int getTileCapacity() { - throw new UnsupportedOperationException("Deprecated Operation"); -} - -/** Not supported */ -public Comparator getTileComparator() { - throw new UnsupportedOperationException("Comparator not supported"); -} - -/** Not supported */ -public void setTileComparator(Comparator arg0) { - throw new UnsupportedOperationException("Comparator not supported"); -} - -/** Not supported */ -public void memoryControl() { - throw new UnsupportedOperationException("Memory Control not supported"); -} - -} diff --git a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentLinkedCache.java b/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentLinkedCache.java deleted file mode 100644 index ea45cdb3..00000000 --- a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentLinkedCache.java +++ /dev/null @@ -1,486 +0,0 @@ -package it.geosolutions.concurrentlinked; - -import it.geosolutions.concurrent.CachedTileImpl; -import it.geosolutions.concurrent.ConcurrentTileCache.Actions; -import java.awt.Point; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.Observable; -import javax.media.jai.TileCache; -import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; -import com.googlecode.concurrentlinkedhashmap.EvictionListener; -import com.googlecode.concurrentlinkedhashmap.Weigher; -import com.sun.media.jai.util.CacheDiagnostics; - -public class ConcurrentLinkedCache extends Observable implements TileCache, - CacheDiagnostics { - -/** Default value for memory threshold */ -public static final float DEFAULT_MEMORY_THRESHOLD = 0.75F; - -/** Default value for cache memory */ -public static final long DEFAULT_MEMORY_CACHE = 16L * 1024L * 1024L; - -/** Default boolean for diagnostic mode */ -public static final boolean DEFAULT_DIAGNOSTIC = false; - -/** Default ConcurrentLinkedHashMap concurrency level */ -public static final int DEFAULT_CONCURRENCY_LEVEL = 16; - -/** The real cache */ -private ConcurrentLinkedHashMap cacheObject; - -/** Cache memory capacity */ -private long memoryCacheCapacity = DEFAULT_MEMORY_CACHE; - -/** concurrency level of the ConcurrentLinkedHashMap */ -private int concurrencyLevel = DEFAULT_CONCURRENCY_LEVEL; - -/** Cache memory threshold (typically 0.75F) */ -private float memoryCacheThreshold = DEFAULT_MEMORY_THRESHOLD; - -/** - * Current diagnostic mode (enabled or not). This variable is set to volatile - * for visibility - */ -private volatile boolean diagnosticEnabled = DEFAULT_DIAGNOSTIC; - -/** Current number of cache miss */ -private int missNumber; - -/** Current number of cache hits */ -private int hitNumber; - -/** - * Eviction listener for catching the tile evicted by the - * ConcurrentLinkedHashMap and, if diagnostic is enabled, the eviction is - * notified to the observer - */ -private EvictionListener listener = new EvictionListener() { - - public void onEviction(Object key, CachedTileImpl oldValue) { - if (diagnosticEnabled) { - synchronized (this) { - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.REMOVAL_FROM_EVICTION); - setChanged(); - notifyObservers(oldValue); - } - - } - } - -}; - -/* Simple constructor */ -public ConcurrentLinkedCache() { - this(DEFAULT_MEMORY_CACHE, DEFAULT_CONCURRENCY_LEVEL, - DEFAULT_MEMORY_THRESHOLD, DEFAULT_DIAGNOSTIC); -} - -/* Parameterized constructor */ -public ConcurrentLinkedCache(long memoryCacheCapacity, int concurrencyLevel, - float memoryCacheThreshold, boolean diagnosticEnabled) { - this.concurrencyLevel = concurrencyLevel; - this.memoryCacheCapacity = memoryCacheCapacity; - this.diagnosticEnabled = diagnosticEnabled; - this.memoryCacheThreshold = memoryCacheThreshold; - /* the cache instantiation is done in the buildLinkedCache() method */ - cacheObject = buildLinkedCache(); - /* the counters are set to 0 */ - hitNumber = 0; - missNumber = 0; - System.err.println("Using ConcurrentLinkedCache"); -} - -/** Private method for building the cache */ -private ConcurrentLinkedHashMap buildLinkedCache() { - /* Builder instantiation */ - ConcurrentLinkedHashMap.Builder builder = new ConcurrentLinkedHashMap.Builder(); - builder.concurrencyLevel(concurrencyLevel) - .maximumWeightedCapacity( - (long) (memoryCacheCapacity * memoryCacheThreshold)) - /* The weigher is used for weighing every entry */ - .weigher(new Weigher() { - public int weightOf(CachedTileImpl tile) { - return (int) ((CachedTileImpl) tile).getTileSize(); - } - }); - /* Listener is used only with diagnostic */ - if (diagnosticEnabled) { - builder.listener(listener); - } - /* Cache creation */ - return builder.build(); -} - -public void add(RenderedImage image, int xTile, int yTile, Raster dataTile) { - this.add(image, xTile, yTile, dataTile, null); -} - -/** - * This method adds a new tile in cache and, if diagnostic is enabled, notify - * observers - */ -public void add(RenderedImage image, int xTile, int yTile, Raster dataTile, - Object tileMetric) { - /* Tile key calculation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - /* New tile creation */ - CachedTileImpl newValue = new CachedTileImpl(image, xTile, yTile, dataTile, - tileMetric); - /* If diagnostic is enabled the tile status is changed */ - if (diagnosticEnabled) { - synchronized (this) { - /* Updates the new tile status and notifies the observers */ - newValue.setAction(Actions.ADDITION); - setChanged(); - notifyObservers(newValue); - /* Puts the new value in cache and takes the old one */ - CachedTileImpl oldValue = cacheObject.put(key, newValue); - if (oldValue != null) { - hitNumber++; - /* Update the old tile status and notify the observers */ - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.SUBSTITUTION_FROM_ADD); - setChanged(); - notifyObservers(oldValue); - return; - } - } - } else { - /* Simply put the value in cache */ - cacheObject.put(key, newValue); - } -} - -/** This method add an array of tiles at given positions */ -public void addTiles(RenderedImage image, Point[] positions, - Raster[] dataTiles, Object tileMetric) { - for (int i = 0; i < positions.length; i++) { - int xIndex = positions[i].x; - int yIndex = positions[i].y; - add(image, xIndex, yIndex, dataTiles[i], tileMetric); - } -} - -/** - * This method flushes the cache and, if diagnostic is enabled, reset the - * counters and, for every tile, update tile status - */ -public synchronized void flush() { - if (diagnosticEnabled) { - /* - * An iterator of all the key in the cache is used for updating every - * tile and the removing it - */ - CachedTileImpl oldValue; - Iterator iter = cacheObject.keySet().iterator(); - while (iter.hasNext()) { - oldValue = cacheObject.remove(iter.next()); - oldValue.setAction(Actions.REMOVAL_FROM_FLUSH); - oldValue.updateTileTimeStamp(); - setChanged(); - notifyObservers(oldValue); - } - /* Counter reset */ - hitNumber = 0; - missNumber = 0; - } else { - /* Simple cache clearing */ - cacheObject.clear(); - } - /* The cache is rebuilt */ - cacheObject = buildLinkedCache(); -} - -/** This method gets the current cache memory capacity */ -public long getMemoryCapacity() { - return memoryCacheCapacity; -} - -/** This method gets the current cache memory threshold */ -public float getMemoryThreshold() { - return memoryCacheThreshold; -} - -/** - * This method gets a tile raster from his (x,y) tile coordinates and the image - * reference - */ -public Raster getTile(RenderedImage image, int xTile, int yTile) { - /* Key creation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - if (diagnosticEnabled) { - synchronized (this) { - /* - * In diagnostic mode the oldvalue, if present, is updated and - * retrieved - */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - /* if the tile is present the hit number is increased */ - hitNumber++; - oldValue.setAction(Actions.UPDATING_TILE_FROM_GETTILE); - oldValue.updateTileTimeStamp(); - /* Observers notifications */ - setChanged(); - notifyObservers(oldValue); - return oldValue.getTile(); - } else { - /* if the tile is not present the miss number is increased */ - missNumber++; - return null; - } - } - } else { - /* The tile is returned if present, else null returned */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - return oldValue.getTile(); - } else { - return null; - } - } -} - -/** All the tile of the specific image are returned */ -public Raster[] getTiles(RenderedImage image) { - // instantiation of the result array - Raster[] tilesData = null; - // total number of tiles present in the cache - int tileCount = (int) cacheObject.size(); - - int size = Math.min(image.getNumXTiles() * image.getNumYTiles(), tileCount); - - if (size > 0) { - int minTx = image.getMinTileX(); - int minTy = image.getMinTileY(); - int maxTx = minTx + image.getNumXTiles(); - int maxTy = minTy + image.getNumYTiles(); - - // creates a temporary data arrayList - ArrayList tempData = new ArrayList(); - // every not-null raster is added to the array - for (int y = minTy; y < maxTy; y++) { - for (int x = minTx; x < maxTx; x++) { - - Raster rasterTile = getTile(image, x, y); - - if (rasterTile != null) { - tempData.add(rasterTile); - } - } - } - // the arrayList is then changed in an array - int tmpsize = tempData.size(); - if (tmpsize > 0) { - tilesData = (Raster[]) tempData.toArray(new Raster[tmpsize]); - } - } - - return tilesData; -} - -/** This method returns an array of tiles at the given positions */ -public Raster[] getTiles(RenderedImage image, Point[] positions) { - /* Initialization of an array of rasters */ - Raster[] tileData = new Raster[positions.length]; - if (diagnosticEnabled) { - synchronized (this) { - /* - * If the diagnostic mode is enabled, the tiles are returned from - * the method getTile which updates the tile status - */ - for (int j = 0; j < positions.length; j++) { - int xTile = positions[j].x; - int yTile = positions[j].y; - tileData[j] = getTile(image, xTile, yTile); - } - } - } else { - /* - * Else, they are simply returned by the ConcurrentLinkedHashMap.get() - * method - */ - for (int j = 0; j < positions.length; j++) { - int xTile = positions[j].x; - int yTile = positions[j].y; - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - tileData[j] = cacheObject.get(key).getTile(); - } - } - return tileData; -} - -/** This method removes the specified tile and notify it to the observers */ -public void remove(RenderedImage image, int xTile, int yTile) { - /* Tile key calculation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - if (diagnosticEnabled) { - synchronized (this) { - /* - * In diagnostic mode this method check if the old tile was present - * and if so update its status and notify it to the observers, and - * removes it - */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.ABOUT_TO_REMOVAL); - setChanged(); - notifyObservers(oldValue); - cacheObject.remove(key); - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.MANUAL_REMOVAL); - setChanged(); - notifyObservers(oldValue); - } - } - } else { - /* The tile is removed without checking if it is present or not */ - cacheObject.remove(key); - } -} - -/** This method removes all the tiles that belong to the specified image */ -public void removeTiles(RenderedImage image) { - /* Image tile coordinates */ - int minTx = image.getMinTileX(); - int minTy = image.getMinTileY(); - int maxTx = minTx + image.getNumXTiles(); - int maxTy = minTy + image.getNumYTiles(); - if (diagnosticEnabled) { - synchronized (this) { - /* - * This method is the same for both the diagnostic or non-diagnostic - * mode the difference is the sincronized block - */ - removeAllImageTiles(image, minTx, maxTx, minTy, maxTy); - } - } else { - removeAllImageTiles(image, minTx, maxTx, minTy, maxTy); - } -} - -/** This method cycles through the image eliminating all of its tiles */ -private void removeAllImageTiles(RenderedImage image, int minX, int maxX, - int minY, int maxY) { - for (int y = minY; y < maxY; y++) { - for (int x = minX; x < maxX; x++) { - remove(image, x, y); - } - } -} - -/** This method sets the memory capacity, then flush and rebuild the cache */ -public synchronized void setMemoryCapacity(long memoryCacheCapacity) { - if (memoryCacheCapacity < 0) { - throw new IllegalArgumentException("Memory capacity too small"); - } else { - this.memoryCacheCapacity = memoryCacheCapacity; - flush(); - } - -} - -/** This method sets the memory threshold, then flush and rebuild the cache */ -public synchronized void setMemoryThreshold(float memoryCacheThreshold) { - if (memoryCacheThreshold < 0 || memoryCacheThreshold > 1) { - throw new IllegalArgumentException( - "Memory threshold must be between 0 and 1"); - } else { - this.memoryCacheThreshold = memoryCacheThreshold; - flush(); - } - -} - -/** - * This method sets the cache ConcurrencyLevel and then flush and rebuild the - * cache - */ -public synchronized void setConcurrencyLevel(int concurrency) { - if (concurrency < 1) { - throw new IllegalArgumentException( - "ConcurrencyLevel must be at least 1"); - } else { - concurrencyLevel = concurrency; - flush(); - - } - -} - -/** Diagnostic is disabled, then the cache is flushed and rebuilt */ -public synchronized void disableDiagnostics() { - this.diagnosticEnabled = false; - flush(); -} - -/** Diagnostic is enabled, then the cache is flushed and rebuilt */ -public synchronized void enableDiagnostics() { - this.diagnosticEnabled = true; - flush(); -} - -/** The counters are set to 0 when the cache is flushed */ -public synchronized void resetCounts() { - flush(); -} - -/** This method returns the number of cache hits */ -public long getCacheHitCount() { - return hitNumber; -} - -/** This method returns the cache weighed size */ -public long getCacheMemoryUsed() { - return cacheObject.weightedSize(); -} - -/** This method returns the number of cache miss */ -public long getCacheMissCount() { - return missNumber; -} - -/** This method returns the number of tile present in the cache */ -public long getCacheTileCount() { - return cacheObject.size(); -} - -/** This method returns the cache concurrency level */ -public int getConcurrencyLevel() { - return concurrencyLevel; -} - -/** Not supported */ -public void setTileCapacity(int arg0) { - throw new UnsupportedOperationException("Deprecated Operation"); - -} - -/** Not supported */ -public int getTileCapacity() { - throw new UnsupportedOperationException("Deprecated Operation"); -} - -/** Not supported */ -public Comparator getTileComparator() { - throw new UnsupportedOperationException("Comparator not supported"); -} - -/** Not supported */ -public void setTileComparator(Comparator arg0) { - throw new UnsupportedOperationException("Comparator not supported"); -} - -/** Not supported */ -public void memoryControl() { - throw new UnsupportedOperationException("Memory Control not supported"); -} - -} diff --git a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentNonBlockingCache.java b/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentNonBlockingCache.java deleted file mode 100644 index 97a3d705..00000000 --- a/concurrent-tile-cache/src/main/java/it/geosolutions/concurrentlinked/ConcurrentNonBlockingCache.java +++ /dev/null @@ -1,411 +0,0 @@ -package it.geosolutions.concurrentlinked; - -import it.geosolutions.concurrent.CachedTileImpl; -import it.geosolutions.concurrent.ConcurrentTileCache.Actions; - -import java.awt.Point; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.Observable; -import javax.media.jai.TileCache; -import org.cliffc.high_scale_lib.NonBlockingHashMap; -import com.sun.media.jai.util.CacheDiagnostics; - -public class ConcurrentNonBlockingCache extends Observable implements - TileCache, CacheDiagnostics { - -/** Default value for memory threshold */ -public static final float DEFAULT_MEMORY_THRESHOLD = 0.75F; - -/** Default value for cache memory */ -public static final long DEFAULT_MEMORY_CACHE = 16L * 1024L * 1024L; - -/** Default boolean for diagnostic mode */ -public static final boolean DEFAULT_DIAGNOSTIC = false; - -/** Cache memory capacity */ -private long memoryCacheCapacity = DEFAULT_MEMORY_CACHE; - -/** Cache memory threshold (typically 0.75F) */ -private float memoryCacheThreshold = DEFAULT_MEMORY_THRESHOLD; - -/** The real cache */ -private NonBlockingHashMap cacheObject = new NonBlockingHashMap(1000); - -/** - * Current diagnostic mode (enabled or not). This variable is set to volatile - * for visibility - */ -private volatile boolean diagnosticEnabled = DEFAULT_DIAGNOSTIC; - -/** Current number of cache miss */ -private int missNumber; - -/** Current number of cache hits */ -private int hitNumber; - -/* Simple constructor */ -public ConcurrentNonBlockingCache() { - this(DEFAULT_MEMORY_CACHE, DEFAULT_MEMORY_THRESHOLD, DEFAULT_DIAGNOSTIC); -} - -/* Parameterized constructor */ -public ConcurrentNonBlockingCache(long memoryCacheCapacity, - float memoryCacheThreshold, boolean diagnosticEnabled) { - this.memoryCacheCapacity = memoryCacheCapacity; - this.diagnosticEnabled = diagnosticEnabled; - this.memoryCacheThreshold = memoryCacheThreshold; - /* the counters are set to 0 */ - hitNumber = 0; - missNumber = 0; - System.err.println("Using ConcurrentNonBlockingCache"); -} - -public void add(RenderedImage image, int xTile, int yTile, Raster dataTile) { - this.add(image, xTile, yTile, dataTile, null); -} - -/** - * This method adds a new tile in cache and, if diagnostic is enabled, notify - * observers - */ -public void add(RenderedImage image, int xTile, int yTile, Raster dataTile, - Object tileMetric) { - /* Tile key calculation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - /* New tile creation */ - CachedTileImpl newValue = new CachedTileImpl(image, xTile, yTile, dataTile, - tileMetric); - /* If diagnostic is enabled the tile status is changed */ - if (diagnosticEnabled) { - synchronized (this) { - /* Updates the new tile status and notifies the observers */ - newValue.setAction(Actions.ADDITION); - setChanged(); - notifyObservers(newValue); - /* Puts the new value in cache and takes the old one */ - CachedTileImpl oldValue = cacheObject.put(key, newValue); - if (oldValue != null) { - hitNumber++; - /* Update the old tile status and notify the observers */ - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.SUBSTITUTION_FROM_ADD); - setChanged(); - notifyObservers(oldValue); - return; - } - } - } else { - /* Simply put the value in cache */ - cacheObject.put(key, newValue); - } -} - -/** This method add an array of tiles at given positions */ -public void addTiles(RenderedImage image, Point[] positions, - Raster[] dataTiles, Object tileMetric) { - for (int i = 0; i < positions.length; i++) { - int xIndex = positions[i].x; - int yIndex = positions[i].y; - add(image, xIndex, yIndex, dataTiles[i], tileMetric); - } -} - -/** - * This method flushes the cache and, if diagnostic is enabled, reset the - * counters and, for every tile, update tile status - */ -public synchronized void flush() { - if (diagnosticEnabled) { - /* - * An iterator of all the key in the cache is used for updating every - * tile and the removing it - */ - CachedTileImpl oldValue; - Iterator iter = cacheObject.keySet().iterator(); - while (iter.hasNext()) { - oldValue = cacheObject.remove(iter.next()); - oldValue.setAction(Actions.REMOVAL_FROM_FLUSH); - oldValue.updateTileTimeStamp(); - setChanged(); - notifyObservers(oldValue); - } - /* Counter reset */ - hitNumber = 0; - missNumber = 0; - } else { - /* Simple cache clearing */ - cacheObject.clear(); - } -} - -/** This method gets the current cache memory capacity */ -public long getMemoryCapacity() { - return memoryCacheCapacity; -} - -/** This method gets the current cache memory threshold */ -public float getMemoryThreshold() { - return memoryCacheThreshold; -} - -/** - * This method gets a tile raster from his (x,y) tile coordinates and the image - * reference - */ -public Raster getTile(RenderedImage image, int xTile, int yTile) { - /* Key creation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - if (diagnosticEnabled) { - synchronized (this) { - /* - * In diagnostic mode the oldvalue, if present, is updated and - * retrieved - */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - /* if the tile is present the hit number is increased */ - hitNumber++; - oldValue.setAction(Actions.UPDATING_TILE_FROM_GETTILE); - oldValue.updateTileTimeStamp(); - /* Observers notifications */ - setChanged(); - notifyObservers(oldValue); - return oldValue.getTile(); - } else { - /* if the tile is not present the miss number is increased */ - missNumber++; - return null; - } - } - } else { - /* The tile is returned if present, else null returned */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - return oldValue.getTile(); - } else { - return null; - } - } -} - -/** All the tile of the specific image are returned */ -public Raster[] getTiles(RenderedImage image) { - // instantiation of the result array - Raster[] tilesData = null; - // total number of tiles present in the cache - int tileCount = (int) cacheObject.size(); - - int size = Math.min(image.getNumXTiles() * image.getNumYTiles(), tileCount); - - if (size > 0) { - int minTx = image.getMinTileX(); - int minTy = image.getMinTileY(); - int maxTx = minTx + image.getNumXTiles(); - int maxTy = minTy + image.getNumYTiles(); - - // creates a temporary data arrayList - ArrayList tempData = new ArrayList(); - // every not-null raster is added to the array - for (int y = minTy; y < maxTy; y++) { - for (int x = minTx; x < maxTx; x++) { - - Raster rasterTile = getTile(image, x, y); - - // the arrayList is then changed in an array - if (rasterTile != null) { - tempData.add(rasterTile); - } - } - } - - int tmpsize = tempData.size(); - if (tmpsize > 0) { - tilesData = (Raster[]) tempData.toArray(new Raster[tmpsize]); - } - } - - return tilesData; -} - -/** This method returns an array of tiles at the given positions */ -public Raster[] getTiles(RenderedImage image, Point[] positions) { - /* Initialization of an array of rasters */ - Raster[] tileData = new Raster[positions.length]; - if (diagnosticEnabled) { - synchronized (this) { - /* - * If the diagnostic mode is enabled, the tiles are returned from - * the method getTile which updates the tile status - */ - for (int j = 0; j < positions.length; j++) { - int xTile = positions[j].x; - int yTile = positions[j].y; - tileData[j] = getTile(image, xTile, yTile); - } - } - } else { - /* - * Else, they are simply returned by the NonBlockingHashMap.get() - * method - */ - for (int j = 0; j < positions.length; j++) { - int xTile = positions[j].x; - int yTile = positions[j].y; - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - tileData[j] = cacheObject.get(key).getTile(); - } - } - return tileData; -} - -/** This method removes the specified tile and notify it to the observers */ -public void remove(RenderedImage image, int xTile, int yTile) { - /* Tile key calculation */ - Object key = CachedTileImpl.hashKey(image, xTile, yTile); - if (diagnosticEnabled) { - synchronized (this) { - /* - * In diagnostic mode this method check if the old tile was present - * and if so update its status and notify it to the observers, and - * removes it - */ - CachedTileImpl oldValue = cacheObject.get(key); - if (oldValue != null) { - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.ABOUT_TO_REMOVAL); - setChanged(); - notifyObservers(oldValue); - cacheObject.remove(key); - oldValue.updateTileTimeStamp(); - oldValue.setAction(Actions.MANUAL_REMOVAL); - setChanged(); - notifyObservers(oldValue); - } - } - } else { - /* The tile is removed without checking if it is present or not */ - cacheObject.remove(key); - } -} - -/** This method removes all the tiles that belong to the specified image */ -public void removeTiles(RenderedImage image) { - /* Image tile coordinates */ - int minTx = image.getMinTileX(); - int minTy = image.getMinTileY(); - int maxTx = minTx + image.getNumXTiles(); - int maxTy = minTy + image.getNumYTiles(); - if (diagnosticEnabled) { - synchronized (this) { - /* - * This method is the same for both the diagnostic or non-diagnostic - * mode the difference is the sincronized block - */ - removeAllImageTiles(image, minTx, maxTx, minTy, maxTy); - } - } else { - removeAllImageTiles(image, minTx, maxTx, minTy, maxTy); - } -} - -/** This method cycles through the image eliminating all of its tiles */ -private void removeAllImageTiles(RenderedImage image, int minX, int maxX, - int minY, int maxY) { - for (int y = minY; y < maxY; y++) { - for (int x = minX; x < maxX; x++) { - remove(image, x, y); - } - } -} - -/** This method sets the memory capacity, then flush and rebuild the cache */ -public synchronized void setMemoryCapacity(long memoryCacheCapacity) { - if (memoryCacheCapacity < 0) { - throw new IllegalArgumentException("Memory capacity too small"); - } else { - this.memoryCacheCapacity = memoryCacheCapacity; - flush(); - } - -} - -/** This method sets the memory threshold, then flush and rebuild the cache */ -public synchronized void setMemoryThreshold(float memoryCacheThreshold) { - if (memoryCacheThreshold < 0 || memoryCacheThreshold > 1) { - throw new IllegalArgumentException( - "Memory threshold must be between 0 and 1"); - } else { - this.memoryCacheThreshold = memoryCacheThreshold; - flush(); - } - -} - -/** Diagnostic is disabled, then the cache is flushed and rebuilt */ -public synchronized void disableDiagnostics() { - this.diagnosticEnabled = false; - flush(); -} - -/** Diagnostic is enabled, then the cache is flushed and rebuilt */ -public synchronized void enableDiagnostics() { - this.diagnosticEnabled = true; - flush(); -} - -/** The counters are set to 0 when the cache is flushed */ -public synchronized void resetCounts() { - flush(); -} - -/** This method returns the number of cache hits */ -public long getCacheHitCount() { - return hitNumber; -} - -/** This method returns the cache weighed size */ -public long getCacheMemoryUsed() { - return -1; -} - -/** This method returns the number of cache miss */ -public long getCacheMissCount() { - return missNumber; -} - -/** This method returns the number of tile present in the cache */ -public long getCacheTileCount() { - return cacheObject.size(); -} - -/** Not supported */ -public void setTileCapacity(int arg0) { - throw new UnsupportedOperationException("Deprecated Operation"); - -} - -/** Not supported */ -public int getTileCapacity() { - throw new UnsupportedOperationException("Deprecated Operation"); -} - -/** Not supported */ -public Comparator getTileComparator() { - throw new UnsupportedOperationException("Comparator not supported"); -} - -/** Not supported */ -public void setTileComparator(Comparator arg0) { - throw new UnsupportedOperationException("Comparator not supported"); -} - -/** Not supported */ -public void memoryControl() { - throw new UnsupportedOperationException("Memory Control not supported"); -} - -} diff --git a/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/CacheCoberturaTest.java b/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/CacheCoberturaTest.java index e4ef232a..b2e232f2 100644 --- a/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/CacheCoberturaTest.java +++ b/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/CacheCoberturaTest.java @@ -15,7 +15,7 @@ public class CacheCoberturaTest { @Test public void testConcurrency() throws Exception { // setting the parameters - int cacheUsed=4; + int cacheUsed=1; int concurrencyLevel = 16; long memoryCacheCapacity = 128 * 1024 * 1024; RenderedImage imageSynth = ConcurrentCacheTest.getSynthetic(1); diff --git a/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/ConcurrentCacheTest.java b/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/ConcurrentCacheTest.java index 161e1445..54dbc662 100644 --- a/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/ConcurrentCacheTest.java +++ b/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/ConcurrentCacheTest.java @@ -1,9 +1,7 @@ package it.geosolutions.concurrencytest; import it.geosolutions.concurrent.ConcurrentTileCache; -import it.geosolutions.concurrentlinked.ConcurrentCache; -import it.geosolutions.concurrentlinked.ConcurrentLinkedCache; -import it.geosolutions.concurrentlinked.ConcurrentNonBlockingCache; + import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.io.File; @@ -16,6 +14,7 @@ import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.Logger; + import javax.imageio.stream.FileImageInputStream; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; @@ -133,29 +132,6 @@ public double[] testwriteImageAndWatchFlag(int cacheUsed, int concurrencyLevel, } JAI.getDefaultInstance().setTileCache(cTileCache); break; - case 2: - ConcurrentLinkedCache cLinkedCache = new ConcurrentLinkedCache(); - cLinkedCache.setConcurrencyLevel(concurrencyLevel); - if (diagnostics) { - cLinkedCache.enableDiagnostics(); - } - JAI.getDefaultInstance().setTileCache(cLinkedCache); - break; - case 3: - ConcurrentCache cCache = new ConcurrentCache(); - cCache.setConcurrencyLevel(concurrencyLevel); - if (diagnostics) { - cCache.enableDiagnostics(); - } - JAI.getDefaultInstance().setTileCache(cCache); - break; - case 4: - ConcurrentNonBlockingCache cNonBlockingCache = new ConcurrentNonBlockingCache(); - if (diagnostics) { - cNonBlockingCache.enableDiagnostics(); - } - JAI.getDefaultInstance().setTileCache(cNonBlockingCache); - break; } JAI.getDefaultInstance().getTileCache().setMemoryCapacity(memoryCacheCapacity); JAI.getDefaultInstance().getTileScheduler().setParallelism(10); @@ -436,18 +412,6 @@ public void run() { ConcurrentTileCache cTileCache = (ConcurrentTileCache) cache; memory = cTileCache.getCacheMemoryUsed(); break; - case 2: - ConcurrentLinkedCache cLinkedCache = (ConcurrentLinkedCache) cache; - memory = cLinkedCache.getCacheMemoryUsed(); - break; - case 3: - ConcurrentCache cCache = (ConcurrentCache) cache; - memory = cCache.getCacheMemoryUsed(); - break; - case 4: - ConcurrentNonBlockingCache cNonBlockingCache = (ConcurrentNonBlockingCache) cache; - memory = cNonBlockingCache.getCacheMemoryUsed(); - break; } LOGGER.log(Level.INFO, "Current Memory Used: " + memory / (1024) + " Kb"); diff --git a/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/ConcurrentMultiMapTest.java b/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/ConcurrentMultiMapTest.java new file mode 100644 index 00000000..cd39b3af --- /dev/null +++ b/concurrent-tile-cache/src/test/java/it/geosolutions/concurrencytest/ConcurrentMultiMapTest.java @@ -0,0 +1,155 @@ +package it.geosolutions.concurrencytest; + +import it.geosolutions.concurrent.ConcurrentTileCacheMultiMap; + +import java.awt.image.Raster; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.imageio.stream.FileImageInputStream; +import javax.imageio.stream.ImageInputStream; +import javax.media.jai.RenderedOp; + +import junit.framework.Assert; + +import org.geotools.TestData; +import org.junit.Test; + +import com.sun.media.jai.operator.ImageReadDescriptor; + +/** + * This test class is used for checking if the {@link ConcurrentTileCacheMultiMap} behaves correctly. + * + * @author Nicola Lagomarsini GeoSolutions S.A.S. + * + */ +public class ConcurrentMultiMapTest { + + /** Total number of requests to execute */ + private final static int TOTAL = 100; + + @Test + public void testAddAndGetTile() throws InterruptedException, FileNotFoundException, IOException { + // Input stream to use + ImageInputStream stream_in = null; + try { + stream_in = new FileImageInputStream(TestData.file(this, "world.tiff")); + // Input RenderedImage to use + final RenderedOp input = ImageReadDescriptor.create(stream_in, 0, false, false, false, + null, null, null, null, null); + + // Boolean used for checking if the conditions are passed + final AtomicBoolean passed = new AtomicBoolean(true); + // Cache creation + final ConcurrentTileCacheMultiMap cache = new ConcurrentTileCacheMultiMap(1000 * 1000, + false, 1f, 4); + // Selection of one tile from the image + Raster data = input.getTile(input.getMinTileX(), input.getMinTileY()); + // Setting the tile inside the cache + cache.add(input, input.getMinTileX(), input.getMinTileY(), data); + // Thread pool to use for doing concurrent access on the cache + ThreadPoolExecutor executor = new ThreadPoolExecutor(TOTAL, TOTAL, 60, TimeUnit.SECONDS, + new ArrayBlockingQueue(1000000)); + // Latch used for waiting all the threads to end their work + final CountDownLatch latch = new CountDownLatch(TOTAL); + // Cycle for launching various requests + int counter = TOTAL; + while (counter > 0) { + + executor.execute(new Runnable() { + + public void run() { + // Get the tile to use + Raster data = cache.getTile(input, input.getMinTileX(), input.getMinTileY()); + if (data == null) { + passed.getAndSet(false); + } + latch.countDown(); + + } + }); + // Counter update + counter--; + } + // Waiting all threads to finish + latch.await(); + // Ensure that all the threads have found the tile + Assert.assertTrue(passed.get()); + } finally { + try { + if (stream_in != null) { + stream_in.flush(); + stream_in.close(); + } + } catch (Throwable t) { + // + } + } + } + + @Test + public void testRemoveTile() throws InterruptedException, FileNotFoundException, IOException { + // Input stream to use + ImageInputStream stream_in = null; + try { + stream_in = new FileImageInputStream(TestData.file(this, "world.tiff")); + // Input RenderedImage to use + final RenderedOp input = ImageReadDescriptor.create(stream_in, 0, false, false, false, + null, null, null, null, null); + + // Boolean used for checking if the conditions are passed + final AtomicBoolean passed = new AtomicBoolean(true); + // Cache creation + final ConcurrentTileCacheMultiMap cache = new ConcurrentTileCacheMultiMap(1000 * 1000, + false, 1f, 4); + // Selection of one tile from the image + Raster data = input.getTile(input.getMinTileX(), input.getMinTileY()); + // Setting the tile inside the cache + cache.add(input, input.getMinTileX(), input.getMinTileY(), data); + // Removing tile + cache.remove(input, input.getMinTileX(), input.getMinTileY()); + // Thread pool to use for doing concurrent access on the cache + ThreadPoolExecutor executor = new ThreadPoolExecutor(TOTAL, TOTAL, 60, TimeUnit.SECONDS, + new ArrayBlockingQueue(1000000)); + // Latch used for waiting all the threads to end their work + final CountDownLatch latch = new CountDownLatch(TOTAL); + // Cycle for launching various requests + int counter = TOTAL; + while (counter > 0) { + + executor.execute(new Runnable() { + + public void run() { + // Get the tile to use + Raster data = cache.getTile(input, input.getMinTileX(), input.getMinTileY()); + if (data != null) { + passed.getAndSet(false); + } + latch.countDown(); + + } + }); + // Counter update + counter--; + } + // Waiting all threads to finish + latch.await(); + // Ensure that all the threads have found the tile + Assert.assertTrue(passed.get()); + } finally { + try { + if (stream_in != null) { + stream_in.flush(); + stream_in.close(); + } + } catch (Throwable t) { + // + } + } + } +} diff --git a/concurrent-tile-cache/src/test/resources/it/geosolutions/concurrencytest/test-data/world.tiff b/concurrent-tile-cache/src/test/resources/it/geosolutions/concurrencytest/test-data/world.tiff new file mode 100644 index 0000000000000000000000000000000000000000..7ea9c6f7131d165a584ab2d37d1ab616ef7ecf97 GIT binary patch literal 52550 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 Z>HjSX`+q0s`hWT>DZ1ykgE}!y{ts7!R9FB2 literal 0 HcmV?d00001 diff --git a/jt-utilities/src/test/java/it/geosolutions/jaiext/range/RangeTest.java b/jt-utilities/src/test/java/it/geosolutions/jaiext/range/RangeTest.java index bb18a72c..e68e3c71 100644 --- a/jt-utilities/src/test/java/it/geosolutions/jaiext/range/RangeTest.java +++ b/jt-utilities/src/test/java/it/geosolutions/jaiext/range/RangeTest.java @@ -14,6 +14,8 @@ /** * This test-class is used for evaluating the functionalities of the {@link Range} class and its subclasses. Also this class is compared to other * Range classes for seeing if its contain() method could have a better performance than that of the other Range classes. + * + * Guava Ranges are commented in order to wait to upgrade the Guava version to 14.0.1 */ public class RangeTest { @@ -187,25 +189,25 @@ public class RangeTest { private static NumberRange rangeGeoToolsDpoint; - private static com.google.common.collect.Range rangeGuavaB; - - private static com.google.common.collect.Range rangeGuavaS; - - private static com.google.common.collect.Range rangeGuavaI; - - private static com.google.common.collect.Range rangeGuavaF; - - private static com.google.common.collect.Range rangeGuavaD; - - private static com.google.common.collect.Range rangeGuavaBpoint; - - private static com.google.common.collect.Range rangeGuavaSpoint; - - private static com.google.common.collect.Range rangeGuavaIpoint; - - private static com.google.common.collect.Range rangeGuavaFpoint; - - private static com.google.common.collect.Range rangeGuavaDpoint; +// private static com.google.common.collect.Range rangeGuavaB; +// +// private static com.google.common.collect.Range rangeGuavaS; +// +// private static com.google.common.collect.Range rangeGuavaI; +// +// private static com.google.common.collect.Range rangeGuavaF; +// +// private static com.google.common.collect.Range rangeGuavaD; +// +// private static com.google.common.collect.Range rangeGuavaBpoint; +// +// private static com.google.common.collect.Range rangeGuavaSpoint; +// +// private static com.google.common.collect.Range rangeGuavaIpoint; +// +// private static com.google.common.collect.Range rangeGuavaFpoint; +// +// private static com.google.common.collect.Range rangeGuavaDpoint; @BeforeClass public static void initialSetup() { @@ -305,18 +307,18 @@ public static void initialSetup() { rangeGeoToolsFpoint = new org.geotools.util.NumberRange(Float.class, 5f, 5f); rangeGeoToolsDpoint = new org.geotools.util.NumberRange(Double.class, 5d, 5d); - // Guava Ranges - rangeGuavaB = com.google.common.collect.Range.closed((byte) 1, (byte) 60); - rangeGuavaS = com.google.common.collect.Range.closed((short) 1, (short) 60); - rangeGuavaI = com.google.common.collect.Range.closed(1, 60); - rangeGuavaF = com.google.common.collect.Range.closed(0.5f, 60.5f); - rangeGuavaD = com.google.common.collect.Range.closed(1.5d, 60.5d); - // 1 point Ranges - rangeGuavaBpoint = com.google.common.collect.Range.singleton((byte) 5); - rangeGuavaSpoint = com.google.common.collect.Range.singleton((short) 5); - rangeGuavaIpoint = com.google.common.collect.Range.singleton(5); - rangeGuavaFpoint = com.google.common.collect.Range.singleton(5f); - rangeGuavaDpoint = com.google.common.collect.Range.singleton(5d); +// // Guava Ranges +// rangeGuavaB = com.google.common.collect.Range.closed((byte) 1, (byte) 60); +// rangeGuavaS = com.google.common.collect.Range.closed((short) 1, (short) 60); +// rangeGuavaI = com.google.common.collect.Range.closed(1, 60); +// rangeGuavaF = com.google.common.collect.Range.closed(0.5f, 60.5f); +// rangeGuavaD = com.google.common.collect.Range.closed(1.5d, 60.5d); +// // 1 point Ranges +// rangeGuavaBpoint = com.google.common.collect.Range.singleton((byte) 5); +// rangeGuavaSpoint = com.google.common.collect.Range.singleton((short) 5); +// rangeGuavaIpoint = com.google.common.collect.Range.singleton(5); +// rangeGuavaFpoint = com.google.common.collect.Range.singleton(5f); +// rangeGuavaDpoint = com.google.common.collect.Range.singleton(5d); } @Test @@ -612,51 +614,51 @@ public void testGeoToolsRangeTimeByte1or2Points() { } } - @Test - public void testGuavaRangeTimeByte1or2Points() { - - if (!SINGLE_POINT) { - switch(TEST_SELECTOR){ - case DataBuffer.TYPE_BYTE: - testGuavaRangeTime(rangeGuavaB, SINGLE_POINT,arrayBtest); - break; - case DataBuffer.TYPE_SHORT: - testGuavaRangeTime(rangeGuavaS, SINGLE_POINT,arrayStest); - break; - case DataBuffer.TYPE_INT: - testGuavaRangeTime(rangeGuavaI, SINGLE_POINT,arrayItest); - break; - case DataBuffer.TYPE_FLOAT: - testGuavaRangeTime(rangeGuavaF, SINGLE_POINT,arrayFtest); - break; - case DataBuffer.TYPE_DOUBLE: - testGuavaRangeTime(rangeGuavaD, SINGLE_POINT,arrayDtest); - break; - default: - throw new IllegalArgumentException("Wrong data type"); - } - } else { - switch(TEST_SELECTOR){ - case DataBuffer.TYPE_BYTE: - testGuavaRangeTime(rangeGuavaBpoint, SINGLE_POINT,arrayBtest); - break; - case DataBuffer.TYPE_SHORT: - testGuavaRangeTime(rangeGuavaSpoint, SINGLE_POINT,arrayStest); - break; - case DataBuffer.TYPE_INT: - testGuavaRangeTime(rangeGuavaIpoint, SINGLE_POINT,arrayItest); - break; - case DataBuffer.TYPE_FLOAT: - testGuavaRangeTime(rangeGuavaFpoint, SINGLE_POINT,arrayFtest); - break; - case DataBuffer.TYPE_DOUBLE: - testGuavaRangeTime(rangeGuavaDpoint, SINGLE_POINT,arrayDtest); - break; - default: - throw new IllegalArgumentException("Wrong data type"); - } - } - } +// @Test +// public void testGuavaRangeTimeByte1or2Points() { +// +// if (!SINGLE_POINT) { +// switch(TEST_SELECTOR){ +// case DataBuffer.TYPE_BYTE: +// testGuavaRangeTime(rangeGuavaB, SINGLE_POINT,arrayBtest); +// break; +// case DataBuffer.TYPE_SHORT: +// testGuavaRangeTime(rangeGuavaS, SINGLE_POINT,arrayStest); +// break; +// case DataBuffer.TYPE_INT: +// testGuavaRangeTime(rangeGuavaI, SINGLE_POINT,arrayItest); +// break; +// case DataBuffer.TYPE_FLOAT: +// testGuavaRangeTime(rangeGuavaF, SINGLE_POINT,arrayFtest); +// break; +// case DataBuffer.TYPE_DOUBLE: +// testGuavaRangeTime(rangeGuavaD, SINGLE_POINT,arrayDtest); +// break; +// default: +// throw new IllegalArgumentException("Wrong data type"); +// } +// } else { +// switch(TEST_SELECTOR){ +// case DataBuffer.TYPE_BYTE: +// testGuavaRangeTime(rangeGuavaBpoint, SINGLE_POINT,arrayBtest); +// break; +// case DataBuffer.TYPE_SHORT: +// testGuavaRangeTime(rangeGuavaSpoint, SINGLE_POINT,arrayStest); +// break; +// case DataBuffer.TYPE_INT: +// testGuavaRangeTime(rangeGuavaIpoint, SINGLE_POINT,arrayItest); +// break; +// case DataBuffer.TYPE_FLOAT: +// testGuavaRangeTime(rangeGuavaFpoint, SINGLE_POINT,arrayFtest); +// break; +// case DataBuffer.TYPE_DOUBLE: +// testGuavaRangeTime(rangeGuavaDpoint, SINGLE_POINT,arrayDtest); +// break; +// default: +// throw new IllegalArgumentException("Wrong data type"); +// } +// } +// } @@ -964,38 +966,38 @@ public >void testGeoToolsRangeTime(org.geotools + " nsec."); } - public >void testGuavaRangeTime(com.google.common.collect.Range testRange, - boolean isPoint, T[] array) { - int totalCycles = NOT_BENCHMARK_ITERATION + BENCHMARK_ITERATION; - // Initialization of the statistics - long mean = 0; - for (int i = 0; i < totalCycles; i++) { - // Total calculation time - long start = System.nanoTime(); - - for (int j = 0; j < array.length; j++) { - testRange.contains(array[j]); - } - long end = System.nanoTime() - start; - - // If the the first NOT_BENCHMARK_ITERATION cycles has been done, then the mean, maximum and minimum values are stored - if (i > NOT_BENCHMARK_ITERATION - 1) { - if (i == NOT_BENCHMARK_ITERATION) { - mean = end; - } else { - mean = mean + end; - } - } - } - String description = ""; - if (isPoint) { - description += " a single point"; - } - // Mean values - double meanValue = mean / BENCHMARK_ITERATION; - // Output print - System.out.println("\nMean value for" + description + " Guava Range : " + meanValue - + " nsec."); - } +// public >void testGuavaRangeTime(com.google.common.collect.Range testRange, +// boolean isPoint, T[] array) { +// int totalCycles = NOT_BENCHMARK_ITERATION + BENCHMARK_ITERATION; +// // Initialization of the statistics +// long mean = 0; +// for (int i = 0; i < totalCycles; i++) { +// // Total calculation time +// long start = System.nanoTime(); +// +// for (int j = 0; j < array.length; j++) { +// testRange.contains(array[j]); +// } +// long end = System.nanoTime() - start; +// +// // If the the first NOT_BENCHMARK_ITERATION cycles has been done, then the mean, maximum and minimum values are stored +// if (i > NOT_BENCHMARK_ITERATION - 1) { +// if (i == NOT_BENCHMARK_ITERATION) { +// mean = end; +// } else { +// mean = mean + end; +// } +// } +// } +// String description = ""; +// if (isPoint) { +// description += " a single point"; +// } +// // Mean values +// double meanValue = mean / BENCHMARK_ITERATION; +// // Output print +// System.out.println("\nMean value for" + description + " Guava Range : " + meanValue +// + " nsec."); +// } } diff --git a/pom.xml b/pom.xml index 7d36bdc2..c75da939 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 10-SNAPSHOT true 256M - 14.0.1 + 11.0.1