001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer;
003
004import java.util.Map;
005import java.util.concurrent.ConcurrentHashMap;
006
007import org.apache.commons.jcs3.access.CacheAccess;
008import org.apache.commons.jcs3.access.behavior.ICacheAccess;
009import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
010import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource;
011import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
012import org.openstreetmap.josm.data.cache.JCSCacheManager;
013import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
014import org.openstreetmap.josm.data.imagery.ImageryInfo;
015import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
016import org.openstreetmap.josm.data.preferences.IntegerProperty;
017
018/**
019 *
020 * Class providing cache to other layers
021 *
022 * @author Wiktor Niesiobędzki
023 * @param <T> Tile Source class used by this Imagery Layer
024 *
025 */
026public abstract class AbstractCachedTileSourceLayer<T extends AbstractTMSTileSource> extends AbstractTileSourceLayer<T> {
027    /** loader factory responsible for loading tiles for all layers */
028    private static final Map<String, TileLoaderFactory> loaderFactories = new ConcurrentHashMap<>();
029
030    private static final String PREFERENCE_PREFIX = "imagery.cache.";
031
032    private static volatile TileLoaderFactory loaderFactoryOverride;
033
034    /**
035     * how many object on disk should be stored for TMS region in MB. 500 MB is default value
036     */
037    public static final IntegerProperty MAX_DISK_CACHE_SIZE = new IntegerProperty(PREFERENCE_PREFIX + "max_disk_size", 512);
038
039    private ICacheAccess<String, BufferedImageCacheEntry> cache;
040    private volatile TileLoaderFactory loaderFactory;
041
042    /**
043     * Creates an instance of class based on ImageryInfo
044     *
045     * @param info ImageryInfo describing the layer
046     */
047    protected AbstractCachedTileSourceLayer(ImageryInfo info) {
048        super(info);
049
050        if (loaderFactoryOverride != null) {
051            loaderFactory = loaderFactoryOverride;
052        } else {
053            String key = this.getClass().getCanonicalName();
054            loaderFactory = loaderFactories.get(key);
055            synchronized (AbstractCachedTileSourceLayer.class) {
056                if (loaderFactory == null) {
057                    // check again, maybe another thread initialized factory
058                    loaderFactory = loaderFactories.get(key);
059                    if (loaderFactory == null) {
060                        loaderFactory = new CachedTileLoaderFactory(getCache(), getTileLoaderClass());
061                        loaderFactories.put(key, loaderFactory);
062                    }
063                }
064            }
065        }
066    }
067
068    @Override
069    protected synchronized TileLoaderFactory getTileLoaderFactory() {
070        if (loaderFactory == null) {
071            loaderFactory = new CachedTileLoaderFactory(getCache(), getTileLoaderClass());
072        }
073        return loaderFactory;
074    }
075
076    /**
077     * @return cache used by this layer
078     */
079    private synchronized ICacheAccess<String, BufferedImageCacheEntry> getCache() {
080        if (cache == null) {
081            cache = JCSCacheManager.getCache(getCacheName(),
082                    0,
083                    getDiskCacheSize(),
084                    CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
085        }
086        return cache;
087    }
088
089    /**
090     * Plugins that wish to set custom tile loader should call this method
091     * @param newLoaderFactory that will be used to load tiles
092     */
093
094    public static synchronized void setTileLoaderFactory(TileLoaderFactory newLoaderFactory) {
095        loaderFactoryOverride = newLoaderFactory;
096    }
097
098    /**
099     * Returns tile loader factory for cache region and specified TileLoader class
100     * @param name of the cache region
101     * @param klazz type of the TileLoader
102     * @return factory returning cached tile loaders using specified cache and TileLoaders
103     */
104    public static TileLoaderFactory getTileLoaderFactory(String name, Class<? extends TileLoader> klazz) {
105        CacheAccess<String, BufferedImageCacheEntry> cache = getCache(name);
106        if (cache == null) {
107            return null;
108        }
109        return new CachedTileLoaderFactory(cache, klazz);
110    }
111
112    /**
113     * Returns cache configured object for specified cache region.
114     * @param name of cache region
115     * @return cache configured object for specified cache region
116     */
117    public static CacheAccess<String, BufferedImageCacheEntry> getCache(String name) {
118            return JCSCacheManager.getCache(name,
119                    0,
120                    MAX_DISK_CACHE_SIZE.get() * 1024, // MAX_DISK_CACHE_SIZE is in MB, needs to by in sync with getDiskCacheSize
121                    CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
122    }
123
124    protected abstract Class<? extends TileLoader> getTileLoaderClass();
125
126    protected int getDiskCacheSize() {
127        return MAX_DISK_CACHE_SIZE.get() * 1024;
128    }
129
130    protected abstract String getCacheName();
131}