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 /** 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 cache = JCSCacheManager.getCache(getCacheName(), 085 0, 086 getDiskCacheSize(), 087 CachedTileLoaderFactory.PROP_TILECACHE_DIR.get()); 088 return cache; 089 } 090 091 092 /** 093 * Plugins that wish to set custom tile loader should call this method 094 * @param newLoaderFactory that will be used to load tiles 095 */ 096 097 public static synchronized void setTileLoaderFactory(TileLoaderFactory newLoaderFactory) { 098 loaderFactoryOverride = newLoaderFactory; 099 } 100 101 /** 102 * Returns tile loader factory for cache region and specified TileLoader class 103 * @param name of the cache region 104 * @param klazz type of the TileLoader 105 * @return factory returning cached tile loaders using specified cache and TileLoaders 106 */ 107 public static TileLoaderFactory getTileLoaderFactory(String name, Class<? extends TileLoader> klazz) { 108 CacheAccess<String, BufferedImageCacheEntry> cache = getCache(name); 109 if (cache == null) { 110 return null; 111 } 112 return new CachedTileLoaderFactory(cache, klazz); 113 } 114 115 /** 116 * @param name of cache region 117 * @return cache configured object for specified cache region 118 */ 119 public static CacheAccess<String, BufferedImageCacheEntry> getCache(String name) { 120 return JCSCacheManager.getCache(name, 121 0, 122 MAX_DISK_CACHE_SIZE.get() * 1024, // MAX_DISK_CACHE_SIZE is in MB, needs to by in sync with getDiskCacheSize 123 CachedTileLoaderFactory.PROP_TILECACHE_DIR.get()); 124 } 125 126 protected abstract Class<? extends TileLoader> getTileLoaderClass(); 127 128 protected int getDiskCacheSize() { 129 return MAX_DISK_CACHE_SIZE.get() * 1024; 130 } 131 132 protected abstract String getCacheName(); 133}