diff --git a/documentation/en/user/source/configuration/storage.rst b/documentation/en/user/source/configuration/storage.rst index 9a5df27af7..1143e38480 100644 --- a/documentation/en/user/source/configuration/storage.rst +++ b/documentation/en/user/source/configuration/storage.rst @@ -255,9 +255,6 @@ a thorough assessment of costs associated to seeding and serving caches**. Yet w the bigger the size difference between two identical caches on S3 vs a regular file system. The S3 cache takes less space because the actual space used for each tile is not padded to a file system block size. For example, the ``topp:states`` layer seeded up to zoom level 10 for EPSG:4326 with png8 format takes roughly 240MB on an Ext4 file system, and about 21MB on S3. -* Use in-memory caching. When serving S3 tiles from GeoWebcache, you can greately reduce the number of GET requests to S3 by configuring an in-memory cache as - described in the "In-Memory caching" section bellow. This will allow for frequently requested tiles to be kept in memory instead of retrieved from S3 on each - call. The following is an example OpenLayers 3 HTML/JavaScript to set up a map that fetches tiles from a pre-seeded geowebcache layer directly from S3. We're using the typical GeoServer ``topp:states`` sample layer on a fictitious ``my-geowebcache-bucket`` bucket, using ``test-cache`` as the cache prefix, png8 tile format, and EPSG:4326 CRS. @@ -636,221 +633,6 @@ The *layer* parameter identifies the layer whose associated blob store content s This are the only valid combinations of this parameters other combinations will ignore some of the provided parameters or will throw an exception. -In-Memory caching ------------------ - -Default **blobstore** can be changed with a new one called **MemoryBlobStore**, which allows in memory tile caching. The **MemoryBlobStore** is a wrapper of a **blobstore** -implementation, which can be the default one(*FileBlobStore*) or another one. For using the new **blobstore** implementation, the user have to -modify the **blobstore** bean associated to the **gwcStorageBroker** bean (inside the Application Context file *geowebcache-core-context.xml*) by setting *gwcMemoryBlobStore* -instead of *gwcBlobStore*. - -The configuration of a MemoryBlobStore requires a *blobstore* to wrap and a **CacheProvider** object. This one provides the caching mechanism for saving input data in memory. -User can define different caching objects but can only inject one of them inside the **MemoryBlobStore**. More information about the **CacheProvider** can be found in the next section. - -An example of MemoryBlobStore configuration can be found beow: - -.. code-block:: xml - - - - - - - - - -.. note:: Note that *cacheProviderName*/*cacheProvider* cannote be used together, if a *cacheProvider* is defined, the *cacheProviderName* is not considered. If *cacheProviderName*/*cacheProvider* are not defined, the **MemoryBlobStore** will internally search for a suitable **CacheProvider**. - -CacheProvider configuration -+++++++++++++++++++++++++++ - -A **CacheProvider** object should be configured with an input object called **CacheConfiguration**. **CacheConfiguration** parameters are: - - * *hardMemoryLimit* : which is the cache size in Mb - * *policy* : which can be LRU, LFU, EXPIRE_AFTER_WRITE, EXPIRE_AFTER_ACCESS, NULL - * *evitionTime* : which is the cache eviction time in seconds - * *concurrencyLevel* : which is the cache concurrency level - -These parameters must be defined as properties in the **cacheConfiguration** bean in the Spring Application Context (like *geowebcache-core-context.xml*). - -At the time of writing there are two implementations of the **CacheProvider** interface: - - * **GuavaCacheProvider** - * **HazelcastCacheProvider** - -GuavaCacheProvider -`````````````````````` -**GuavaCacheProvider** provides local in-memory caching by using a `Guava `_ *Cache* for storing the various GWC Tiles locally on the machine. For configuring a **GuavaCacheProvider** -the user must create a new bean in the Application Context file (like *geowebcache-core-context.xml*) and then add a reference to a **CacheConfiguration** instance. - -Here is an example of configuration: - -.. code-block:: xml - - - - - - - - - - - - - -HazelcastCacheProvider -`````````````````````` -**HazelcastCacheProvider** is useful for implementing distributed in memory caching for clustering. It internally uses `Hazelcast `_ for handling distributed caching. -The **HazelcastCacheProvider** configuration requires another object called **HazelcastLoader**. This object accepts an Hazelcast instance or loads a file called *hazelcast.xml* from a proper directory defined -by the property "hazelcast.config.dir". If none of them is present, the CacheProvider object cannot be used. - -The user must follow these rules for configuring the Hazelcast instance: - - #. The Hazelcast configuration requires a Map object with name *CacheProviderMap* - #. Map eviction policy must be *LRU* or *LFU* - #. Map configuration must have a fixed size defined in Mb - #. Map configuration must have **USED_HEAP_SIZE** as *MaxSizePolicy* - -Here the user can find both examples: - - * From *hazelcast.xml*: - - .. code-block:: xml - - - - cacheCluster - geoserverCache - - - - - 5701 - - - 224.2.2.3 - 54327 - - - 192.168.1.32 - 192.168.1.110 - - - - - LRU - 16 - - - - - And the related application context will be: - - .. code-block:: xml - - - - - - - - - .. note:: Remember that in this case the user must define the *hazelcast.config.dir* property when starting the application. - - * From application context (See Hazelcast documentation for more info): - - .. code-block:: xml - - - - - - - - - 10.10.1.2, 10.10.1.3 - - - - - - - - - - - - - - - -Optional configuration parameters -`````````````````````````````````` -In this section are described other available configuration parameters to configure: - -* Cache expiration time: - - .. code-block:: xml - - - ... - - 0 - 0 - - - - Where *time-to-live-seconds* indicates how many seconds an entry can stay in cache and *max-idle-seconds* indicates how many seconds an entry may be not accessed before being evicted. - -* Near Cache. - - .. code-block:: xml - - - ... - - - 5000 - 0 - 60 - LRU - - - true - - - false - - - - - Near Cache is a local cache for each cluster instance which is used for caching entries in the other cluster instances. This behaviour avoids to request those entries each time by executing a remote call. This feature could be helpful in order to improve Hazelcast Cache performances. - - .. note:: A value of *max-size* bigger or equal to Integer.MAX_VALUE cannot be used in order to avoid an uncontrollable growth of the cache size. - OpenStack Swift (Swift) Blob Store +++++++++++++++++++++++++++++++++++++++++++++ diff --git a/documentation/en/user/source/rest/index.rst b/documentation/en/user/source/rest/index.rst index 09d925217d..778032c80e 100644 --- a/documentation/en/user/source/rest/index.rst +++ b/documentation/en/user/source/rest/index.rst @@ -15,7 +15,6 @@ This section will discuss the GeoWebCache REST API. seed.rst diskquota.rst masstruncate.rst - statistics.rst diff --git a/documentation/en/user/source/rest/statistics.rst b/documentation/en/user/source/rest/statistics.rst deleted file mode 100644 index 24e9d20599..0000000000 --- a/documentation/en/user/source/rest/statistics.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. _rest.statistics: - -In Memory Cache Statistics -========================== - -The REST API allows you to get the in memory cache statistics if the **blobstore** used is an instance of **MemoryBlobStore**. - -Operations ----------- - -``/statistics`` - -.. list-table:: - :header-rows: 1 - - * - Method - - Action - - Return Code - - Formats - * - GET - - Return a representation of the statistics - - 200 - - XML, JSON - * - POST - - - - 405 - - - * - PUT - - - - 405 - - - * - DELETE - - - - 405 - - - -Available Requests -+++++++++++++++++++ - -Request in XML: - -.. code-block:: xml - - curl -v -u geowebcache:secured -XGET "http://localhost:8080/geowebcache/rest/statistics.xml" - -Sample response: - -.. code-block:: xml - - - 0 - 0 - 0 - 0 - 100.0 - 0.0 - 0.0 - 67108864 - 0 - - -Request in JSON: - -.. code-block:: xml - - curl -v -u geowebcache:secured -XGET "http://localhost:8080/geowebcache/rest/statistics.json" - -Sample response: - -.. code-block:: xml - - {"gwcInMemoryCacheStatistics":{"missRate":0,"totalCount":0,"missCount":0,"hitCount":0,"actualSize":0,"evictionCount":0,"hitRate":100,"totalSize":67108864,"currentMemoryOccupation":0}} \ No newline at end of file diff --git a/documentation/en/user/source/webinterface/status.rst b/documentation/en/user/source/webinterface/status.rst index becbce5410..3bfdcb9765 100644 --- a/documentation/en/user/source/webinterface/status.rst +++ b/documentation/en/user/source/webinterface/status.rst @@ -30,8 +30,3 @@ Runtime statistics The Status page displays basic runtime statistics including: uptime; how many requests have been made; total and peak throughput and statitics over intervals of 3, 15, and 60 seconds. -In Memory Cache statistics --------------------------- - -If the **blobstore** object used is an instance of **MemoryBlobStore**, the user can find a new section called *In Memory Cache statistics*, containing the statistics of the cache used -by the **blobstore**. Note that these statistics are related to the local entries, even in case of distributed in-memory caching. diff --git a/geowebcache/core/src/main/java/org/geowebcache/GeoWebCacheDispatcher.java b/geowebcache/core/src/main/java/org/geowebcache/GeoWebCacheDispatcher.java index 4e765fc49e..144b353949 100644 --- a/geowebcache/core/src/main/java/org/geowebcache/GeoWebCacheDispatcher.java +++ b/geowebcache/core/src/main/java/org/geowebcache/GeoWebCacheDispatcher.java @@ -61,8 +61,6 @@ import org.geowebcache.storage.DefaultStorageBroker; import org.geowebcache.storage.DefaultStorageFinder; import org.geowebcache.storage.StorageBroker; -import org.geowebcache.storage.blobstore.memory.CacheStatistics; -import org.geowebcache.storage.blobstore.memory.MemoryBlobStore; import org.geowebcache.util.ResponseUtils; import org.geowebcache.util.ServletUtils; import org.springframework.http.MediaType; @@ -499,9 +497,6 @@ private void handleFrontPage(HttpServletRequest request, HttpServletResponse res if (!Boolean.parseBoolean(GeoWebCacheExtensions.getProperty("GEOWEBCACHE_HIDE_STORAGE_LOCATIONS"))) { appendStorageLocations(str); } - if (storageBroker != null) { - appendInternalCacheStats(str); - } } str.append("\n"); @@ -566,120 +561,6 @@ private void appendStorageLocations(StringBuilder str) { str.append("\n"); } - /** - * This method appends the cache statistics to the GWC homepage if the blobstore used is an instance of the - * {@link MemoryBlobStore} class - * - * @param strGlobal Input {@link StringBuilder} containing the HTML for the GWC homepage - */ - private void appendInternalCacheStats(StringBuilder strGlobal) { - - if (storageBroker == null) { - return; - } - - // Searches for the BlobStore inside the StorageBroker - BlobStore blobStore = null; - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Searching for the blobstore used"); - } - // Getting the BlobStore if present - if (storageBroker instanceof DefaultStorageBroker broker) { - blobStore = broker.getBlobStore(); - } - - // If it is not present, or it is not a memory blobstore, nothing is done - if (blobStore == null || !(blobStore instanceof MemoryBlobStore)) { - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("No Memory BlobStore found"); - } - return; - } - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Memory BlobStore found"); - } - - // Get the MemoryBlobStore if present - MemoryBlobStore store = (MemoryBlobStore) blobStore; - - if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Getting statistics"); - } - // Get the statistics - CacheStatistics statistics = store.getCacheStatistics(); - - // No Statistics found - if (statistics == null) { - return; - } - - // Get the statistics values - long hitCount = statistics.getHitCount(); - long missCount = statistics.getMissCount(); - long evictionCount = statistics.getEvictionCount(); - long requestCount = statistics.getRequestCount(); - double hitRate = statistics.getHitRate(); - double missRate = statistics.getMissRate(); - double currentMemory = statistics.getCurrentMemoryOccupation(); - long byteToMb = 1024 * 1024; - double actualSize = ((long) (100 * (statistics.getActualSize() * 1.0d) / byteToMb)) / 100d; - double totalSize = ((long) (100 * (statistics.getTotalSize() * 1.0d) / byteToMb)) / 100d; - - // Append the HTML with the statistics to a new StringBuilder - StringBuilder str = new StringBuilder(); - - str.append("

In Memory Cache Statistics

\n"); - - str.append(""); - - str.append("\n"); - - str.append(""); - - str.append("\n"); - - str.append("\n"); - - str.append("\n"); - - str.append("\n"); - - str.append(""); - - str.append("\n"); - - str.append(""); - - str.append("\n"); - - str.append(""); - - str.append("\n"); - - str.append(""); - - str.append("
Total number of requests:" - + (requestCount >= 0 ? requestCount + "" : "Unavailable")); - str.append("
Internal Cache hit count:"); - str.append(hitCount >= 0 ? hitCount + "" : "Unavailable"); - str.append("
Internal Cache miss count:"); - str.append(missCount >= 0 ? missCount + "" : "Unavailable"); - str.append("
Internal Cache hit ratio:"); - str.append(hitRate >= 0 ? hitRate + " %" : "Unavailable"); - str.append("
Internal Cache miss ratio:"); - str.append(missRate >= 0 ? missRate + " %" : "Unavailable"); - str.append("
Total number of evicted tiles:" - + (evictionCount >= 0 ? evictionCount + "" : "Unavailable")); - str.append("
Cache Memory occupation:" - + (currentMemory >= 0 ? currentMemory + " %" : "Unavailable")); - str.append("
Cache Actual Size/ Total Size :" - + (totalSize >= 0 && actualSize >= 0 ? actualSize + " / " + totalSize + " Mb" : "Unavailable")); - str.append("
\n"); - - // Append to the homepage HTML - strGlobal.append(str); - } - protected SecurityDispatcher getSecurityDispatcher() { return securityDispatcher; } diff --git a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheConfiguration.java b/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheConfiguration.java deleted file mode 100644 index 8b3bf664c7..0000000000 --- a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheConfiguration.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - *

