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