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.jcs.access.CacheAccess;
008import org.apache.commons.jcs.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 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 InageryInfo
044     *
045     * @param info ImageryInfo describing the layer
046     */
047    public 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            if (loaderFactory == null) {
056                synchronized (AbstractCachedTileSourceLayer.class) {
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            return cache;
082        }
083        cache = JCSCacheManager.getCache(getCacheName(),
084                0,
085                getDiskCacheSize(),
086                CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
087        return cache;
088    }
089
090    /**
091     * Plugins that wish to set custom tile loader should call this method
092     * @param newLoaderFactory that will be used to load tiles
093     */
094
095    public static synchronized void setTileLoaderFactory(TileLoaderFactory newLoaderFactory) {
096        loaderFactoryOverride = newLoaderFactory;
097    }
098
099    /**
100     * Returns tile loader factory for cache region and specified TileLoader class
101     * @param name of the cache region
102     * @param klazz type of the TileLoader
103     * @return factory returning cached tile loaders using specified cache and TileLoaders
104     */
105    public static TileLoaderFactory getTileLoaderFactory(String name, Class<? extends TileLoader> klazz) {
106        CacheAccess<String, BufferedImageCacheEntry> cache = getCache(name);
107        if (cache == null) {
108            return null;
109        }
110        return new CachedTileLoaderFactory(cache, klazz);
111    }
112
113    /**
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}