Copyright 2019 - */ -package org.geowebcache.storage.blobstore.memory; - -import java.io.Serial; -import java.io.Serializable; -import java.util.Objects; - -/** - * This class contains the configuration for the {@link CacheProvider} object to use. - * - * @author Nicola Lagomarsini Geosolutions - */ -public class CacheConfiguration implements Serializable { - - /** serialVersionUID */ - @Serial - private static final long serialVersionUID = 3875121032331372267L; - - /** Evction policy */ - public enum EvictionPolicy { - LRU, - LFU, - EXPIRE_AFTER_WRITE, - EXPIRE_AFTER_ACCESS, - NULL; - - public static EvictionPolicy getEvictionPolicy(String policy) { - if (policy == null || policy.isEmpty()) { - return NULL; - } - return valueOf(policy); - } - } - - /** Default value for the Cache memory limit */ - public static final long DEFAULT_MEMORY_LIMIT = 16; // 16Mb - - /** Default value for the cache concurrency level */ - public static final int DEFAULT_CONCURRENCY_LEVEL = 4; - - /** Default value for the eviction time (s) */ - public static final long DEFAULT_EVICTION_TIME = 2 * 60; // 2 minutes - - /** Default value for the eviction policy */ - public static final EvictionPolicy DEFAULT_EVICTION_POLICY = EvictionPolicy.NULL; - - /** Parameter associated to the Cache memory limit */ - private long hardMemoryLimit = DEFAULT_MEMORY_LIMIT; - - /** Cache eviction policy */ - private EvictionPolicy policy = DEFAULT_EVICTION_POLICY; - - /** Parameter associated to the Cache concurrency level */ - private int concurrencyLevel = DEFAULT_CONCURRENCY_LEVEL; - - /** Start Value */ - private long evictionTime = DEFAULT_EVICTION_TIME; - - /** @return the current cache memory limit */ - public long getHardMemoryLimit() { - return hardMemoryLimit; - } - - /** Sets the cache memory limit */ - public void setHardMemoryLimit(long hardMemoryLimit) { - this.hardMemoryLimit = hardMemoryLimit; - } - - /** @return The cache eviction policy */ - public EvictionPolicy getPolicy() { - return policy; - } - - /** Sets the Cache eviction policy */ - public void setPolicy(EvictionPolicy policy) { - this.policy = policy; - } - - /** @return the cache concurrency level */ - public int getConcurrencyLevel() { - return concurrencyLevel; - } - - /** Sets the cache concurrency level */ - public void setConcurrencyLevel(int concurrencyLevel) { - this.concurrencyLevel = concurrencyLevel; - } - - /** @return the cache eviction time */ - public long getEvictionTime() { - return evictionTime; - } - - /** Sets the cache eviction time */ - public void setEvictionTime(long evictionTime) { - this.evictionTime = evictionTime; - } - - @Override - public boolean equals(Object obj) { - // Ensure that the internal objects are equals - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof CacheConfiguration)) { - return false; - } - CacheConfiguration config = (CacheConfiguration) obj; - if (this.concurrencyLevel != config.concurrencyLevel) { - return false; - } else if (!(this.policy == config.policy)) { - return false; - } else if (this.hardMemoryLimit != config.hardMemoryLimit) { - return false; - } else if (this.evictionTime != config.evictionTime) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - return Objects.hash(hardMemoryLimit, policy, concurrencyLevel, evictionTime); - } -} diff --git a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheProvider.java b/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheProvider.java deleted file mode 100644 index 8bca9fc083..0000000000 --- a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheProvider.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - *

Copyright 2019 - */ -package org.geowebcache.storage.blobstore.memory; - -import java.util.List; -import org.geowebcache.storage.TileObject; -import org.geowebcache.storage.blobstore.memory.CacheConfiguration.EvictionPolicy; - -/** - * Interface providing access to a cache object. It must be used by the {@link MemoryBlobStore} class for caching - * {@link TileObject} instances. This cache should be configured only with the setConfiguration method and should be - * modified by calling in sequence: resetCache() and setConfiguration(). Users should be able to add and remove Layers - * that should not be cached. - * - * @author Nicola Lagomarsini GeoSolutions - */ -public interface CacheProvider { - - /** Returns the {@link TileObject} for the selected id */ - public TileObject getTileObj(TileObject obj); - - /** Insert a {@link TileObject} in cache. */ - public void putTileObj(TileObject obj); - - /** Removes a {@link TileObject} from cache. */ - public void removeTileObj(TileObject obj); - - /** Removes all the {@link TileObject}s for the related layer from cache. */ - public void removeLayer(String layername); - - /** Removes all the cached {@link TileObject}s */ - public void clear(); - - /** Resets the Cache status and requires to reconfigure it by calling setConfiguration(), */ - public void reset(); - - /** Returns a {@link CacheStatistics} object containing the current cache statistics. */ - public CacheStatistics getStatistics(); - - /** Sets the CacheConfiguration to use */ - void configure(CacheConfiguration configuration); - - /** Add a new Layer that should not be cached */ - public void addUncachedLayer(String layername); - - /** Remove a Layer so that it can be cached again */ - public void removeUncachedLayer(String layername); - - /** - * Checks if the Layer must be cached or not - * - * @return true if the Layer should not be cached - */ - public boolean containsUncachedLayer(String layername); - - /** - * Returns a list of the supported {@link EvictionPolicy} of the cache Provider - * - * @return a list containing the supported eviction policy - */ - public List getSupportedPolicies(); - - /** - * Indicates if the CacheProvider configuration can be changed. - * - * @return a boolean indicating if the cache configuration can be changed or not - */ - public boolean isImmutable(); - - /** - * Indicates if this {@link CacheProvider} object can be used - * - * @return a boolean indicating that the {@link CacheProvider} can be used for caching - */ - public boolean isAvailable(); - - /** - * Name of the {@link CacheProvider} - * - * @return a String with the Cache Provider name - */ - public String getName(); -} diff --git a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheStatistics.java b/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheStatistics.java deleted file mode 100644 index efc6b2f985..0000000000 --- a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/CacheStatistics.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - *

Copyright 2019 - */ -package org.geowebcache.storage.blobstore.memory; - -import java.io.Serial; -import java.io.Serializable; - -/** - * This class is a container of all the Statistics of the {@link CacheProvider} object used by the - * {@link MemoryBlobStore}. - * - * @author Nicola Lagomarsini, GeoSolutions - */ -public class CacheStatistics implements Serializable { - - /** serialVersionUID */ - @Serial - private static final long serialVersionUID = -1049287017217353112L; - - /** Cache hit count */ - private long hitCount = 0; - - /** Cache miss count */ - private long missCount = 0; - - /** Cache eviction count */ - private long evictionCount = 0; - - /** Cache total request count (hit + miss) */ - private long totalCount = 0; - - /** Cache hit rate */ - private double hitRate = 0; - - /** Cache miss rate */ - private double missRate = 0; - - /** Cache current memory occupation */ - private double currentMemoryOccupation = 0; - - /** Cache total size */ - private long totalSize = 0; - - /** Cache actual size */ - private long actualSize = 0; - - public CacheStatistics() {} - - // Copy Constructor - public CacheStatistics(CacheStatistics stats) { - this.setEvictionCount(stats.getEvictionCount()); - this.setHitCount(stats.getHitCount()); - this.setMissCount(stats.getMissCount()); - this.setTotalCount(stats.getRequestCount()); - this.setHitRate(stats.getHitRate()); - this.setMissRate(stats.getMissRate()); - this.setCurrentMemoryOccupation(stats.getCurrentMemoryOccupation()); - this.setActualSize(stats.getActualSize()); - this.setTotalSize(stats.getTotalSize()); - } - - /** @return the cache hit count */ - public long getHitCount() { - return hitCount; - } - - /** Setter for cache hit count */ - public void setHitCount(long hitCount) { - this.hitCount = hitCount; - } - - /** @return the cache miss count */ - public long getMissCount() { - return missCount; - } - - /** Setter for cache miss count */ - public void setMissCount(long missCount) { - this.missCount = missCount; - } - - /** @return the cache eviction count */ - public long getEvictionCount() { - return evictionCount; - } - - /** Setter for cache eviction count */ - public void setEvictionCount(long evictionCount) { - this.evictionCount = evictionCount; - } - - /** @return the cache total request count */ - public long getRequestCount() { - return totalCount; - } - - /** Setter for cache total count */ - public void setTotalCount(long totalCount) { - this.totalCount = totalCount; - } - - /** @return the cache hit rate */ - public double getHitRate() { - return hitRate; - } - - /** Setter for cache hit rate */ - public void setHitRate(double hitRate) { - this.hitRate = hitRate; - } - - /** @return the cache miss rate */ - public double getMissRate() { - return missRate; - } - - /** Setter for cache miss rate */ - public void setMissRate(double missRate) { - this.missRate = missRate; - } - - /** @return the cache current memory occupation */ - public double getCurrentMemoryOccupation() { - return currentMemoryOccupation; - } - - /** Setter for cache memory occupation */ - public void setCurrentMemoryOccupation(double currentMemoryOccupation) { - this.currentMemoryOccupation = currentMemoryOccupation; - } - - /** @return the cache current total size */ - public long getTotalSize() { - return totalSize; - } - - /** Setter for cache total size */ - public void setTotalSize(long totalSize) { - this.totalSize = totalSize; - } - - /** @return the cache current actual size */ - public long getActualSize() { - return actualSize; - } - - /** Setter for cache actual size */ - public void setActualSize(long actualSize) { - this.actualSize = actualSize; - } -} diff --git a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStore.java b/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStore.java deleted file mode 100644 index 5711749ab0..0000000000 --- a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStore.java +++ /dev/null @@ -1,785 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - *

Copyright 2019 - */ -package org.geowebcache.storage.blobstore.memory; - -import java.io.IOException; -import java.nio.channels.Channels; -import java.nio.channels.WritableByteChannel; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.commons.io.output.ByteArrayOutputStream; -import org.geotools.util.logging.Logging; -import org.geowebcache.io.ByteArrayResource; -import org.geowebcache.io.Resource; -import org.geowebcache.storage.BlobStore; -import org.geowebcache.storage.BlobStoreListener; -import org.geowebcache.storage.StorageException; -import org.geowebcache.storage.TileObject; -import org.geowebcache.storage.TileRange; -import org.geowebcache.storage.blobstore.memory.guava.GuavaCacheProvider; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - -/** - * This class is an implementation of the {@link BlobStore} interface wrapping another {@link BlobStore} implementation - * and supporting in memory caching. Caching is provided by an input {@link CacheProvider} object. It must be pointed - * out that this Blobstore has an asynchronous relation with the underlying wrapped {@link BlobStore}. In fact, each - * operation on the wrapped {@link BlobStore} is scheduled in a queue and will be done by an executor thread. Operations - * that require a boolean value will have to wait until previous tasks are completed. - * - * @author Nicola Lagomarsini Geosolutions - */ -public class MemoryBlobStore implements BlobStore, ApplicationContextAware { - - /** {@link Log} object used for logging exceptions */ - private static final Logger log = Logging.getLogger(MemoryBlobStore.class.getName()); - - /** {@link BlobStore} to use when no element is found */ - private BlobStore store; - - /** {@link CacheProvider} object to use for caching */ - private CacheProvider cacheProvider; - - /** Executor service used for scheduling cacheProvider store operations like put,delete,... */ - private final ExecutorService executorService; - - /** Optional name used for searching the bean related to the CacheProvider to set in the ApplicationContext */ - private String cacheBeanName; - - /** Boolean used for Application Context initialization */ - private AtomicBoolean cacheAlreadySet; - - /** {@link ReentrantReadWriteLock} used for handling concurrency when accessing the cacheProvider. */ - private final ReentrantReadWriteLock lock; - - /** {@link WriteLock} used for scheduling the access to the {@link MemoryBlobStore} state */ - private final WriteLock blobStoreStateLock; - - /** - * {@link ReadLock} used for granting access to operations which does not change the {@link MemoryBlobStore} state, - * but can change the state of its components like {@link CacheProvider} and {@link BlobStore} - */ - private final ReadLock componentsStateLock; - - public MemoryBlobStore() { - // Initialization of the various elements - this.executorService = Executors.newFixedThreadPool(1); - lock = new ReentrantReadWriteLock(true); - blobStoreStateLock = lock.writeLock(); - componentsStateLock = lock.readLock(); - cacheAlreadySet = new AtomicBoolean(false); - // Initialization of the cacheProvider and store. Must be overridden, this uses default and - // caches in memory - setStore(new NullBlobStore()); - GuavaCacheProvider startingCache = new GuavaCacheProvider(new CacheConfiguration()); - this.cacheProvider = startingCache; - } - - @Override - public boolean layerExists(String layerName) { - componentsStateLock.lock(); - try { - return store.layerExists(layerName); - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public boolean delete(String layerName) throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Removing layer: " + layerName + " from cache provider"); - } - // Remove from cacheProvider - cacheProvider.removeLayer(layerName); - // Remove the layer. Wait other scheduled tasks - boolean executed = executeBlobStoreTask(BlobStoreAction.DELETE_LAYER, store, layerName); - if (log.isLoggable(Level.FINE)) { - if (executed) { - log.fine("Delete Layer Task executed"); - } else { - log.fine("Delete LayerTask failed"); - } - } - // Returns the result - return executed; - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public boolean deleteByGridsetId(String layerName, String gridSetId) throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Removing Layer: " + layerName); - } - // Remove the layer from the cacheProvider - cacheProvider.removeLayer(layerName); - if (log.isLoggable(Level.FINE)) { - log.fine("Scheduling GridSet: " + gridSetId + " removal for Layer: " + layerName); - } - // Remove selected gridsets - executorService.submit(new BlobStoreTask(store, BlobStoreAction.DELETE_GRIDSET, layerName, gridSetId)); - return true; - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public boolean delete(TileObject obj) throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Removing TileObject: " + obj); - } - // Remove from cacheProvider - cacheProvider.removeTileObj(obj); - // Remove selected TileObject - if (log.isLoggable(Level.FINE)) { - log.fine("Scheduling removal of TileObject: " + obj); - } - executorService.submit(new BlobStoreTask(store, BlobStoreAction.DELETE_SINGLE, obj)); - return true; - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public boolean delete(TileRange obj) throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Removing TileObjects for Layer: " - + obj.getLayerName() - + ", min/max levels: " - + "[" - + obj.getZoomStart() - + ", " - + obj.getZoomStop() - + "], Gridset: " - + obj.getGridSetId()); - } - // Remove layer for the cacheProvider - cacheProvider.removeLayer(obj.getLayerName()); - // Remove selected TileObject - if (log.isLoggable(Level.FINE)) { - log.fine("Scheduling removal of TileObjects for Layer: " - + obj.getLayerName() - + ", min/max levels: " - + "[" - + obj.getZoomStart() - + ", " - + obj.getZoomStop() - + "], Gridset: " - + obj.getGridSetId()); - } - // Remove selected TileRange - executorService.submit(new BlobStoreTask(store, BlobStoreAction.DELETE_RANGE, obj)); - return true; - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public boolean get(TileObject obj) throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Checking if TileObject:" + obj + " is present"); - } - TileObject cached = cacheProvider.getTileObj(obj); - boolean found = false; - if (cached == null) { - if (log.isLoggable(Level.FINE)) { - log.fine("TileObject:" + obj + " not found. Try to get it from the wrapped blobstore"); - } - // Try if it can be found in the system. Wait other scheduled tasks - found = executeBlobStoreTask(BlobStoreAction.GET, store, obj); - - // If the file has been found, it is inserted in cacheProvider - if (found) { - if (log.isLoggable(Level.FINE)) { - log.fine("TileObject:" + obj + " found. Put it in cache"); - } - // Get the Cached TileObject - cached = getByteResourceTile(obj); - // Put the file in Cache - cacheProvider.putTileObj(cached); - } - } else { - // Found in cacheProvider - found = true; - } - // If found add its resource to the input TileObject - if (found) { - if (log.isLoggable(Level.FINE)) { - log.fine("TileObject:" + obj + " found, update the input TileObject"); - } - Resource resource = cached.getBlob(); - obj.setBlob(resource); - obj.setCreated(resource.getLastModified()); - obj.setBlobSize((int) resource.getSize()); - } - - return found; - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public void put(TileObject obj) throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Convert Input resource into a Byte Array"); - } - TileObject cached = getByteResourceTile(obj); - if (log.isLoggable(Level.FINE)) { - log.fine("Adding TileObject: " + obj + " to cache"); - } - cacheProvider.putTileObj(cached); - // Add selected TileObject. Wait other scheduled tasks - if (log.isLoggable(Level.FINE)) { - log.fine("Adding TileObject: " + obj + " to the wrapped blobstore"); - } - // Variable containing the execution result - executeBlobStoreTask(BlobStoreAction.PUT, store, obj); - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public void clear() throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Flushing cache"); - } - // flush the cacheProvider - cacheProvider.clear(); - // Remove all the files - executorService.submit(new BlobStoreTask(store, BlobStoreAction.CLEAR, "")); - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public void destroy() { - blobStoreStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Reset cache"); - } - // flush the cacheProvider - cacheProvider.reset(); - // Remove all the files - if (log.isLoggable(Level.FINE)) { - log.fine("Destroy wrapped store"); - } - executeBlobStoreTask(BlobStoreAction.DESTROY, store, ""); - // Stop the pending tasks - executorService.shutdown(); - } finally { - blobStoreStateLock.unlock(); - } - } - - @Override - public void addListener(BlobStoreListener listener) { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Adding a new Listener"); - } - // Add a new Listener - store.addListener(listener); - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public boolean removeListener(BlobStoreListener listener) { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Removing listener"); - } - // Remove a listener - return store.removeListener(listener); - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public boolean rename(String oldLayerName, String newLayerName) throws StorageException { - componentsStateLock.lock(); - try { - // flush the cacheProvider - if (log.isLoggable(Level.FINE)) { - log.fine("Flushing cache"); - } - cacheProvider.clear(); - // Rename the layer. Wait other scheduled tasks - if (log.isLoggable(Level.FINE)) { - log.fine("Executing Layer rename task"); - } - // Variable containing the execution result - boolean executed = executeBlobStoreTask(BlobStoreAction.RENAME, store, oldLayerName, newLayerName); - return executed; - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public String getLayerMetadata(String layerName, String key) { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Getting metadata for Layer: " + layerName); - } - // Get the Layer metadata - return store.getLayerMetadata(layerName, key); - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public void putLayerMetadata(String layerName, String key, String value) { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Adding metadata for Layer: " + layerName); - } - // Add a new Layer Metadata - store.putLayerMetadata(layerName, key, value); - } finally { - componentsStateLock.unlock(); - } - } - - /** @return a {@link CacheStatistics} object containing the {@link CacheProvider} statistics */ - public CacheStatistics getCacheStatistics() { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Getting Cache Statistics"); - } - return cacheProvider.getStatistics(); - } finally { - componentsStateLock.unlock(); - } - } - - /** Setter for the store to wrap */ - public void setStore(BlobStore store) { - blobStoreStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Setting the wrapped store"); - } - if (store == null) { - throw new NullPointerException("Input BlobStore cannot be null"); - } - this.store = store; - } finally { - blobStoreStateLock.unlock(); - } - } - - /** @return The wrapped {@link BlobStore} implementation */ - public BlobStore getStore() { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Returning the wrapped store"); - } - return store; - } finally { - componentsStateLock.unlock(); - } - } - - /** Setter for the cacheProvider to use */ - public void setCacheProvider(CacheProvider cache) { - blobStoreStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Setting cache provided"); - } - if (cache == null) { - throw new IllegalArgumentException("Input BlobStore cannot be null"); - } - this.cacheProvider = cache; - cacheAlreadySet.getAndSet(true); - } finally { - blobStoreStateLock.unlock(); - } - } - - /** - * * This method is used for converting a {@link TileObject} {@link Resource} into a {@link ByteArrayResource}. - * - * @return a TileObject with resource stored in a Byte Array - */ - private TileObject getByteResourceTile(TileObject obj) throws StorageException { - // Get TileObject resource - Resource blob = obj.getBlob(); - final ByteArrayResource finalBlob; - // If it is a ByteArrayResource, the result is simply copied - if (obj.getBlob() instanceof ByteArrayResource) { - if (log.isLoggable(Level.FINE)) { - log.fine("Resource is already a Byte Array, only a copy is needed"); - } - ByteArrayResource byteArrayResource = (ByteArrayResource) obj.getBlob(); - byte[] contents = byteArrayResource.getContents(); - finalBlob = new ByteArrayResource(contents); - } else { - if (log.isLoggable(Level.FINE)) { - log.fine("Resource is not a Byte Array, data must be transferred"); - } - // Else the result is written to a new WritableByteChannel - try (ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - WritableByteChannel wChannel = Channels.newChannel(bOut)) { - blob.transferTo(wChannel); - finalBlob = new ByteArrayResource(bOut.toByteArray()); - } catch (IOException e) { - throw new StorageException(e.getLocalizedMessage(), e); - } - } - - finalBlob.setLastModified(blob.getLastModified()); - // Creation of a new Resource - TileObject cached = TileObject.createCompleteTileObject( - obj.getLayerName(), - obj.getXYZ(), - obj.getGridSetId(), - obj.getBlobFormat(), - obj.getParameters(), - finalBlob); - return cached; - } - - /** - * Setter for the Cache Provider name, note that this cannot be used in combination with the setCacheProvider method - * in the application Context initialization - */ - public void setCacheBeanName(String cacheBeanName) { - blobStoreStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Setting cache providee name"); - } - this.cacheBeanName = cacheBeanName; - } finally { - blobStoreStateLock.unlock(); - } - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - if (!cacheAlreadySet.get()) { - // Get all the CacheProvider beans - String[] beans = applicationContext.getBeanNamesForType(CacheProvider.class); - int beanSize = beans.length; - boolean configured = false; - // If at least one bean is present, use it - if (beanSize > 0) { - // If a bean name is defined, get the related bean - if (cacheBeanName != null && !cacheBeanName.isEmpty()) { - for (String beanDef : beans) { - if (cacheBeanName.equalsIgnoreCase(beanDef)) { - CacheProvider bean = applicationContext.getBean(beanDef, CacheProvider.class); - if (bean.isAvailable()) { - setCacheProvider(bean); - configured = true; - break; - } - } - } - } - // If only one is present it is used - if (!configured && beanSize == 1) { - CacheProvider bean = applicationContext.getBean(beans[0], CacheProvider.class); - if (bean.isAvailable()) { - setCacheProvider(bean); - configured = true; - } - } - // If two are present and at least one of them is not guava, then it is used - if (!configured && beanSize == 2) { - for (String beanDef : beans) { - CacheProvider bean = applicationContext.getBean(beanDef, CacheProvider.class); - if (!(bean instanceof GuavaCacheProvider) && bean.isAvailable()) { - setCacheProvider(bean); - configured = true; - break; - } - } - // Try again and search if at least a GuavaCacheProvider is present - if (!configured) { - for (String beanDef : beans) { - CacheProvider bean = applicationContext.getBean(beanDef, CacheProvider.class); - if (bean.isAvailable()) { - setCacheProvider(bean); - configured = true; - break; - } - } - } - } - if (!configured) { - if (log.isLoggable(Level.FINE)) { - log.fine("CacheProvider not configured, use default configuration"); - } - } - } - } else { - if (log.isLoggable(Level.FINE)) { - log.fine("CacheProvider already configured"); - } - } - } - - private boolean executeBlobStoreTask(BlobStoreAction action, BlobStore store, Object... objs) { - Future future = executorService.submit(new BlobStoreTask(store, action, objs)); - // Variable containing the execution result - boolean executed = false; - if (log.isLoggable(Level.FINE)) { - log.fine("Waiting scheduled Tasks"); - } - try { - // Waiting tasks - executed = future.get(); - } catch (InterruptedException | ExecutionException e) { - if (log.isLoggable(Level.SEVERE)) { - log.log(Level.SEVERE, e.getMessage(), e); - } - } - return executed; - } - - /** - * {@link Callable} implementation used for creating various tasks to submit to the {@link MemoryBlobStore} executor - * service. - * - * @author Nicola Lagomarsini GeoSolutions - */ - static class BlobStoreTask implements Callable { - - /** Store on which tasks must be executed */ - private BlobStore store; - - /** Array of objects that must be used for the selected operation */ - private Object[] objs; - - /** Enum containing the kind of action to execute */ - private BlobStoreAction action; - - public BlobStoreTask(BlobStore store, BlobStoreAction action, Object... objs) { - this.objs = objs; - this.store = store; - this.action = action; - } - - @Override - public Boolean call() throws Exception { - boolean result = false; - try { - // Execution of the requested operation - result = action.executeOperation(store, objs); - } catch (StorageException s) { - if (log.isLoggable(Level.SEVERE)) { - log.log(Level.SEVERE, s.getMessage(), s); - } - } - return result; - } - } - - /** - * Enum containing all the possible operations that can be executed by a {@link BlobStoreTask}. Each operation must - * implement the "executeOperation" method. - * - * @author Nicola Lagomarsini GeoSolutions - */ - public enum BlobStoreAction { - PUT { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - if (objs == null || objs.length < 1 || !(objs[0] instanceof TileObject)) { - return false; - } - store.put((TileObject) objs[0]); - return true; - } - }, - GET { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - if (objs == null || objs.length < 1 || !(objs[0] instanceof TileObject)) { - return false; - } - return store.get((TileObject) objs[0]); - } - }, - DELETE_SINGLE { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - if (objs == null || objs.length < 1 || !(objs[0] instanceof TileObject)) { - return false; - } - return store.delete((TileObject) objs[0]); - } - }, - DELETE_RANGE { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - if (objs == null || objs.length < 1 || !(objs[0] instanceof TileRange)) { - return false; - } - return store.delete((TileRange) objs[0]); - } - }, - DELETE_GRIDSET { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - if (objs == null || objs.length < 2 || !(objs[0] instanceof String) || !(objs[1] instanceof String)) { - return false; - } - return store.deleteByGridsetId((String) objs[0], (String) objs[1]); - } - }, - DELETE_PARAMS_ID { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - if (objs == null || objs.length < 2 || !(objs[0] instanceof String) || !(objs[1] instanceof String)) { - return false; - } - return store.deleteByParametersId((String) objs[0], (String) objs[1]); - } - }, - DELETE_LAYER { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - if (objs == null || objs.length < 1 || !(objs[0] instanceof String)) { - return false; - } - return store.delete((String) objs[0]); - } - }, - CLEAR { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - store.clear(); - return true; - } - }, - DESTROY { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - store.destroy(); - return true; - } - }, - RENAME { - @Override - public boolean executeOperation(BlobStore store, Object... objs) throws StorageException { - if (objs == null || objs.length < 2 || !(objs[0] instanceof String) || !(objs[1] instanceof String)) { - return false; - } - return store.rename((String) objs[0], (String) objs[1]); - } - }; - - /** - * Executes an operation defined by the Enum. - * - * @return operation result - */ - public abstract boolean executeOperation(BlobStore store, Object... objs) throws StorageException; - } - - @Override - public boolean deleteByParametersId(String layerName, String parametersId) throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Removing Layer: " + layerName); - } - // Remove the layer from the cacheProvider - cacheProvider.removeLayer(layerName); - if (log.isLoggable(Level.FINE)) { - log.fine("Scheduling Parameters: " + parametersId + " removal for Layer: " + layerName); - } - // Remove selected parameters - executorService.submit(new BlobStoreTask(store, BlobStoreAction.DELETE_PARAMS_ID, layerName, parametersId)); - return true; - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public Set> getParameters(String layerName) throws StorageException { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Getting parameters for Layer: " + layerName); - } - return store.getParameters(layerName); - } finally { - componentsStateLock.unlock(); - } - } - - @Override - public Map>> getParametersMapping(String layerName) { - componentsStateLock.lock(); - try { - if (log.isLoggable(Level.FINE)) { - log.fine("Getting parameters for Layer: " + layerName); - } - return store.getParametersMapping(layerName); - } finally { - componentsStateLock.unlock(); - } - } -} diff --git a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/NullBlobStore.java b/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/NullBlobStore.java deleted file mode 100644 index b49654f37e..0000000000 --- a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/NullBlobStore.java +++ /dev/null @@ -1,160 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - *

Copyright 2019 - */ -package org.geowebcache.storage.blobstore.memory; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.geotools.util.logging.Logging; -import org.geowebcache.storage.BlobStore; -import org.geowebcache.storage.BlobStoreListener; -import org.geowebcache.storage.StorageException; -import org.geowebcache.storage.TileObject; -import org.geowebcache.storage.TileRange; - -/** - * This class is an implementation of the {@link BlobStore} interface which does not store anything on the file system - * and can be used for doing a pure in memory caching of the {@link TileObject}s. This class simply stores the layer - * metadata in a {@link Map} object. The other operations are not executed. - * - * @author Nicola Lagomarsini Geosolutions - */ -public class NullBlobStore implements BlobStore { - - private Map metadataMap; - - /** {@link Logger} object used for logging exceptions */ - private static final Logger LOGGER = Logging.getLogger(NullBlobStore.class.getName()); - - public NullBlobStore() { - // Map initialization - metadataMap = new HashMap<>(); - } - - @Override - public boolean delete(String layerName) throws StorageException { - return true; - } - - @Override - public boolean deleteByGridsetId(String layerName, String gridSetId) throws StorageException { - return true; - } - - @Override - public boolean delete(TileObject obj) throws StorageException { - return true; - } - - @Override - public boolean delete(TileRange obj) throws StorageException { - return true; - } - - @Override - public boolean get(TileObject obj) throws StorageException { - return false; - } - - @Override - public void put(TileObject obj) throws StorageException {} - - @Override - public void clear() throws StorageException {} - - @Override - public synchronized void destroy() { - // Clear the properties map - metadataMap.clear(); - } - - @Override - public void addListener(BlobStoreListener listener) {} - - @Override - public boolean removeListener(BlobStoreListener listener) { - return true; - } - - @Override - public boolean rename(String oldLayerName, String newLayerName) throws StorageException { - return true; - } - - @Override - public synchronized String getLayerMetadata(String layerName, String key) { - // Check if the property is present - Properties properties = metadataMap.get(layerName); - if (properties != null) { - // Returns the property associated to the key - try { - return URLDecoder.decode((String) properties.get(key), "UTF-8"); - } catch (UnsupportedEncodingException e) { - LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); - } - } - return null; - } - - @Override - public synchronized void putLayerMetadata(String layerName, String key, String value) { - // Check if the property is present - Properties props = metadataMap.get(layerName); - if (props != null) { - try { - // If present adds the new property - props.setProperty(key, URLEncoder.encode(value, "UTF-8")); - } catch (UnsupportedEncodingException e) { - LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); - } - } else { - // Else creates a new Property object and them adds the new property - props = new Properties(); - try { - props.setProperty(key, URLEncoder.encode(value, "UTF-8")); - } catch (UnsupportedEncodingException e) { - LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e); - } - metadataMap.put(layerName, props); - } - } - - @Override - public boolean layerExists(String layerName) { - return false; - } - - @Override - public boolean deleteByParametersId(String layerName, String parametersId) throws StorageException { - return true; - } - - @Override - public Set> getParameters(String layerName) { - return Collections.emptySet(); - } - - @Override - public Map>> getParametersMapping(String layerName) { - return Collections.emptyMap(); - } -} diff --git a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/guava/GuavaCacheProvider.java b/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/guava/GuavaCacheProvider.java deleted file mode 100644 index 93528fbe9e..0000000000 --- a/geowebcache/core/src/main/java/org/geowebcache/storage/blobstore/memory/guava/GuavaCacheProvider.java +++ /dev/null @@ -1,667 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - *

Copyright 2019 - */ -package org.geowebcache.storage.blobstore.memory.guava; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheStats; -import com.google.common.cache.Weigher; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.geotools.util.logging.Logging; -import org.geowebcache.storage.TileObject; -import org.geowebcache.storage.blobstore.memory.CacheConfiguration; -import org.geowebcache.storage.blobstore.memory.CacheConfiguration.EvictionPolicy; -import org.geowebcache.storage.blobstore.memory.CacheProvider; -import org.geowebcache.storage.blobstore.memory.CacheStatistics; -import org.geowebcache.util.SuppressFBWarnings; - -/** - * This class is an implementation of the {@link CacheProvider} interface using a backing Guava {@link Cache} object. - * This implementation requires to be configured with the configure() method. - * - * @author Nicola Lagomarsini Geosolutions - */ -public class GuavaCacheProvider implements CacheProvider { - - /** {@link Logger} object used for logging exceptions */ - private static final Logger LOGGER = Logging.getLogger(GuavaCacheProvider.class.getName()); - - /** Separator char used for creating Cache keys */ - public static final String SEPARATOR = "_"; - - /** Constant for multiplying bytes to MB */ - public static final long BYTES_TO_MB = 1048576; - - /** Size of the scheduled thread pool */ - public static final int CORE_POOL_SIZE = 1; - - private static final String GUAVA_NAME = "Guava Cache"; - - /** Array containing the supported Policies */ - public static final List POLICIES = Collections.unmodifiableList( - Arrays.asList(EvictionPolicy.NULL, EvictionPolicy.EXPIRE_AFTER_ACCESS, EvictionPolicy.EXPIRE_AFTER_WRITE)); - - /** - * This class handles the {@link CacheStats} object returned by the guava cache. - * - * @author Nicola Lagomarsini Geosolutions - */ - public static class GuavaCacheStatistics extends CacheStatistics { - - /** serialVersionUID */ - private static final long serialVersionUID = 1L; - - public GuavaCacheStatistics(CacheStats stats, double currentSpace, long actualSize, long totalSize) { - this.setEvictionCount(stats.evictionCount()); - this.setHitCount(stats.hitCount()); - this.setMissCount(stats.missCount()); - this.setTotalCount(stats.requestCount()); - this.setHitRate((int) (stats.hitRate() * 100)); - this.setMissRate(100 - getHitRate()); - this.setCurrentMemoryOccupation(currentSpace); - this.setActualSize(actualSize); - this.setTotalSize(totalSize); - } - } - - /** Cache object containing the various {@link TileObject}s */ - private Cache cache; - - /** Internal Multimap used for storing the TileObject ids associated to each cached Layer */ - private LayerMap multimap; - - /** {@link AtomicBoolean} used for ensuring that the Cache has already been configured */ - private AtomicBoolean configured; - - /** {@link AtomicLong} used for checking the number of active operations to wait when resetting the cache */ - private AtomicLong actualOperations; - - /** Internal concurrent Set used for saving the names of the Layers that must not be cached */ - private final Set layers; - - /** Cache total memory in Mb */ - private long maxMemory = 0L; - - /** {@link AtomicLong} used for storing the current cache size */ - private AtomicLong currentSize = new AtomicLong(0); - - private ScheduledExecutorService scheduledPool; - - public GuavaCacheProvider(CacheConfiguration config) { - // Initialization of the Layer set and of the Atomic parameters - layers = Collections.newSetFromMap(new ConcurrentHashMap<>()); - configured = new AtomicBoolean(false); - actualOperations = new AtomicLong(0); - configure(config); - } - - /** This method is used for creating a new cache object, from the defined configuration. */ - private void initCache(CacheConfiguration configuration) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Building new Cache"); - } - // Initialization step - int concurrency = configuration.getConcurrencyLevel(); - maxMemory = configuration.getHardMemoryLimit() * BYTES_TO_MB; - long evictionTime = configuration.getEvictionTime(); - EvictionPolicy policy = configuration.getPolicy(); - - // If Cache already exists, flush it - if (cache != null) { - cache.invalidateAll(); - } - // Create the CacheBuilder - CacheBuilder builder = CacheBuilder.newBuilder(); - // Add weigher - Weigher weigher = (key, value) -> { - currentSize.addAndGet(value.getBlobSize()); - return value.getBlobSize(); - }; - // Create the builder - CacheBuilder newBuilder = builder.maximumWeight(maxMemory) - .recordStats() - .weigher(weigher) - .concurrencyLevel(concurrency) - .removalListener(notification -> { - // TODO This operation is not atomic - TileObject obj = notification.getValue(); - // Update the current size - currentSize.addAndGet(-obj.getBlobSize()); - final String tileKey = generateTileKey(obj); - final String layerName = obj.getLayerName(); - multimap.removeTile(layerName, tileKey); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Removed tile " - + tileKey - + " for layer " - + layerName - + " due to reason:" - + notification.getCause().toString()); - LOGGER.fine("Removed tile was evicted? " + notification.wasEvicted()); - } - }); - // Handle eviction policy - boolean configuredPolicy = false; - if (policy != null && evictionTime > 0) { - if (policy == EvictionPolicy.EXPIRE_AFTER_ACCESS) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Configuring Expire After Access eviction policy"); - } - newBuilder.expireAfterAccess(evictionTime, TimeUnit.SECONDS); - configuredPolicy = true; - } else if (policy == EvictionPolicy.EXPIRE_AFTER_WRITE) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Configuring Expire After Write eviction policy"); - } - newBuilder.expireAfterWrite(evictionTime, TimeUnit.SECONDS); - configuredPolicy = true; - } - } - - // Build the cache - cache = newBuilder.build(); - - // Created a new multimap - multimap = new LayerMap(); - - // Configure a new scheduling task if needed - if (configuredPolicy) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Configuring Scheduled Task for cache eviction"); - } - Runnable command = () -> { - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - cache.cleanUp(); - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } - }; - // Initialization of the internal Scheduler task for scheduling cache cleanup - scheduledPool = Executors.newScheduledThreadPool(CORE_POOL_SIZE); - scheduledPool.scheduleAtFixedRate(command, 10, evictionTime + 1, TimeUnit.SECONDS); - } - - // Update the configured parameter - configured.getAndSet(true); - } - - @Override - public boolean isImmutable() { - return false; - } - - @Override - public synchronized void configure(CacheConfiguration configuration) { - // NOTE that if the cache has already been configured, the user must always call - // resetCache() before - // setting the new configuration - reset(); - // Configure a new cache - initCache(configuration); - } - - @Override - public TileObject getTileObj(TileObject obj) { - // Check if the cache has already been configured - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Checking if the layer must not be cached"); - } - // Check if the layer must be cached - if (layers.contains(obj.getLayerName())) { - // The layer must not be cached - return null; - } - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Retrieving TileObject: " + obj + " from cache"); - } - // Generate the TileObject key - String id = generateTileKey(obj); - // Get the key from the cache - return cache.getIfPresent(id); - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } - return null; - } - - @Override - public void putTileObj(TileObject obj) { - // Check if the cache has already been configured - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Checking if the layer must not be cached"); - } - // Check if the layer must be cached - if (layers.contains(obj.getLayerName())) { - // The layer must not be cached - return; - } - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Adding TileObject: " + obj + " to cache"); - } - // Generate the TileObject key - String id = generateTileKey(obj); - // Add the TileObject to the cache and its id in the multimap - cache.put(id, obj); - multimap.putTile(obj.getLayerName(), id); - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } - } - - @Override - public void removeTileObj(TileObject obj) { - // Check if the cache has already been configured - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Checking if the layer must not be cached"); - } - // Check if the layer must be cached - if (layers.contains(obj.getLayerName())) { - // The layer must not be cached - return; - } - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Removing TileObject: " + obj + " from cache"); - } - // Generate the TileObject key - String id = generateTileKey(obj); - // Remove the key - cache.invalidate(id); - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } - } - - @Override - public void removeLayer(String layername) { - // Check if the cache has already been configured - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Checking if the layer must not be cached"); - } - // Check if the layer must be cached - if (layers.contains(layername)) { - // The layer must not be cached - return; - } - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Removing Layer: " + layername + " from cache"); - } - // Get all the TileObject ids associated to the Layer and removes them - Set keys = multimap.removeLayer(layername); - if (keys != null) { - cache.invalidateAll(keys); - } - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } - } - - @Override - public void clear() { - // Check if the cache has already been configured - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Flushing cache"); - } - // Remove all the elements from the cache - if (cache != null) { - cache.invalidateAll(); - } - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } - } - - @Override - @SuppressWarnings("PMD.EmptyControlStatement") - public void reset() { - if (configured.getAndSet(false)) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Reset Cache internally"); - } - // Avoid to call the While cycle before having started an operation with configured == - // false - actualOperations.incrementAndGet(); - actualOperations.decrementAndGet(); - // Wait until all the operations are finished - while (actualOperations.get() > 0) {} - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Flushing cache"); - } - // Remove all the elements from the cache - if (cache != null) { - cache.invalidateAll(); - } - // Remove all the Layers configured for avoiding caching - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Removing Layers"); - } - layers.clear(); - // Shutdown the current Executor service - if (scheduledPool != null) { - scheduledPool.shutdown(); - try { - scheduledPool.awaitTermination(10, TimeUnit.SECONDS); - } catch (InterruptedException e) { - if (LOGGER.isLoggable(Level.SEVERE)) { - LOGGER.log(Level.SEVERE, e.getMessage(), e); - } - Thread.currentThread().interrupt(); - } finally { - scheduledPool = null; - } - } - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache is already reset"); - } - } - } - - @Override - public CacheStatistics getStatistics() { - // Check if the cache has already been configured - if (configured.get()) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Retrieving statistics"); - } - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - // Get cache statistics - long actualSize = currentSize.get(); - long currentSpace = (long) (100L - (1L) * (100 * ((1.0d) * (maxMemory - actualSize)) / maxMemory)); - if (currentSpace < 0) { - currentSpace = 0; - } - // Returns a new Object containing a snapshot of the cache statistics - return new GuavaCacheStatistics(cache.stats(), currentSpace, actualSize, maxMemory); - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Returning empty statistics"); - } - // Else returns an empty CacheStatistics object - return new CacheStatistics(); - } - } - - /** - * * Static method for generating the {@link TileObject} cache key to use for caching. - * - * @return {@link TileObject} key - */ - public static String generateTileKey(TileObject obj) { - Map parameters = obj.getParameters(); - - StringBuilder builder = new StringBuilder(obj.getLayerName()) - .append(SEPARATOR) - .append(obj.getGridSetId()) - .append(SEPARATOR) - .append(Arrays.toString(obj.getXYZ())) - .append(SEPARATOR) - .append(obj.getBlobFormat()); - - // If parameters are present they must be handled - if (parameters != null && !parameters.isEmpty()) { - for (String key : parameters.keySet()) { - builder.append(SEPARATOR).append(key).append(SEPARATOR).append(parameters.get(key)); - } - } - - return builder.toString(); - } - - @Override - public String getName() { - return GUAVA_NAME; - } - - @Override - public void addUncachedLayer(String layername) { - // Check if the cache has already been configured - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Adding Layer:" + layername + " to avoid cache"); - } - // Adds the layer which should not be cached - layers.add(layername); - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } - } - - @Override - public void removeUncachedLayer(String layername) { - // Check if the cache has already been configured - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Removing Layer:" + layername + " to avoid cache"); - } - // Configure a Layer for being cached again - layers.remove(layername); - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } - } - - @Override - public boolean containsUncachedLayer(String layername) { - // Check if the cache has already been configured - if (configured.get()) { - // Increment the number of current operations - // This behavior is used in order to wait - // the end of all the operations after setting - // the configured parameter to false - actualOperations.incrementAndGet(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Checking if Layer:" + layername + " must not be cached"); - } - // Check if the layer must not be cached - return layers.contains(layername); - } finally { - // Decrement the number of current operations. - actualOperations.decrementAndGet(); - } - } else { - return false; - } - } - - @Override - public List getSupportedPolicies() { - return POLICIES; - } - - @Override - public boolean isAvailable() { - return true; - } - - /** - * Internal class representing a concurrent multimap which associates to each Layer name the related - * {@link TileObject} cache keys. This map is useful when trying to remove a Layer, because it returns quicly all - * the cached keys of the selected layer, without having to cycle on the cache and checking if each TileObject - * belongs to the selected Layer. - * - * @author Nicola Lagomarsini, GeoSolutions - */ - static class LayerMap { - /** {@link WriteLock} used when trying to change remove a layer from the map */ - private final Lock writeLock; - - /** MultiMap containing the {@link TileObject} keys for the Layers */ - private final ConcurrentHashMap> layerMap = new ConcurrentHashMap<>(); - - public LayerMap() { - // Lock initialization - writeLock = new ReentrantLock(true); - } - - /** Insertion of a {@link TileObject} key in the map for the associated Layer. */ - // not sure why locking is used on top of concurrent structures to start with... just - // ignoring to move on, but imho all locks should be removed and concurrent structure - // be used as intended (e.g., putIfAbsent and the like - @SuppressFBWarnings({ - "AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION", - "UL_UNRELEASED_LOCK", - "UL_UNRELEASED_LOCK_EXCEPTION_PATH" - }) - public void putTile(String layer, String id) { - Set tileKeys = layerMap.computeIfAbsent(layer, l -> new ConcurrentSkipListSet<>()); - - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Add the TileObject id to the Map"); - } - // Finally the tile key is added. - tileKeys.add(id); - } - - /** Removal of a {@link TileObject} key in the map for the associated Layer. */ - public void removeTile(String layer, String id) { - Set tileKeys = layerMap.get(layer); - if (tileKeys != null) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Remove TileObject id to the Map"); - } - // Removal of the keys - tileKeys.remove(id); - // If the KeySet is empty then it is removed from the multimap - if (tileKeys.isEmpty()) { - writeLock.lock(); - try { - tileKeys = layerMap.get(layer); - if (tileKeys != null && tileKeys.isEmpty()) { - removeLayer(layer); - } - } finally { - writeLock.unlock(); - } - } - } - } - - /** - * Removes a layer {@link Set} and returns it to the cache. - * - * @return the keys associated to the Layer - */ - public Set removeLayer(String layer) { - writeLock.lock(); - try { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Removing KeySet for Layer: " + layer); - } - // Get the Set from the map - Set layers = layerMap.get(layer); - // Removes the set from the map - layerMap.remove(layer); - // Returns the set - return layers; - } finally { - writeLock.unlock(); - } - } - } -} diff --git a/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreComformanceTest.java b/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreComformanceTest.java deleted file mode 100644 index bcedf19787..0000000000 --- a/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreComformanceTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - * @author Kevin Smith, Boundless, 2017 - */ -package org.geowebcache.storage.blobstore.memory; - -import org.geowebcache.storage.AbstractBlobStoreTest; -import org.geowebcache.storage.StorageException; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -public class MemoryBlobStoreComformanceTest extends AbstractBlobStoreTest { - - @Override - public void createTestUnit() throws Exception { - this.store = new MemoryBlobStore(); - } - - @Before - public void setEvents() throws Exception { - this.events = false; - } - - @Override - @Ignore - @Test // Memory store can be more relaxed about this. It would be nice to pass this though - public void testDeleteGridsetDoesntDeleteOthers() throws Exception { - super.testDeleteGridsetDoesntDeleteOthers(); - } - - @Override - @Ignore - @Test // Memory store can be more relaxed about this. It would be nice to pass this though - public void testDeleteByParametersIdDoesNotDeleteOthers() throws Exception { - super.testDeleteByParametersIdDoesNotDeleteOthers(); - } - - @Override - @Ignore - @Test // TODO For now, this is a limitation of MemoryBlobStore - public void testParameterList() throws Exception { - super.testParameterList(); - } - - @Override - @Ignore - @Test // TODO For now, this is a limitation of MemoryBlobStore - public void testParameterIDList() throws Exception { - super.testParameterIDList(); - } - - @Override - @Ignore - @Test // Memory store can be more relaxed about this. It would be nice to pass this though - public void testPurgeOrphans() throws Exception { - super.testPurgeOrphans(); - } - - @Override - @Ignore - @Test // Memory store can be more relaxed about this. It would be nice to pass this though - public void testPurgeOrphansWithDefault() throws Exception { - super.testPurgeOrphansWithDefault(); - } - - @Override - @Ignore - @Test // Memory store can be more relaxed about this. It would be nice to pass this though - public void testDeleteRangeSingleLevel() throws StorageException { - super.testDeleteRangeSingleLevel(); - } - - @Override - @Ignore - @Test // Memory store can be more relaxed about this. It would be nice to pass this though - public void testDeleteRangeMultiLevel() throws StorageException { - super.testDeleteRangeMultiLevel(); - } -} diff --git a/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreTest.java b/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreTest.java deleted file mode 100644 index 4dc86c4732..0000000000 --- a/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreTest.java +++ /dev/null @@ -1,301 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - */ -package org.geowebcache.storage.blobstore.memory; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.geotools.util.logging.Logging; -import org.geowebcache.io.ByteArrayResource; -import org.geowebcache.io.Resource; -import org.geowebcache.storage.BlobStore; -import org.geowebcache.storage.StorageBrokerTest; -import org.geowebcache.storage.TileObject; -import org.geowebcache.storage.blobstore.file.FileBlobStore; -import org.geowebcache.storage.blobstore.memory.guava.GuavaCacheProvider; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * This test class is used for testing {@link org.geowebcache.storage.blobstore.memory.MemoryBlobStore} functionality - * - * @author Nicola Lagomarsini Geosolutions - */ -public class MemoryBlobStoreTest { - - /** LOGGER */ - public static final Logger log = Logging.getLogger(MemoryBlobStoreTest.class.getName()); - - public static final String TEST_BLOB_DIR_NAME = "gwcTestBlobs"; - - /** Cache object */ - private CacheProvider cache; - - private MemoryBlobStore mbs; - private NullBlobStore nbs; - private BlobStore fbs; - - @After - public void destroyBlobStores() { - if (mbs != null) { - mbs.destroy(); - } - if (nbs != null) { - nbs.destroy(); - } - if (fbs != null) { - fbs.destroy(); - } - } - - @Before - public void initCache() { - // Initial cache configuration - cache = new GuavaCacheProvider(new CacheConfiguration()); - } - - @Test - public void testNullStore() throws Exception { - // Add a nullblobstore to the memory blobstore - nbs = new NullBlobStore(); - cache.clear(); - - mbs = new MemoryBlobStore(); - mbs.setStore(nbs); - mbs.setCacheProvider(cache); - - // Put a TileObject - Resource bytes = new ByteArrayResource("1 2 3 4 5 6 test".getBytes()); - long[] xyz = {1L, 2L, 3L}; - Map parameters = new HashMap<>(); - parameters.put("a", "x"); - parameters.put("b", "ø"); - TileObject to = TileObject.createCompleteTileObject( - "test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters, bytes); - - mbs.put(to); - // Try to get the same TileObject - TileObject to2 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - mbs.get(to2); - - // Checks on the format - assertEquals(to.getBlobFormat(), to2.getBlobFormat()); - - // Checks if the resources are equals - try (InputStream is = to.getBlob().getInputStream(); - InputStream is2 = to2.getBlob().getInputStream()) { - checkInputStreams(is, is2); - } - - // Ensure Cache contains the result - TileObject to3 = cache.getTileObj(to); - assertNotNull(to3); - assertEquals(to.getBlobFormat(), to3.getBlobFormat()); - - // Checks if the resources are equals - try (InputStream is = to.getBlob().getInputStream(); - InputStream is3 = to3.getBlob().getInputStream()) { - checkInputStreams(is, is3); - } - - // Ensure that NullBlobStore does not contain anything - assertFalse(nbs.get(to)); - } - - @Test - public void testTilePut() throws Exception { - // Add a fileblobstore to the memory blobstore - fbs = setup(); - cache.clear(); - - mbs = new MemoryBlobStore(); - mbs.setStore(fbs); - mbs.setCacheProvider(cache); - - // Put a TileObject - Resource bytes = new ByteArrayResource("1 2 3 4 5 6 test".getBytes()); - long[] xyz = {1L, 2L, 3L}; - Map parameters = new HashMap<>(); - parameters.put("a", "x"); - parameters.put("b", "ø"); - TileObject to = TileObject.createCompleteTileObject( - "test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters, bytes); - - mbs.put(to); - - // Try to get the same TileObject - TileObject to2 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - mbs.get(to2); - - // Checks on the format - assertEquals(to.getBlobFormat(), to2.getBlobFormat()); - - // Checks if the resources are equals - try (InputStream is = to.getBlob().getInputStream(); - InputStream is2 = to2.getBlob().getInputStream()) { - checkInputStreams(is, is2); - } - - // Ensure Cache contains the result - TileObject to3 = cache.getTileObj(to); - assertNotNull(to3); - assertEquals(to.getBlobFormat(), to3.getBlobFormat()); - - // Checks if the resources are equals - try (InputStream is = to.getBlob().getInputStream(); - InputStream is3 = to3.getBlob().getInputStream()) { - checkInputStreams(is, is3); - } - } - - @Test - public void testTileDelete() throws Exception { - // Add a fileblobstore to the memory blobstore - fbs = setup(); - cache.clear(); - - mbs = new MemoryBlobStore(); - mbs.setStore(fbs); - mbs.setCacheProvider(cache); - - Map parameters = new HashMap<>(); - parameters.put("a", "x"); - parameters.put("b", "ø"); - - // Put a TileObject - Resource bytes = new ByteArrayResource("1 2 3 4 5 6 test".getBytes()); - long[] xyz = {5L, 6L, 7L}; - TileObject to = TileObject.createCompleteTileObject( - "test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters, bytes); - - mbs.put(to); - - // Try to get the same TileObject - TileObject to2 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - mbs.get(to2); - - // Checks if the resources are equals - try (InputStream is = to2.getBlob().getInputStream(); - InputStream is2 = bytes.getInputStream()) { - checkInputStreams(is, is2); - } - - // Delete TileObject - TileObject to3 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - mbs.delete(to3); - - // Checks if the resource is not present - TileObject to4 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - assertFalse(mbs.get(to4)); - - // Ensure that cache does not contain the TileObject - TileObject to5 = cache.getTileObj(to); - assertNull(to5); - } - - @Test - public void testLastModifiedFromFilesystem() throws Exception { - // Add a fileblobstore to the memory blobstore - fbs = setup(); - cache.clear(); - - mbs = new MemoryBlobStore(); - mbs.setStore(fbs); - mbs.setCacheProvider(cache); - - // Put a TileObject - Resource bytes = new ByteArrayResource("1 2 3 4 5 6 test".getBytes()); - long[] xyz = {1L, 2L, 3L}; - Map parameters = new HashMap<>(); - parameters.put("a", "x"); - parameters.put("b", "ø"); - TileObject to = TileObject.createCompleteTileObject( - "test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters, bytes); - - mbs.put(to); - mbs.clear(); - - // Try to get the same TileObject twice with a cache cleanup in the middle - TileObject to2 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - mbs.get(to2); - mbs.clear(); - // wait a second to ensure we are not getting the same tile because the machine is just that - // fast - Thread.sleep(1000); - TileObject to3 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - mbs.get(to3); - - // check the last modified is the same - assertEquals(to2.getCreated(), to3.getCreated()); - } - - /** - * * Private method for creating a {@link FileBlobStore} - * - * @return a new FileBlobStore - */ - private BlobStore setup() throws Exception { - File fh = new File(StorageBrokerTest.findTempDir() + File.separator + TEST_BLOB_DIR_NAME); - - if (fh.exists()) { - FileUtils.deleteDirectory(fh); - } - if (!fh.exists()) { - Files.createDirectories(fh.toPath()); - } - - return new FileBlobStore(StorageBrokerTest.findTempDir() + File.separator + TEST_BLOB_DIR_NAME); - } - - /** Checks if the streams are equals, note that this method also closes the {@link InputStream} */ - @SuppressWarnings("PMD.UseTryWithResources") // provided as method params, handle in java 11 - private void checkInputStreams(InputStream is, InputStream is2) throws IOException { - try { - assertTrue(IOUtils.contentEquals(is, is2)); - } finally { - try { - is.close(); - } catch (IOException e) { - log.log(Level.SEVERE, e.getMessage(), e); - fail(); - } - try { - is2.close(); - } catch (IOException e) { - log.log(Level.SEVERE, e.getMessage(), e); - fail(); - } - } - } -} diff --git a/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreWithFilesComformanceTest.java b/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreWithFilesComformanceTest.java deleted file mode 100644 index 2ae0f498f1..0000000000 --- a/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/MemoryBlobStoreWithFilesComformanceTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - * @author Kevin Smith, Boundless, 2017 - */ -package org.geowebcache.storage.blobstore.memory; - -import org.geowebcache.storage.AbstractBlobStoreTest; -import org.geowebcache.storage.blobstore.file.FileBlobStore; -import org.junit.Before; -import org.junit.Rule; -import org.junit.rules.TemporaryFolder; - -public class MemoryBlobStoreWithFilesComformanceTest extends AbstractBlobStoreTest { - - @Override - public void createTestUnit() throws Exception { - this.store = new MemoryBlobStore(); - this.store.setStore(new FileBlobStore(temp.getRoot().getAbsolutePath())); - } - - @Before - public void setEvents() throws Exception { - this.events = false; - } - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); -} diff --git a/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/NullBlobStoreComformanceTest.java b/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/NullBlobStoreComformanceTest.java deleted file mode 100644 index 5db00f6191..0000000000 --- a/geowebcache/core/src/test/java/org/geowebcache/storage/blobstore/memory/NullBlobStoreComformanceTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - * @author Kevin Smith, Boundless, 2017 - */ -package org.geowebcache.storage.blobstore.memory; - -import org.geowebcache.storage.AbstractBlobStoreTest; -import org.geowebcache.storage.StorageException; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -public class NullBlobStoreComformanceTest extends AbstractBlobStoreTest { - - @Override - public void createTestUnit() throws Exception { - this.store = new NullBlobStore(); - } - - @Before - public void setEvents() throws Exception { - this.events = false; - } - - @Override - @Ignore - @Test - public void testStoreTile() throws Exception { - super.testStoreTile(); - } - - @Override - @Ignore - @Test - public void testStoreTilesInMultipleLayers() throws Exception { - super.testStoreTilesInMultipleLayers(); - } - - @Override - @Ignore - @Test - public void testUpdateTile() throws Exception { - super.testUpdateTile(); - } - - @Override - @Ignore - @Test - public void testGridsets() throws Exception { - super.testGridsets(); - } - - @Override - @Ignore - @Test - public void testDeleteGridsetDoesntDeleteOthers() throws Exception { - super.testDeleteGridset(); - } - - @Override - @Ignore - @Test - public void testDeleteByParametersId() throws Exception { - super.testDeleteByParametersId(); - } - - @Override - @Ignore - @Test - public void testParameters() throws Exception { - super.testParameters(); - } - - @Override - @Ignore - @Test - public void testParameterIDList() throws Exception { - super.testParameterIDList(); - } - - @Override - @Ignore - @Test - public void testParameterList() throws Exception { - super.testParameterList(); - } - - @Override - @Ignore - @Test - public void testDeleteByParametersIdDoesNotDeleteOthers() throws Exception { - super.testDeleteByParametersIdDoesNotDeleteOthers(); - } - - @Override - @Ignore - @Test - public void testPurgeOrphans() throws Exception { - super.testPurgeOrphans(); - } - - @Override - @Ignore - @Test - public void testPurgeOrphansWithDefault() throws Exception { - super.testPurgeOrphansWithDefault(); - } - - @Override - @Ignore - @Test - public void testDeleteRangeSingleLevel() throws StorageException { - super.testDeleteRangeSingleLevel(); - } - - @Override - @Ignore - @Test - public void testDeleteRangeMultiLevel() throws StorageException { - super.testDeleteRangeMultiLevel(); - } -} diff --git a/geowebcache/diskquota/core/src/main/java/org/geowebcache/diskquota/rest/controller/DiskQuotaController.java b/geowebcache/diskquota/core/src/main/java/org/geowebcache/diskquota/rest/controller/DiskQuotaController.java index 682326ada6..62a074f907 100644 --- a/geowebcache/diskquota/core/src/main/java/org/geowebcache/diskquota/rest/controller/DiskQuotaController.java +++ b/geowebcache/diskquota/core/src/main/java/org/geowebcache/diskquota/rest/controller/DiskQuotaController.java @@ -33,7 +33,6 @@ import org.geowebcache.diskquota.DiskQuotaConfig; import org.geowebcache.diskquota.DiskQuotaMonitor; import org.geowebcache.io.GeoWebCacheXStream; -import org.geowebcache.storage.blobstore.memory.CacheStatistics; import org.geowebcache.util.ApplicationContextProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -197,7 +196,6 @@ private ResponseEntity getXmlRepresentation(DiskQuotaConfig config) { */ public static XStream getConfiguredXStream(XStream xs) { xs.setMode(XStream.NO_REFERENCES); - xs.alias("gwcInMemoryCacheStatistics", CacheStatistics.class); return xs; } } diff --git a/geowebcache/distributed/pom.xml b/geowebcache/distributed/pom.xml deleted file mode 100644 index 1f6a6ca7f4..0000000000 --- a/geowebcache/distributed/pom.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - 4.0.0 - - org.geowebcache - geowebcache - 1.28-SNAPSHOT - - - org.geowebcache - gwc-distributed - jar - gwc-distributed - https://geowebcache.osgeo.org - - - - com.hazelcast - hazelcast - ${hazelcast.version} - - - com.hazelcast - hazelcast-spring - ${hazelcast.version} - - - org.geowebcache - gwc-core - ${project.version} - - - org.geotools - gt-xml - - - junit - junit - test - - - com.hazelcast - hazelcast - ${hazelcast.version} - tests - test - - - - - - - false - - central - Central Maven Repository - https://repo1.maven.org/maven2 - - - diff --git a/geowebcache/distributed/src/main/java/org/geowebcache/storage/blobstore/memory/distributed/HazelcastCacheProvider.java b/geowebcache/distributed/src/main/java/org/geowebcache/storage/blobstore/memory/distributed/HazelcastCacheProvider.java deleted file mode 100644 index 9fc4f914c9..0000000000 --- a/geowebcache/distributed/src/main/java/org/geowebcache/storage/blobstore/memory/distributed/HazelcastCacheProvider.java +++ /dev/null @@ -1,320 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - *

Copyright 2019 - */ -package org.geowebcache.storage.blobstore.memory.distributed; - -import com.hazelcast.map.EntryProcessor; -import com.hazelcast.map.IMap; -import com.hazelcast.map.LocalMapStats; -import com.hazelcast.query.Predicate; -import com.hazelcast.query.PredicateBuilder; -import com.hazelcast.query.impl.PredicateBuilderImpl; -import java.util.Collections; -import java.util.List; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.geotools.util.logging.Logging; -import org.geowebcache.storage.TileObject; -import org.geowebcache.storage.blobstore.memory.CacheConfiguration; -import org.geowebcache.storage.blobstore.memory.CacheConfiguration.EvictionPolicy; -import org.geowebcache.storage.blobstore.memory.CacheProvider; -import org.geowebcache.storage.blobstore.memory.CacheStatistics; -import org.geowebcache.storage.blobstore.memory.guava.GuavaCacheProvider; -import org.springframework.beans.factory.DisposableBean; - -/** - * This class is an implementation of the {@link CacheProvider} interface for a distributed configuration using - * Hazelcast. This class requires a configuration at the GWC startup. The Hazelcast instance used is returned by the - * {@link HazelcastLoader} object which internally handles the configuration for the cache. Note that this cache does - * not provide access to all the local statistics parameters so some of them will return -1 as result. There could - * happen that the number cache HITS is bigger than the number of total operations. This is caused by the fact that HITS - * number indicates the number of hits on the local entries considering also the requests made by other cluster - * instances while the total operation count indicates only the number of GET operations requested on the local cluster - * instance. - * - * @author Nicola Lagomarsini Geosolutions - */ -public class HazelcastCacheProvider implements CacheProvider, DisposableBean { - - /** {@link Logger} object used for logging operations */ - private static final Logger LOGGER = Logging.getLogger(HazelcastCacheProvider.class.getName()); - - /** Fixed name for the Hazelcast map */ - public static final String HAZELCAST_MAP_DEFINITION = "CacheProviderMap"; - - /** Converter from Mb to Bytes */ - public static final long MB_TO_BYTES = 1048576; - - /** Name of the {@link CacheProvider} used as Label */ - private static final String HAZELCAST_NAME = "Hazelcast Cache"; - - /** Hazelcast {@link IMap} */ - private final IMap map; - - /** Boolean indicating that the Cache has been configured */ - private final boolean configured; - - /** Long value indicating the total size in Bytes */ - private final long totalSize; - - public HazelcastCacheProvider(HazelcastLoader loader) { - configured = loader.isConfigured(); - // If the Hazelcast instance is configured, then the other - // cacheProvider parameters are defined - if (configured) { - map = loader.getInstance().getMap(HAZELCAST_MAP_DEFINITION); - totalSize = loader.getInstance() - .getConfig() - .getMapConfig(HAZELCAST_MAP_DEFINITION) - .getEvictionConfig() - .getSize() - * MB_TO_BYTES; - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Configured Hazelcast Cache"); - } - } else { - map = null; - totalSize = 0; - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Hazelcast Cache not configured"); - } - } - } - - public HazelcastCacheProvider(IMap map, long totalSize) { - this.map = map; - this.totalSize = totalSize; - this.configured = true; - } - - @Override - public TileObject getTileObj(TileObject obj) { - if (configured) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Getting TileObject:" + obj); - } - String key = GuavaCacheProvider.generateTileKey(obj); - return map.get(key); - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache not configured"); - } - return null; - } - } - - @Override - public void putTileObj(TileObject obj) { - if (configured) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Adding TileObject:" + obj); - } - String key = GuavaCacheProvider.generateTileKey(obj); - map.put(key, obj); - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache not configured"); - } - } - } - - @Override - public void removeTileObj(TileObject obj) { - if (configured) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Removing TileObject:" + obj); - } - String key = GuavaCacheProvider.generateTileKey(obj); - map.remove(key); - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache not configured"); - } - } - } - - @Override - public void removeLayer(String layername) { - if (configured) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Removing Layer:" + layername); - } - // Creation of the Predicate - PredicateBuilder.EntryObject e = new PredicateBuilderImpl().getEntryObject(); - @SuppressWarnings("unchecked") - Predicate predicate = - (Predicate) e.get("layer_name").equal(layername); - // Creation of the processor - CacheEntryProcessor entryProcessor = new CacheEntryProcessor(); - // Execution of the Processor - map.executeOnEntries(entryProcessor, predicate); - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache not configured"); - } - } - } - - @Override - public void clear() { - if (configured) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Clearing cache"); - } - map.clear(); - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache not configured"); - } - } - } - - @Override - public void reset() { - if (configured) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Resetting cache"); - } - map.clear(); - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache not configured"); - } - } - } - - @Override - public void destroy() throws Exception { - if (configured) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Destroying cache"); - } - map.destroy(); - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache not configured"); - } - } - } - - @Override - public CacheStatistics getStatistics() { - if (configured) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Getting cache statistics"); - } - // Getting statistics and then creating a new HazelcastCacheStatistics instance - LocalMapStats localMapStats = map.getLocalMapStats(); - CacheStatistics stats = new HazelcastCacheStatistics(localMapStats, totalSize); - return stats; - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Cache not configured"); - } - } - return new CacheStatistics(); - } - - @Override - public void configure(CacheConfiguration configuration) {} - - @Override - public void addUncachedLayer(String layername) {} - - @Override - public void removeUncachedLayer(String layername) {} - - @Override - public boolean containsUncachedLayer(String layername) { - return false; - } - - @Override - public List getSupportedPolicies() { - return Collections.emptyList(); - } - - @Override - public boolean isImmutable() { - return true; - } - - @Override - public boolean isAvailable() { - return configured; - } - - @Override - public String getName() { - return HAZELCAST_NAME; - } - - /** - * {@link CacheStatistics} extensions used for handling local map statistics - * - * @author Nicola Lagomarsini Geosolutions - */ - static class HazelcastCacheStatistics extends CacheStatistics { - - public HazelcastCacheStatistics(LocalMapStats localMapStats, long totalSize) { - // Note that HITS indicates all the hits to the local entries, even if the request - // is made from another cluster instance - long hits = localMapStats.getHits(); - setHitCount(hits); - // Total indicates the total number of the GET operations made by the local cache - long total = localMapStats.getGetOperationCount(); - // Miss count not defined - setMissCount(-1); - setTotalCount(total); - // If miss is not present, rate cannot be calculated - double hitRate = -1; - double missRate = -1; - setHitRate(hitRate); - setMissRate(missRate); - // Setting total cache size - setTotalSize(totalSize); - // Setting actual size - long actualSize = localMapStats.getOwnedEntryMemoryCost(); - setActualSize(actualSize); - // Calculation of the memory occupation - int currentMemoryOccupation = (int) (100L - (1L) * (100 * ((1.0d) * (totalSize - actualSize)) / totalSize)); - if (currentMemoryOccupation < 0) { - currentMemoryOccupation = 0; - } - setCurrentMemoryOccupation(currentMemoryOccupation); - // Eviction count not defined - setEvictionCount(-1); - } - } - - /** - * {@link EntryProcessor} implementation used for removing defined entries - * - * @author Nicola Lagomarsini Geosolutions - */ - static class CacheEntryProcessor implements EntryProcessor { - - @Override - public Object process(Entry entry) { - // By setting the entry value to null the entry is evicted - entry.setValue(null); - return null; - } - - @Override - public EntryProcessor getBackupProcessor() { - return null; - } - } -} diff --git a/geowebcache/distributed/src/main/java/org/geowebcache/storage/blobstore/memory/distributed/HazelcastLoader.java b/geowebcache/distributed/src/main/java/org/geowebcache/storage/blobstore/memory/distributed/HazelcastLoader.java deleted file mode 100644 index 84a1597a3d..0000000000 --- a/geowebcache/distributed/src/main/java/org/geowebcache/storage/blobstore/memory/distributed/HazelcastLoader.java +++ /dev/null @@ -1,172 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - *

Copyright 2019 - */ -package org.geowebcache.storage.blobstore.memory.distributed; - -import com.hazelcast.config.Config; -import com.hazelcast.config.MapConfig; -import com.hazelcast.config.MaxSizePolicy; -import com.hazelcast.config.NearCacheConfig; -import com.hazelcast.config.XmlConfigBuilder; -import com.hazelcast.core.Hazelcast; -import com.hazelcast.core.HazelcastInstance; -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.commons.io.filefilter.NameFileFilter; -import org.geotools.util.logging.Logging; -import org.geotools.xml.XMLUtils; -import org.springframework.beans.factory.InitializingBean; - -/** - * This class is used for handling configuration of the Hazelcast cluster. User can directly inject an Hazelcast - * instance or can setup a file called hazelcast.xml and define its directory with the hazelcast.config.dir Java - * property. Note that the configuration must contain a map with name "CacheProviderMap" with a specific size in MB, an - * eviction policy equal to LRU or LFU. Also if NearCache is enabled, user must be careful that the max size is not - * bigger or equal to Integer.MAX_VALUE - * - * @author Nicola Lagomarsini Geosolutions - */ -public class HazelcastLoader implements InitializingBean { - /** {@link Logger} object used for logging exceptions */ - private static final Logger LOGGER = Logging.getLogger(HazelcastLoader.class.getName()); - - /** Property name for the Hazelcast property file */ - public static final String HAZELCAST_CONFIG_DIR = "hazelcast.config.dir"; - - /** Name of the Hazelcast XML file to use */ - public static final String HAZELCAST_NAME = "hazelcast.xml"; - - /** Hazelcast instance to pass to the {@link HazelcastCacheProvider} class */ - private HazelcastInstance instance; - - // Disable Hazelcast's XXE protection if the XML libraries don't support JAXP 1.5 - static { - if (System.getProperty("hazelcast.ignoreXxeProtectionFailures") == null) { - try { - XMLUtils.checkSupportForJAXP15Properties(); - } catch (IllegalStateException e) { - LOGGER.warning("Disabling Hazelcast XXE protection because " + e.getMessage()); - System.setProperty("hazelcast.ignoreXxeProtectionFailures", "true"); - } - } - } - - @Override - public void afterPropertiesSet() throws Exception { - if (instance == null) { - // Search for the Hazelcast configuration directory - String hazelDirPath = System.getProperty(HAZELCAST_CONFIG_DIR); - if (hazelDirPath != null) { - File hazelCastDir = new File(hazelDirPath); - if (hazelCastDir.exists() && hazelCastDir.isDirectory() && hazelCastDir.canRead()) { - FileFilter filter = new NameFileFilter(HAZELCAST_NAME); - File[] files = hazelCastDir.listFiles(filter); - if (files != null && files.length > 0) { - File hazelCastConf = files[0]; - Config config = null; - try (InputStream stream = new FileInputStream(hazelCastConf)) { - config = new XmlConfigBuilder(stream).build(); - } - // Ensure the configuration is accepted - if (configAccepted(config)) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Hazelcast instance validated"); - } - instance = Hazelcast.newHazelcastInstance(config); - } else { - if (LOGGER.isLoggable(Level.INFO)) { - LOGGER.info("No mapping for CacheProvider Map is present"); - } - } - } - } - } - } else if (!configAccepted(instance.getConfig())) { - instance = null; - } - if (LOGGER.isLoggable(Level.FINE) && instance == null) { - LOGGER.fine("Hazelcast instance invalid or not found"); - } - } - - /** - * Indicates if the Hazelcast instance has been configured - * - * @return a boolean indicating if hazelcast instance can be used or not - */ - public boolean isConfigured() { - return instance != null; - } - - /** Setter for the Hazelcast instance */ - public void setInstance(HazelcastInstance instance) { - this.instance = instance; - } - - /** - * Returns the Hazelcast instance to use - * - * @return Hazelcast instance if present or null - */ - public HazelcastInstance getInstance() { - return isConfigured() ? instance : null; - } - - /** - * Validation for an input {@link Config} object provided. This method ensures that the input configuration contains - * a map with name "CacheProviderMap", contains a size configuration in Mb and related to the used Heap size and has - * an eviction policy equal to LRU or LFU. If a NearCache object is defined it cannot have max size greater or equal - * to {@link Integer}.MAX_VALUE - */ - private boolean configAccepted(Config config) { - boolean configAccepted = false; - if (config != null) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Checking configuration"); - } - // Check if the cache map is present - if (config.getMapConfigs().containsKey(HazelcastCacheProvider.HAZELCAST_MAP_DEFINITION)) { - MapConfig mapConfig = config.getMapConfig(HazelcastCacheProvider.HAZELCAST_MAP_DEFINITION); - // Check size policy - boolean sizeDefined = mapConfig.getEvictionConfig().getSize() > 0; - boolean policyExists = - mapConfig.getEvictionConfig().getEvictionPolicy() != MapConfig.DEFAULT_EVICTION_POLICY; - boolean sizeFromHeap = mapConfig.getEvictionConfig().getMaxSizePolicy() == MaxSizePolicy.USED_HEAP_SIZE; - - // Check Near Cache size - boolean nearCacheAccepted = true; - if (mapConfig.getNearCacheConfig() != null) { - NearCacheConfig conf = mapConfig.getNearCacheConfig(); - nearCacheAccepted = conf.getEvictionConfig().getSize() < Integer.MAX_VALUE; - } - - if (sizeDefined && policyExists && sizeFromHeap && nearCacheAccepted) { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Hazelcast config validated"); - } - configAccepted = true; - } - } - } else { - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("No configuration provided"); - } - } - - return configAccepted; - } -} diff --git a/geowebcache/distributed/src/test/java/org/geowebcache/blobstore/memory/distributed/HazelcastCacheProviderTest.java b/geowebcache/distributed/src/test/java/org/geowebcache/blobstore/memory/distributed/HazelcastCacheProviderTest.java deleted file mode 100644 index ec4389d381..0000000000 --- a/geowebcache/distributed/src/test/java/org/geowebcache/blobstore/memory/distributed/HazelcastCacheProviderTest.java +++ /dev/null @@ -1,245 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - */ -package org.geowebcache.blobstore.memory.distributed; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import com.hazelcast.config.Config; -import com.hazelcast.config.TcpIpConfig; -import com.hazelcast.core.Hazelcast; -import com.hazelcast.core.HazelcastInstance; -import java.io.IOException; -import java.io.InputStream; -import java.net.UnknownHostException; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.apache.commons.io.IOUtils; -import org.geotools.util.logging.Logging; -import org.geowebcache.io.ByteArrayResource; -import org.geowebcache.io.Resource; -import org.geowebcache.storage.TileObject; -import org.geowebcache.storage.blobstore.memory.MemoryBlobStore; -import org.geowebcache.storage.blobstore.memory.NullBlobStore; -import org.geowebcache.storage.blobstore.memory.distributed.HazelcastCacheProvider; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -/** - * This test class is used for testing {@link HazelcastCacheProvider} functionalities. - * - * @author Nicola Lagomarsini Geosolutions - */ -public class HazelcastCacheProviderTest { - - /** LOGGER */ - public static final Logger LOG = Logging.getLogger(HazelcastCacheProviderTest.class.getName()); - - /** Name of the application context */ - public static final String APP_CONTEXT_FILENAME = "applicationContextTest.xml"; - - /** Name of the bean associated to the first cache */ - public static final String CACHE_1_NAME = "HazelCastCacheProvider1"; - - /** Name of the bean associated to the first cache */ - public static final String CACHE_2_NAME = "HazelCastCacheProvider2"; - - /** Cache object 1 */ - private static HazelcastCacheProvider cache1; - - /** Cache object 2 */ - private static HazelcastCacheProvider cache2; - - /** First {@link MemoryBlobStore} instance used for tests */ - private static MemoryBlobStore mem1; - - /** Second {@link MemoryBlobStore} instance used for tests */ - private static MemoryBlobStore mem2; - - @BeforeClass - public static void initialSetup() throws UnknownHostException { - Config config = new Config(); - config.getMapConfig("default").setBackupCount(1).setAsyncBackupCount(0); - config.setClusterName("gwc"); - TcpIpConfig tcpIpConfig = config.getNetworkConfig().getJoin().getTcpIpConfig(); - tcpIpConfig.setEnabled(true); - tcpIpConfig.getMembers().add("localhost"); - HazelcastInstance h1 = Hazelcast.newHazelcastInstance(config); - HazelcastInstance h2 = Hazelcast.newHazelcastInstance(config); - // Create a nullblobstore to add to the memory blobstore - NullBlobStore nbs = new NullBlobStore(); - - mem1 = new MemoryBlobStore(); - mem1.setStore(nbs); - cache1 = new HazelcastCacheProvider(h1.getMap("map1"), 16); - mem1.setCacheProvider(cache1); - - mem2 = new MemoryBlobStore(); - mem2.setStore(nbs); - cache2 = new HazelcastCacheProvider(h2.getMap("map1"), 16); - mem2.setCacheProvider(cache2); - - // Ensure both the caches are available and immutable - assertTrue(HazelcastCacheProviderTest.cache1.isAvailable()); - assertTrue(HazelcastCacheProviderTest.cache1.isImmutable()); - assertTrue(HazelcastCacheProviderTest.cache2.isAvailable()); - assertTrue(HazelcastCacheProviderTest.cache2.isImmutable()); - } - - @Test - public void testTilePut() throws Exception { - // Clearing cache - cache1.clear(); - - // Ensure that both caches are cleared - assertEquals(0, cache1.getStatistics().getActualSize()); - assertEquals(0, cache2.getStatistics().getActualSize()); - - // Put a TileObject - Resource bytes = new ByteArrayResource("1 2 3 4 5 6 test".getBytes()); - long[] xyz = {1L, 2L, 3L}; - Map parameters = new HashMap<>(); - parameters.put("a", "x"); - parameters.put("b", "ø"); - TileObject to = TileObject.createCompleteTileObject( - "test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters, bytes); - - mem1.put(to); - - // Try to get the same TileObject - TileObject to2 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - assertTrue(mem2.get(to2)); - - // Checks on the format - assertEquals(to.getBlobFormat(), to2.getBlobFormat()); - - // Checks if the resources are equals - try (InputStream is = to.getBlob().getInputStream(); - InputStream is2 = to2.getBlob().getInputStream()) { - checkInputStreams(is, is2); - } - - // Ensure Caches contain the result - TileObject to3 = cache1.getTileObj(to); - assertNotNull(to3); - - // Checks if the resources are equals - try (InputStream is = to.getBlob().getInputStream(); - InputStream is3 = to3.getBlob().getInputStream()) { - checkInputStreams(is, is3); - } - - TileObject to4 = cache2.getTileObj(to); - assertNotNull(to4); - - // Checks if the resources are equals - try (InputStream is = to.getBlob().getInputStream(); - InputStream is4 = to4.getBlob().getInputStream()) { - checkInputStreams(is, is4); - } - } - - @Test - public void testTileDelete() throws Exception { - // Clearing cache - cache1.clear(); - - // Ensure that both caches are cleared - assertEquals(0, cache1.getStatistics().getActualSize()); - assertEquals(0, cache2.getStatistics().getActualSize()); - - Map parameters = new HashMap<>(); - parameters.put("a", "x"); - parameters.put("b", "y"); - - // Put a TileObject - Resource bytes = new ByteArrayResource("1 2 3 4 5 6 test".getBytes()); - long[] xyz = {5L, 6L, 7L}; - TileObject to = TileObject.createCompleteTileObject( - "test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters, bytes); - - mem1.put(to); - - // Try to get the same TileObject - TileObject to2 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - assertTrue(mem2.get(to2)); - - // Checks if the resources are equals - try (InputStream is = to2.getBlob().getInputStream(); - InputStream is2 = bytes.getInputStream()) { - checkInputStreams(is, is2); - } - - // Delete TileObject - TileObject to3 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - mem1.delete(to3); - - // Checks if the resource is not present - TileObject to4 = - TileObject.createQueryTileObject("test:123123 112", xyz, "EPSG:4326", "image/jpeg", parameters); - assertFalse(mem1.get(to4)); - assertFalse(mem2.get(to4)); - - // Ensure that caches do not contain the TileObject - TileObject to5 = cache1.getTileObj(to); - assertNull(to5); - TileObject to6 = cache2.getTileObj(to); - assertNull(to6); - } - - @AfterClass - public static void afterClass() throws Exception { - // Blobstore destroy - mem1.destroy(); - mem2.destroy(); - - // Cache destruction - cache1.destroy(); - cache2.destroy(); - - // tear down hazelcast itself - Hazelcast.shutdownAll(); - } - - /** Checks if the streams are equals, note that this method also closes the {@link InputStream} */ - @SuppressWarnings("PMD.UseTryWithResources") // resources not created here - private void checkInputStreams(InputStream is, InputStream is2) throws IOException { - try { - // Ensure the two contents are equal - assertTrue(IOUtils.contentEquals(is, is2)); - } finally { - // Closing streams - try { - is.close(); - } catch (IOException e) { - LOG.log(Level.SEVERE, e.getMessage(), e); - fail(); - } - try { - is2.close(); - } catch (IOException e) { - LOG.log(Level.SEVERE, e.getMessage(), e); - fail(); - } - } - } -} diff --git a/geowebcache/distributed/src/test/resources/logging.properties b/geowebcache/distributed/src/test/resources/logging.properties deleted file mode 100644 index d2ddbda2b1..0000000000 --- a/geowebcache/distributed/src/test/resources/logging.properties +++ /dev/null @@ -1,10 +0,0 @@ -handlers=java.util.logging.ConsoleHandler - -java.util.logging.ConsoleHandler.level = INFO -java.util.logging.ConsoleHandler.formatter = org.geotools.util.logging.MonolineFormatter -org.geotools.util.logging.MonolineFormatter.source = logger:long - -.level= INFO -org.geotools.level = WARNING -org.geowebcache.level = INFO -org.geowebcache.config.XMLConfiguration.level = INFO \ No newline at end of file diff --git a/geowebcache/pom.xml b/geowebcache/pom.xml index 6526f34c73..92f4ff24e2 100644 --- a/geowebcache/pom.xml +++ b/geowebcache/pom.xml @@ -23,7 +23,6 @@ web diskquota arcgiscache - distributed s3storage sqlite azureblob diff --git a/geowebcache/rest/src/main/java/org/geowebcache/rest/controller/MemoryCacheController.java b/geowebcache/rest/src/main/java/org/geowebcache/rest/controller/MemoryCacheController.java deleted file mode 100644 index dbe9ed3138..0000000000 --- a/geowebcache/rest/src/main/java/org/geowebcache/rest/controller/MemoryCacheController.java +++ /dev/null @@ -1,149 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - * @author David Vick / Boundless 2017 - *

Original files MemoryCacheStatsFinder.java MemoryCacheStatsResource.java - */ -package org.geowebcache.rest.controller; - -import com.thoughtworks.xstream.XStream; -import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.servlet.http.HttpServletRequest; -import org.geotools.util.logging.Logging; -import org.geowebcache.config.ContextualConfigurationProvider.Context; -import org.geowebcache.config.XMLConfiguration; -import org.geowebcache.io.GeoWebCacheXStream; -import org.geowebcache.storage.BlobStore; -import org.geowebcache.storage.DefaultStorageBroker; -import org.geowebcache.storage.StorageBroker; -import org.geowebcache.storage.blobstore.memory.CacheStatistics; -import org.geowebcache.storage.blobstore.memory.MemoryBlobStore; -import org.geowebcache.util.ApplicationContextProvider; -import org.json.JSONException; -import org.json.JSONObject; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.WebApplicationContext; - -@Component -@RestController -@RequestMapping(path = "${gwc.context.suffix:}/rest") -public class MemoryCacheController { - /** {@link Log} used for logging the exceptions */ - public static Logger log = Logging.getLogger(MemoryCacheController.class.getName()); - - /** Store associated to the StorageBroker to use */ - @Autowired - StorageBroker broker; - - private WebApplicationContext context; - - @Autowired - public MemoryCacheController(ApplicationContextProvider appCtx) { - context = appCtx == null ? null : appCtx.getApplicationContext(); - } - - /** BlobStore used for getting statistics */ - private BlobStore store; - - // set by spring - public void setStorageBroker(StorageBroker broker) { - this.broker = broker; - } - - public void setContext(WebApplicationContext context) { - this.context = context; - } - - /** Setter for the BlobStore */ - public void setBlobStore(BlobStore store) { - this.store = store; - } - - @RequestMapping(value = "/statistics", method = RequestMethod.GET) - public ResponseEntity doGet(HttpServletRequest request) { - ResponseEntity entity; - - if (log.isLoggable(Level.FINE)) { - log.fine("Getting BlobStore from the storage broker"); - } - - // Getting the BlobStore if present - if (broker instanceof DefaultStorageBroker storageBroker) { - store = storageBroker.getBlobStore(); - } - - if (store != null && store instanceof MemoryBlobStore memoryStore) { - if (log.isLoggable(Level.FINE)) { - log.fine("Memory Blobstore found, now getting statistics"); - } - CacheStatistics stats = memoryStore.getCacheStatistics(); - CacheStatistics statistics = new CacheStatistics(stats); - - if (request.getPathInfo().contains("json")) { - try { - entity = getJsonRepresentation(statistics); - } catch (JSONException e) { - entity = new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } - } else { - entity = getXmlRepresentation(statistics); - } - } else { - entity = new ResponseEntity<>( - "No statistics available for the current BlobStore: " + (store != null ? store.getClass() : null), - HttpStatus.NOT_FOUND); - } - return entity; - } - - /** - * Private method for retunring a JSON representation of the Statistics - * - * @return a {@link ResponseEntity} object - */ - private ResponseEntity getJsonRepresentation(CacheStatistics stats) throws JSONException { - XStream xs = XMLConfiguration.getConfiguredXStreamWithContext( - new GeoWebCacheXStream(new JsonHierarchicalStreamDriver()), context, Context.REST); - JSONObject obj = new JSONObject(xs.toXML(stats)); - return new ResponseEntity<>(obj.toString(), HttpStatus.OK); - } - - /** - * Private method for retunring an XML representation of the Statistics - * - * @return a {@link ResponseEntity} object - */ - private ResponseEntity getXmlRepresentation(CacheStatistics stats) { - XStream xStream = getConfiguredXStream(new GeoWebCacheXStream()); - String xmlText = "\n" + xStream.toXML(stats); - - return new ResponseEntity<>(xmlText, HttpStatus.OK); - } - - /** - * This method adds to the input {@link XStream} an alias for the CacheStatistics - * - * @return an updated XStream - */ - public static XStream getConfiguredXStream(XStream xs) { - xs.setMode(XStream.NO_REFERENCES); - xs.alias("gwcInMemoryCacheStatistics", CacheStatistics.class); - return xs; - } -} diff --git a/geowebcache/rest/src/test/java/org/geowebcache/rest/statistics/MemoryCacheControllerTest.java b/geowebcache/rest/src/test/java/org/geowebcache/rest/statistics/MemoryCacheControllerTest.java deleted file mode 100644 index 6d7f708205..0000000000 --- a/geowebcache/rest/src/test/java/org/geowebcache/rest/statistics/MemoryCacheControllerTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General - * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - *

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - *

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see - * . - * - * @author David Vick, Boundless, 2017 - */ -package org.geowebcache.rest.statistics; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import java.util.Collections; -import org.geowebcache.GeoWebCacheException; -import org.geowebcache.config.DefaultGridsets; -import org.geowebcache.config.MockConfigurationResourceProvider; -import org.geowebcache.config.XMLConfiguration; -import org.geowebcache.config.XMLConfigurationBackwardsCompatibilityTest; -import org.geowebcache.grid.GridSetBroker; -import org.geowebcache.rest.controller.MemoryCacheController; -import org.geowebcache.storage.blobstore.memory.CacheConfiguration; -import org.geowebcache.storage.blobstore.memory.CacheProvider; -import org.geowebcache.storage.blobstore.memory.MemoryBlobStore; -import org.geowebcache.storage.blobstore.memory.NullBlobStore; -import org.geowebcache.storage.blobstore.memory.guava.GuavaCacheProvider; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration({"file*:/webapp/WEB-INF/web.xml", "file*:/webapp/WEB-INF/geowebcache-servlet.xml"}) -public class MemoryCacheControllerTest { - private MockMvc mockMvc; - - MemoryCacheController mcc; - - @Before - @SuppressWarnings("deprecation") // setUseSuffixPatternMatch is deprecated because Spring wants to - // discourage extensions in paths - public void setup() throws GeoWebCacheException { - GridSetBroker gridSetBroker = new GridSetBroker(Collections.singletonList(new DefaultGridsets(false, false))); - XMLConfiguration xmlConfig = loadXMLConfig(); - xmlConfig.setGridSetBroker(gridSetBroker); - xmlConfig.afterPropertiesSet(); - - mcc = new MemoryCacheController(null); - this.mockMvc = MockMvcBuilders.standaloneSetup(mcc) - .setUseSuffixPatternMatch(true) - .build(); - } - - @Test - public void testStatisticsXml() throws Exception { - // Initialize a new MemoryBlobStore with cache - CacheProvider cache = new GuavaCacheProvider(new CacheConfiguration()); - NullBlobStore nbs = new NullBlobStore(); - cache.clear(); - - MemoryBlobStore mbs = new MemoryBlobStore(); - mbs.setStore(nbs); - mcc.setBlobStore(mbs); - mbs.setCacheProvider(cache); - - this.mockMvc - .perform(get("/rest/statistics").accept("application/xml").contextPath("")) - .andExpect(status().is2xxSuccessful()); - } - - @Test - public void testStatisticsJson() throws Exception { - // Initialize a new MemoryBlobStore with cache - CacheProvider cache = new GuavaCacheProvider(new CacheConfiguration()); - NullBlobStore nbs = new NullBlobStore(); - cache.clear(); - - MemoryBlobStore mbs = new MemoryBlobStore(); - mbs.setStore(nbs); - mcc.setBlobStore(mbs); - mbs.setCacheProvider(cache); - - this.mockMvc - .perform(get("/rest/statistics").accept("application/json").contextPath("")) - .andExpect(status().is2xxSuccessful()); - } - - private XMLConfiguration loadXMLConfig() { - - XMLConfiguration xmlConfig = null; - try { - xmlConfig = new XMLConfiguration( - null, - new MockConfigurationResourceProvider(() -> XMLConfiguration.class.getResourceAsStream( - XMLConfigurationBackwardsCompatibilityTest.GWC_125_CONFIG_FILE))); - } catch (Exception e) { - // Do nothing - } - - return xmlConfig; - } -} diff --git a/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-core-context.xml b/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-core-context.xml index d1845c40fc..61d4a7caa8 100644 --- a/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-core-context.xml +++ b/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-core-context.xml @@ -215,21 +215,4 @@ - - - - - - - - - - - - - - - - - diff --git a/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-distributed.xml b/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-distributed.xml deleted file mode 100644 index 8552e3a6ee..0000000000 --- a/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-distributed.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - Bean configuration file for the gwc-web module - - - - - - - - - diff --git a/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-servlet.xml b/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-servlet.xml index 6cba071580..065c91f9e3 100644 --- a/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-servlet.xml +++ b/geowebcache/web/src/main/webapp/WEB-INF/geowebcache-servlet.xml @@ -26,7 +26,6 @@ - diff --git a/geowebcache/web/src/test/java/org/geowebcache/jetty/RestIntegrationTest.java b/geowebcache/web/src/test/java/org/geowebcache/jetty/RestIntegrationTest.java index 1643f5be35..f3861bdf2a 100644 --- a/geowebcache/web/src/test/java/org/geowebcache/jetty/RestIntegrationTest.java +++ b/geowebcache/web/src/test/java/org/geowebcache/jetty/RestIntegrationTest.java @@ -528,9 +528,6 @@ public void testInvalidMethods() throws Exception { new HttpDelete(jetty.getUri().resolve("rest/diskquota")), new HttpPut(jetty.getUri().resolve("rest/masstruncate")), new HttpDelete(jetty.getUri().resolve("rest/masstruncate")), - new HttpPut(jetty.getUri().resolve("rest/statistics")), - new HttpPost(jetty.getUri().resolve("rest/statistics")), - new HttpDelete(jetty.getUri().resolve("rest/statistics")), new HttpPut(jetty.getUri().resolve("rest/reload")), new HttpGet(jetty.getUri().resolve("rest/reload")), new HttpDelete(jetty.getUri().resolve("rest/reload")))) {