001/*
002 *  Copyright 2020 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.util;
017
018import java.util.Collection;
019
020import org.ametys.core.cache.AbstractCacheManager;
021import org.ametys.core.cache.AbstractCacheManager.CacheType;
022import org.ametys.core.cache.Cache;
023import org.ametys.runtime.i18n.I18nizableText;
024
025/**
026 * Trait for easily cache some elements, in multiple Caches if necessary.
027 * <br>This is the preferred way to manage the caching of elements (over {@link CachingComponent}), as it will use
028 * the {@link AbstractCacheManager CacheManager mechanism}, and as it is an interface, it will not interfere in the class hierarchy
029 * of the implementing component.
030 * <br>
031 * <br>The methods to implement are:
032 * <ul>
033 * <li>{@link #getManagedCaches}: for describing the managed caches;</li>
034 * <li>{@link #getCacheManager}: for binding the reference of {@link AbstractCacheManager};</li>
035 * <li>{@link #isCachingEnabled}: for implementing the potential business logic to determine if the caching is enabled.</li>
036 * </ul>
037 * <br>
038 * <br>The methods to call are:
039 * <ul>
040 * <li>{@link #createCaches}: for creating the managed caches;</li>
041 * <li>{@link #removeCaches}: for removing the managed caches;</li>
042 * <li>{@link #getCache}: for getting a reference to a managed cache.</li>
043 * </ul>
044 */
045public interface Cacheable
046{
047    /**
048     * A configuration for describing to {@link Cacheable} interface the managed {@link Cache caches} to {@link AbstractCacheManager#createMemoryCache create}.
049     * <br>It simply composes a cache id, a label and a description.
050     */
051    public static final class SingleCacheConfiguration
052    {
053        private String _id;
054        private I18nizableText _label;
055        private I18nizableText _description;
056        
057        private SingleCacheConfiguration(String id, I18nizableText label, I18nizableText description)
058        {
059            _id = id;
060            _label = label;
061            _description = description;
062        }
063        
064        /**
065         * Builds a {@link SingleCacheConfiguration}
066         * @param id The id of the cache
067         * @param label The label of the cache
068         * @param description The description of the cache
069         * @return A {@link SingleCacheConfiguration}
070         */
071        public static SingleCacheConfiguration of(String id, I18nizableText label, I18nizableText description)
072        {
073            return new SingleCacheConfiguration(id, label, description);
074        }
075    }
076    
077    /**
078     * Gets the managed caches.
079     * <br>This <b>is</b> meant to be implemented in order to describe the managed caches and automatically
080     * create and remove the corresponding caches in {@link #createCaches} and {@link #removeCaches} default methods.
081     * <br>This <b>is not</b> meant to be called manually.
082     * @return A collection of {@link SingleCacheConfiguration}s to manage
083     */
084    Collection<SingleCacheConfiguration> getManagedCaches();
085    
086    /**
087     * Returns <code>true</code> if the cache is enabled
088     * <br>The default implementation returns <code>true</code>.
089     * <br>You <b>can</b> override this method to modify the behavior.
090     * @return <code>true</code> if the cache is enabled
091     */
092    default boolean isCachingEnabled()
093    {
094        return true;
095    }
096    
097    /**
098     * Returns the instance of the implementation of {@link AbstractCacheManager} to use.
099     * <br>This <b>is not</b> meant to be called manually.
100     * @return The {@link AbstractCacheManager} to bind
101     */
102    AbstractCacheManager getCacheManager();
103    
104    /**
105     * Gets the managed cache with the given id.
106     * <br>It is advised to create its own business methods which call this method in order to retrieve the caches.
107     * <br>For instance, if the implementing class {@link #getManagedCaches declares} two caches,
108     * you can create the two methods:
109     * <ul>
110     * <li><code>Cache&lt;String, Double&gt; getCachePriceByProductId()</code></li>
111     * <li>and <code>Cache&lt;Integer, Boolean&gt; getCacheAvailabilityByProductIndex()</code></li>
112     * </ul>
113     * which simply call this method, so as to have methods to retrieve the caches with more semantic.
114     * @param <K> The type of the keys in the cache
115     * @param <V> The type of the values in the cache
116     * @param cacheId The cache id
117     * @return The managed {@link Cache}
118     */
119    default <K, V> Cache<K, V> getCache(String cacheId)
120    {
121        return getCacheManager().get(cacheId);
122    }
123    
124    /**
125     * Creates the managed caches.
126     * <br>This <b>is</b> meant to be called manually, at the beginning of the lifecycle of the implementing class,
127     * in order to create and initialize the corresponding caches.
128     * <br>This <b>is not</b> meant to be overridden.
129     */
130    default void createCaches()
131    {
132        if (isCachingEnabled())
133        {
134            for (SingleCacheConfiguration cacheConfig : getManagedCaches())
135            {
136                _createCache(cacheConfig._id, cacheConfig._label, cacheConfig._description);
137            }
138        }
139    }
140    
141    private void _createCache(String id, I18nizableText label, I18nizableText description)
142    {
143        getCacheManager().createMemoryCache(
144                id, 
145                label, 
146                description, 
147                hasComputableSize(),
148                null);
149    }
150    
151    /**
152     * Removes the managed caches.
153     * <br>This <b>is</b> meant to be called manually, at the end of the lifecycle of the implementing class,
154     * in order to remove the corresponding caches.
155     * <br>This <b>is not</b> meant to be overridden.
156     */
157    default void removeCaches()
158    {
159        if (isCachingEnabled())
160        {
161            for (SingleCacheConfiguration cacheConfig : getManagedCaches())
162            {
163                getCacheManager().removeCache(cacheConfig._id, CacheType.MEMORY);
164            }
165        }
166    }
167    
168    /**
169     * Determines if the cache has a computable size. This operation can be very slow.
170     * @return <code>true</code> if we can compute the size on the cache.
171     */
172    default boolean hasComputableSize()
173    {
174        return true;
175    }
176}