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