001/*
002 *  Copyright 2019 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.core.cache;
017
018import java.util.HashMap;
019import java.util.Map;
020import java.util.function.Function;
021
022import org.slf4j.Logger;
023
024import org.ametys.core.cache.AbstractCacheManager.CacheType;
025import org.ametys.plugins.core.impl.cache.AbstractCacheKey;
026import org.ametys.runtime.i18n.I18nizableText;
027
028/**
029 * A cache represents a store of objects indexed with a key
030 * @param <K> the type of the keys in cache
031 * @param <V> the type of the values in cache
032 */
033public interface Cache<K, V>
034{
035    /**
036     * Returns the value associated with {@code key} in this cache if it exist,
037     * obtaining that value from given function if necessary and store it.
038     * @param key the key to get the element
039     * @param function the function to compute the value if key is not present
040     * @return V the value associated with key
041     * @throws CacheException throw CacheException if the key is null or invalid, or if failed to compute the new value
042     */
043    public V get(K key, Function<K, V> function) throws CacheException;
044
045    /**
046     * Returns the value associated with {@code key} in this cache if it exist
047     * @param key the key to get the element
048     * @return V the value linked to key
049     */
050    public V get(K key);
051
052    /**
053     * Get the cache as a simple Map, filtered by filled values of the cache key
054     * @param key the key to get the element.
055     * @return the Map&lt;K, V&gt; representing the cache
056     */
057    public Map<K, V> getAll(AbstractCacheKey key);
058    
059    /**
060     * Associates {@code value} with {@code key} in this cache. If the cache
061     * previously contained a value associated with {@code key}, the old value
062     * is replaced by {@code value}. The {@code key} can not be null.
063     * @param key the key to get the element
064     * @param value the value associated with key
065     */
066    public void put(K key, V value);
067
068    /**
069     * Copies all of the mappings from the specified map to the cache.
070     * @param map map of key/values to be stored in the cache
071     */
072    public void putAll(Map<K, V> map);
073
074    /**
075     * return true if key is present in cache
076     * @param key the key to be checked
077     * @return true if key is present in cache
078     */
079    public boolean hasKey(K key);
080
081    /**
082     * Discards any cached value for key {@code key}.
083     * @param key the key to invalidate
084     */
085    public void invalidate(K key);
086
087    /**
088     * Discards all entries in the cache, without reseting statistics
089     */
090    public void invalidateAll();
091
092    /**
093     * Create or reset the whole cache (including statistics).<br>
094     * The CACHE_RESET event is not notified by this method, but should be by callers.
095     */
096    public void resetCache();
097
098    /**
099     * Get all statistics about cache
100     * @return statistic about the cache (hit, miss, evictions...)
101     */
102    public CacheStats getCacheStats();
103
104    /**
105     * Get the size the cache take in bytes
106     * @return the size of the cache if it's computable, return 0 otherwise
107     * @throws CacheException throw CacheException if memory size can not be computed
108     */
109    public long getMemorySize() throws CacheException;
110
111    /**
112     * Get the max size allocated to the cache, in bytes. 
113     * This value can be set to Long.MAX_VALUE for infinite cache
114     * @return the max size allocated to the cache
115     */
116    public long getMaxSize();
117
118    /**
119     * Get the number of values in cache
120     * @return the number of values in cache
121     */
122    public long getNumberOfElements();
123
124    /**
125     * Get the label of cache
126     * @return the label of cache
127     */
128    public I18nizableText getLabel();
129
130    /**
131     * Get the description of cache
132     * @return the description of cache
133     */
134    public I18nizableText getDescription();
135
136    /**
137     * Get the id of cache
138     * @return the id of cache
139     */
140    public String getId();
141
142    /**
143     * Is this cache size computable
144     * @return true if the cache size is computable
145     */
146    public boolean isComputableSize();
147
148    /**
149     * Get the cache as a simple Map
150     * @return the Map&lt;K, V&gt; representing the cache
151     */
152    public Map<K, V> asMap();
153    
154    /**
155     * Get the definition in JSON Map
156     * @param logger Logger to log an error when the size cannot be computed
157     * @return the JSON for the cache
158     */
159    public default Map<String, Object> toJSONMap(Logger logger)
160    {
161        Map<String, Object> properties = new HashMap<>();
162        properties.put("id", this.getId());
163        properties.put("label", this.getLabel());
164        properties.put("description", this.getDescription());
165        properties.put("computableSize", this.isComputableSize());
166        properties.put("access", this.getCacheStats().requestCount());
167        properties.put("hit", this.getCacheStats().hitCount());
168        properties.put("hitRate", this.getCacheStats().hitRate());
169        properties.put("miss", this.getCacheStats().missCount());
170        properties.put("missRate", this.getCacheStats().missRate());
171        properties.put("nbElement", this.getNumberOfElements());
172        properties.put("nbEviction", this.getCacheStats().evictionCount());
173        properties.put("type", CacheType.MEMORY);
174        try
175        {
176            properties.put("currentSize", this.getMemorySize());
177        }
178        catch (CacheException e)
179        {
180            logger.error("Unable to compute size of cache: {} - {}", this.getId(), this.getLabel(), e);
181            properties.put("currentSize", -1);
182        }
183        properties.put("maxSize", this.getMaxSize());
184        return properties;
185    }
186
187    /**
188     * Is this cache filled at least once.
189     * @return true if the cache have been filled (put or putAll have been called even without values), 
190     *         false when the cache is new, reseted or if all values have been cleaned
191     */
192    public boolean isInitialized();
193    
194    /**
195     * Can the cache be transmitted in sub-requests of DispatchGenerator
196     * @return true if the cache can be transmitted in sub-requests of DispatchGenerator
197     */
198    public boolean isDispatchable();
199}