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}