Index: /trunk/src/org/openstreetmap/josm/actions/MapRectifierWMSmenuAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/MapRectifierWMSmenuAction.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/actions/MapRectifierWMSmenuAction.java	(revision 8598)
@@ -235,5 +235,4 @@
     private void addWMSLayer(String title, String url) {
         WMSLayer layer = new WMSLayer(new ImageryInfo(title, url));
-        layer.checkGrabberType();
         Main.main.addLayer(layer);
     }
Index: /trunk/src/org/openstreetmap/josm/data/cache/JCSCacheManager.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/cache/JCSCacheManager.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/data/cache/JCSCacheManager.java	(revision 8598)
@@ -15,5 +15,6 @@
 import org.apache.commons.jcs.access.CacheAccess;
 import org.apache.commons.jcs.auxiliary.AuxiliaryCache;
-import org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCache;
+import org.apache.commons.jcs.auxiliary.AuxiliaryCacheFactory;
+import org.apache.commons.jcs.auxiliary.disk.behavior.IDiskCacheAttributes;
 import org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheAttributes;
 import org.apache.commons.jcs.auxiliary.disk.indexed.IndexedDiskCacheFactory;
@@ -35,10 +36,10 @@
  */
 public final class JCSCacheManager {
-    private static final Logger log = FeatureAdapter.getLogger(JCSCacheManager.class.getCanonicalName());
+    private static final Logger LOG = FeatureAdapter.getLogger(JCSCacheManager.class.getCanonicalName());
 
     private static volatile CompositeCacheManager cacheManager = null;
     private static long maxObjectTTL        = Long.MAX_VALUE;
     private static final String PREFERENCE_PREFIX = "jcs.cache";
-    private static final IndexedDiskCacheFactory diskCacheFactory = new IndexedDiskCacheFactory();
+    private static final AuxiliaryCacheFactory diskCacheFactory = new IndexedDiskCacheFactory();
     private static FileLock cacheDirLock = null;
 
@@ -61,14 +62,14 @@
         File cacheDirLockPath = new File(cacheDir, ".lock");
         if (!cacheDirLockPath.exists() && !cacheDirLockPath.createNewFile()) {
-            log.log(Level.WARNING, "Cannot create cache dir lock file");
+            LOG.log(Level.WARNING, "Cannot create cache dir lock file");
         }
         cacheDirLock = new FileOutputStream(cacheDirLockPath).getChannel().tryLock();
 
         if (cacheDirLock == null)
-            log.log(Level.WARNING, "Cannot lock cache directory. Will not use disk cache");
+            LOG.log(Level.WARNING, "Cannot lock cache directory. Will not use disk cache");
 
         // raising logging level gives ~500x performance gain
         // http://westsworld.dk/blog/2008/01/jcs-and-performance/
-        Logger jcsLog = Logger.getLogger("org.apache.commons.jcs");
+        final Logger jcsLog = Logger.getLogger("org.apache.commons.jcs");
         jcsLog.setLevel(Level.INFO);
         jcsLog.setUseParentHandlers(false);
@@ -92,8 +93,10 @@
             @Override
             public void flush() {
+                // nothing to be done on flush
             }
 
             @Override
             public void close() {
+                // nothing to be done on close
             }
         });
@@ -131,5 +134,5 @@
      * @param cacheName         region name
      * @param maxMemoryObjects  number of objects to keep in memory
-     * @param maxDiskObjects    number of objects to keep on disk (if cachePath provided)
+     * @param maxDiskObjects    maximum size of the objects stored on disk in kB
      * @param cachePath         path to disk cache. if null, no disk cache will be created
      * @return cache access object
@@ -153,9 +156,14 @@
 
         if (cachePath != null && cacheDirLock != null) {
-            IndexedDiskCacheAttributes diskAttributes = getDiskCacheAttributes(maxDiskObjects, cachePath);
+            IDiskCacheAttributes diskAttributes = getDiskCacheAttributes(maxDiskObjects, cachePath);
             diskAttributes.setCacheName(cacheName);
-            IndexedDiskCache<K, V> diskCache = diskCacheFactory.createCache(diskAttributes, cacheManager, null, new StandardSerializer());
-
-            cc.setAuxCaches(new AuxiliaryCache[]{diskCache});
+            try {
+                if (cc.getAuxCaches().length == 0) {
+                    AuxiliaryCache<K, V> diskCache = diskCacheFactory.createCache(diskAttributes, cacheManager, null, new StandardSerializer());
+                    cc.setAuxCaches(new AuxiliaryCache[]{diskCache});
+                }
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
         }
         return new CacheAccess<K, V>(cc);
@@ -173,11 +181,12 @@
     }
 
-    private static IndexedDiskCacheAttributes getDiskCacheAttributes(int maxDiskObjects, String cachePath) {
+    private static IDiskCacheAttributes getDiskCacheAttributes(int maxDiskObjects, String cachePath) {
         IndexedDiskCacheAttributes ret = new IndexedDiskCacheAttributes();
+        ret.setDiskLimitType(IDiskCacheAttributes.DiskLimitType.SIZE);
         ret.setMaxKeySize(maxDiskObjects);
         if (cachePath != null) {
             File path = new File(cachePath);
             if (!path.exists() && !path.mkdirs()) {
-                log.log(Level.WARNING, "Failed to create cache path: {0}", cachePath);
+                LOG.log(Level.WARNING, "Failed to create cache path: {0}", cachePath);
             } else {
                 ret.setDiskPath(path);
Index: /trunk/src/org/openstreetmap/josm/data/imagery/CachedTileLoaderFactory.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/CachedTileLoaderFactory.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/CachedTileLoaderFactory.java	(revision 8598)
@@ -3,12 +3,15 @@
 
 import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.preferences.StringProperty;
 
@@ -19,16 +22,30 @@
  * @since 8526
  */
-public abstract class CachedTileLoaderFactory implements TileLoaderFactory {
+public class CachedTileLoaderFactory implements TileLoaderFactory {
     /**
      * Keeps the cache directory where
      */
     public static final StringProperty PROP_TILECACHE_DIR = getTileCacheDir();
-    private String cacheName;
+    private ICacheAccess<String, BufferedImageCacheEntry> cache;
+    private Constructor<? extends TileLoader> tileLoaderConstructor;
 
     /**
-     * @param cacheName name of the cache region, that the created loader will use
+     * @param cache cache instance which will be used by tile loaders created by this tile loader
+     * @param tileLoaderClass tile loader class that will be created
+     *
      */
-    public CachedTileLoaderFactory(String cacheName) {
-        this.cacheName = cacheName;
+    public CachedTileLoaderFactory(ICacheAccess<String, BufferedImageCacheEntry> cache, Class<? extends TileLoader> tileLoaderClass) {
+        this.cache = cache;
+        try {
+            tileLoaderConstructor = tileLoaderClass.getConstructor(
+                    TileLoaderListener.class,
+                    ICacheAccess.class,
+                    int.class,
+                    int.class,
+                    Map.class);
+        } catch (NoSuchMethodException | SecurityException e) {
+            Main.warn(e);
+            throw new RuntimeException(e);
+        }
     }
 
@@ -50,5 +67,5 @@
     @Override
     public TileLoader makeTileLoader(TileLoaderListener listener, Map<String, String> inputHeaders) {
-        Map<String, String> headers = new HashMap<>();
+        Map<String, String> headers = new ConcurrentHashMap<>();
         headers.put("User-Agent", Version.getInstance().getFullAgentString());
         headers.put("Accept", "text/html, image/png, image/jpeg, image/gif, */*");
@@ -56,17 +73,23 @@
             headers.putAll(inputHeaders);
 
-        try {
-            return getLoader(listener, cacheName,
-                    Main.pref.getInteger("socket.timeout.connect", 15) * 1000,
-                    Main.pref.getInteger("socket.timeout.read", 30) * 1000,
-                    headers,
-                    PROP_TILECACHE_DIR.get());
-        } catch (IOException e) {
-            Main.warn(e);
-        }
-        return null;
+        return getLoader(listener, cache,
+                Main.pref.getInteger("socket.timeout.connect", 15) * 1000,
+                Main.pref.getInteger("socket.timeout.read", 30) * 1000,
+                headers);
     }
 
-    protected abstract TileLoader getLoader(TileLoaderListener listener, String cacheName, int connectTimeout, int readTimeout,
-            Map<String, String> headers, String cacheDir) throws IOException;
+    protected TileLoader getLoader(TileLoaderListener listener, ICacheAccess<String, BufferedImageCacheEntry> cache,
+            int connectTimeout, int readTimeout, Map<String, String> headers) {
+        try {
+            return tileLoaderConstructor.newInstance(
+                    listener,
+                    cache,
+                    connectTimeout,
+                    readTimeout,
+                    headers);
+        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            Main.warn(e);
+            throw new RuntimeException(e);
+        }
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoader.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoader.java	(revision 8598)
@@ -17,5 +17,4 @@
 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.cache.HostLimitQueue;
-import org.openstreetmap.josm.data.cache.JCSCacheManager;
 import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob;
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
@@ -34,14 +33,9 @@
     protected final Map<String, String> headers;
     protected final TileLoaderListener listener;
-    private static final String PREFERENCE_PREFIX   = "imagery.tms.cache.";
-
-    /**
-     * how many object on disk should be stored for TMS region. Average tile size is about 20kb. 25000 is around 500MB under this assumption
-     */
-    public static final IntegerProperty MAX_OBJECTS_ON_DISK = new IntegerProperty(PREFERENCE_PREFIX + "max_objects_disk", 25000);
 
     /**
      * overrides the THREAD_LIMIT in superclass, as we want to have separate limit and pool for TMS
      */
+
     public static final IntegerProperty THREAD_LIMIT = new IntegerProperty("imagery.tms.tmsloader.maxjobs", 25);
 
@@ -51,4 +45,5 @@
     public static final IntegerProperty HOST_LIMIT = new IntegerProperty("imagery.tms.tmsloader.maxjobsperhost", 6);
 
+
     /**
      * separate from JCS thread pool for TMS loader, so we can have different thread pools for default JCS
@@ -57,4 +52,5 @@
     private static ThreadPoolExecutor DEFAULT_DOWNLOAD_JOB_DISPATCHER = getNewThreadPoolExecutor("TMS downloader");
 
+
     private ThreadPoolExecutor downloadExecutor = DEFAULT_DOWNLOAD_JOB_DISPATCHER;
 
@@ -62,17 +58,13 @@
      * Constructor
      * @param listener          called when tile loading has finished
-     * @param name              of the cache
+     * @param cache              of the cache
      * @param connectTimeout    to remote resource
      * @param readTimeout       to remote resource
      * @param headers           HTTP headers to be sent along with request
-     * @param cacheDir          where cache file shall reside
      * @throws IOException      when cache initialization fails
      */
-    public TMSCachedTileLoader(TileLoaderListener listener, String name, int connectTimeout, int readTimeout,
-            Map<String, String> headers, String cacheDir) throws IOException {
-        this.cache = JCSCacheManager.getCache(name,
-                200, // use fairly small memory cache, as cached objects are quite big, as they contain BufferedImages
-                MAX_OBJECTS_ON_DISK.get(),
-                cacheDir);
+    public TMSCachedTileLoader(TileLoaderListener listener, ICacheAccess<String, BufferedImageCacheEntry> cache,
+            int connectTimeout, int readTimeout, Map<String, String> headers) throws IOException {
+        this.cache = cache;
         this.connectTimeout = connectTimeout;
         this.readTimeout = readTimeout;
@@ -113,5 +105,5 @@
     @Override
     public void clearCache(TileSource source) {
-        this.cache.clear();
+        this.cache.remove(source.getName() + ":");
     }
 
Index: /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java	(revision 8598)
@@ -85,6 +85,12 @@
     @Override
     public String getCacheKey() {
-        if (tile != null)
-            return tile.getKey();
+        if (tile != null) {
+            TileSource tileSource = tile.getTileSource();
+            String tsName = tileSource.getName();
+            if (tsName == null) {
+                tsName = "";
+            }
+            return tsName.replace(":", "_") + ":" + tileSource.getTileId(tile.getZoom(), tile.getXtile(), tile.getYtile());
+        }
         return null;
     }
@@ -128,11 +134,4 @@
     }
 
-    private boolean isNoTileAtZoom() {
-        if (attributes == null) {
-            LOG.warning("Cache attributes are null");
-        }
-        return attributes != null && attributes.isNoTileAtZoom();
-    }
-
     @Override
     protected boolean isResponseLoadable(Map<String, List<String>> headers, int statusCode, byte[] content) {
@@ -148,14 +147,4 @@
     protected boolean cacheAsEmpty() {
         return isNoTileAtZoom() || super.cacheAsEmpty();
-    }
-
-    private boolean handleNoTileAtZoom() {
-        if (isNoTileAtZoom()) {
-            LOG.log(Level.FINE, "JCS TMS - Tile valid, but no file, as no tiles at this level {0}", tile);
-            tile.setError("No tile at this zoom level");
-            tile.putValue("tile-info", "no-tile");
-            return true;
-        }
-        return false;
     }
 
@@ -231,4 +220,42 @@
 
     /**
+     * For TMS use BaseURL as settings discovery, so for different paths, we will have different settings (useful for developer servers)
+     *
+     * @return base URL of TMS or server url as defined in super class
+     */
+    @Override
+    protected String getServerKey() {
+        TileSource ts = tile.getSource();
+        if (ts instanceof AbstractTMSTileSource) {
+            return ((AbstractTMSTileSource) ts).getBaseUrl();
+        }
+        return super.getServerKey();
+    }
+
+    @Override
+    protected BufferedImageCacheEntry createCacheEntry(byte[] content) {
+        return new BufferedImageCacheEntry(content);
+    }
+
+    @Override
+    public void submit() {
+        submit(false);
+    }
+
+    @Override
+    protected CacheEntryAttributes parseHeaders(URLConnection urlConn) {
+        CacheEntryAttributes ret = super.parseHeaders(urlConn);
+        // keep the expiration time between MINIMUM_EXPIRES and MAXIMUM_EXPIRES, so we will cache the tiles
+        // at least for some short period of time, but not too long
+        if (ret.getExpirationTime() < MINIMUM_EXPIRES) {
+            ret.setExpirationTime(now + MINIMUM_EXPIRES);
+        }
+        if (ret.getExpirationTime() > MAXIMUM_EXPIRES) {
+            ret.setExpirationTime(now + MAXIMUM_EXPIRES);
+        }
+        return ret;
+    }
+
+    /**
      * Method for getting the tile from cache only, without trying to reach remote resource
      * @return tile or null, if nothing (useful) was found in cache
@@ -272,40 +299,21 @@
     }
 
-    /**
-     * For TMS use BaseURL as settings discovery, so for different paths, we will have different settings (useful for developer servers)
-     *
-     * @return base URL of TMS or server url as defined in super class
-     */
-    @Override
-    protected String getServerKey() {
-        TileSource ts = tile.getSource();
-        if (ts instanceof AbstractTMSTileSource) {
-            return ((AbstractTMSTileSource) ts).getBaseUrl();
-        }
-        return super.getServerKey();
-    }
-
-    @Override
-    protected BufferedImageCacheEntry createCacheEntry(byte[] content) {
-        return new BufferedImageCacheEntry(content);
-    }
-
-    @Override
-    public void submit() {
-        submit(false);
-    }
-
-    @Override
-    protected CacheEntryAttributes parseHeaders(URLConnection urlConn) {
-        CacheEntryAttributes ret = super.parseHeaders(urlConn);
-        // keep the expiration time between MINIMUM_EXPIRES and MAXIMUM_EXPIRES, so we will cache the tiles
-        // at least for some short period of time, but not too long
-        if (ret.getExpirationTime() < MINIMUM_EXPIRES) {
-            ret.setExpirationTime(now + MINIMUM_EXPIRES);
-        }
-        if (ret.getExpirationTime() > MAXIMUM_EXPIRES) {
-            ret.setExpirationTime(now + MAXIMUM_EXPIRES);
-        }
-        return ret;
-    }
+    private boolean handleNoTileAtZoom() {
+        if (isNoTileAtZoom()) {
+            LOG.log(Level.FINE, "JCS TMS - Tile valid, but no file, as no tiles at this level {0}", tile);
+            tile.setError("No tile at this zoom level");
+            tile.putValue("tile-info", "no-tile");
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isNoTileAtZoom() {
+        if (attributes == null) {
+            LOG.warning("Cache attributes are null");
+        }
+        return attributes != null && attributes.isNoTileAtZoom();
+    }
+
+
 }
Index: /trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java	(revision 8598)
@@ -5,12 +5,11 @@
 
 import java.awt.Point;
-import java.io.IOException;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.text.NumberFormat;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -37,9 +36,8 @@
  */
 public class TemplatedWMSTileSource extends TMSTileSource implements TemplatedTileSource {
-    private Map<String, String> headers = new HashMap<>();
-    private List<String> serverProjections;
+    private Map<String, String> headers = new ConcurrentHashMap<>();
+    private final List<String> serverProjections;
     private EastNorth topLeftCorner;
 
-    private static final String COOKIE_HEADER   = "Cookie";
     private static final String PATTERN_HEADER  = "\\{header\\(([^,]+),([^}]+)\\)\\}";
     private static final String PATTERN_PROJ    = "\\{proj(\\([^})]+\\))?\\}";
@@ -69,8 +67,15 @@
     }
 
+    /**
+     * Initializes class with current projection in JOSM. This call is needed every time projection changes.
+     */
     public void initProjection() {
         initProjection(Main.getProjection());
     }
 
+    /**
+     * Initializes class with projection in JOSM. This call is needed every time projection changes.
+     * @param proj new projection that shall be used for computations
+     */
     public void initProjection(Projection proj) {
         Bounds bounds = proj.getWorldBoundsLatLon();
@@ -93,5 +98,5 @@
 
     @Override
-    public String getTileUrl(int zoom, int tilex, int tiley) throws IOException {
+    public String getTileUrl(int zoom, int tilex, int tiley) {
         String myProjCode = Main.getProjection().toCode();
 
@@ -225,5 +230,10 @@
     @Override
     public int getTileXMax(int zoom) {
-        return getTileXMax(zoom, Main.getProjection());
+        Projection proj = Main.getProjection();
+        double scale = getDegreesPerTile(zoom);
+        Bounds bounds = Main.getProjection().getWorldBoundsLatLon();
+        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
+        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
+        return (int) Math.ceil(Math.abs(max.getX() - min.getX()) / scale);
     }
 
@@ -233,8 +243,12 @@
     }
 
-    //TODO: cache this method with projection code as the key
     @Override
     public int getTileYMax(int zoom) {
-        return getTileYMax(zoom, Main.getProjection());
+        Projection proj = Main.getProjection();
+        double scale = getDegreesPerTile(zoom);
+        Bounds bounds = Main.getProjection().getWorldBoundsLatLon();
+        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
+        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
+        return (int) Math.ceil(Math.abs(max.getY() - min.getY()) / scale);
     }
 
@@ -347,27 +361,14 @@
         EastNorth min = proj.latlon2eastNorth(bounds.getMin());
         EastNorth max = proj.latlon2eastNorth(bounds.getMax());
-        int tilesPerZoom = (int) Math.pow(2, zoom);
-        double ret = Math.max(
+        int tilesPerZoom = (int) Math.pow(2, zoom - 1);
+        return Math.max(
                 Math.abs(max.getY() - min.getY()) / tilesPerZoom,
                 Math.abs(max.getX() - min.getX()) / tilesPerZoom
                 );
-
-        return ret;
-    }
-
-    private int getTileYMax(int zoom, Projection proj) {
-        double scale = getDegreesPerTile(zoom);
-        Bounds bounds = Main.getProjection().getWorldBoundsLatLon();
-        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
-        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
-        return (int) Math.ceil(Math.abs(max.getY() - min.getY()) / scale);
-    }
-
-    private int getTileXMax(int zoom, Projection proj) {
-        double scale = getDegreesPerTile(zoom);
-        Bounds bounds = Main.getProjection().getWorldBoundsLatLon();
-        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
-        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
-        return (int) Math.ceil(Math.abs(max.getX() - min.getX()) / scale);
+    }
+
+    @Override
+    public String getTileId(int zoom, int tilex, int tiley) {
+        return getTileUrl(zoom, tilex, tiley);
     }
 }
Index: /trunk/src/org/openstreetmap/josm/data/imagery/WMSCachedTileLoader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/WMSCachedTileLoader.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/WMSCachedTileLoader.java	(revision 8598)
@@ -5,8 +5,9 @@
 import java.util.Map;
 
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
 import org.openstreetmap.gui.jmapviewer.Tile;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileJob;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
-import org.openstreetmap.josm.data.preferences.IntegerProperty;
+import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 
 /**
@@ -19,22 +20,18 @@
 public class WMSCachedTileLoader extends TMSCachedTileLoader {
 
-    /** limit of concurrent connections to WMS tile source (per source) */
-    public static final IntegerProperty THREAD_LIMIT = new IntegerProperty("imagery.wms.simultaneousConnections", 3);
-
     /**
      * Creates a TileLoader with separate WMS downloader.
      *
      * @param listener that will be notified when tile is loaded
-     * @param name name of the cache region
+     * @param cache reference
      * @param connectTimeout to tile source
      * @param readTimeout from tile source
      * @param headers to be sent with requests
-     * @param cacheDir place to store the cache
      * @throws IOException when there is a problem creating cache repository
      */
-    public WMSCachedTileLoader(TileLoaderListener listener, String name, int connectTimeout, int readTimeout,
-            Map<String, String> headers, String cacheDir) throws IOException {
+    public WMSCachedTileLoader(TileLoaderListener listener, ICacheAccess<String, BufferedImageCacheEntry> cache,
+            int connectTimeout, int readTimeout, Map<String, String> headers) throws IOException {
 
-        super(listener, name, connectTimeout, readTimeout, headers, cacheDir);
+        super(listener, cache, connectTimeout, readTimeout, headers);
         setDownloadExecutor(TMSCachedTileLoader.getNewThreadPoolExecutor("WMS downloader", THREAD_LIMIT.get()));
     }
Index: /trunk/src/org/openstreetmap/josm/data/imagery/WMSCachedTileLoaderJob.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/WMSCachedTileLoaderJob.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/WMSCachedTileLoaderJob.java	(revision 8598)
@@ -40,5 +40,5 @@
         String key = super.getCacheKey();
         if (key != null) {
-            return Main.getProjection().toCode() + key;
+            return key + Main.getProjection().toCode();
         }
         return null;
Index: /trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java	(revision 8598)
@@ -15,5 +15,4 @@
 import java.util.Collection;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -27,7 +26,9 @@
 import javax.swing.JPanel;
 import javax.swing.ListSelectionModel;
+import javax.xml.XMLConstants;
 import javax.xml.namespace.QName;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathConstants;
@@ -43,5 +44,4 @@
 import org.openstreetmap.gui.jmapviewer.tilesources.TMSTileSource;
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -53,5 +53,4 @@
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Utils;
-import org.w3c.dom.DOMException;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
@@ -75,11 +74,11 @@
 
     private static class TileMatrix {
-        String identifier;
-        double scaleDenominator;
-        EastNorth topLeftCorner;
-        int tileWidth;
-        int tileHeight;
-        public int matrixWidth = -1;
-        public int matrixHeight = -1;
+        private String identifier;
+        private double scaleDenominator;
+        private EastNorth topLeftCorner;
+        private int tileWidth;
+        private int tileHeight;
+        private int matrixWidth = -1;
+        private int matrixHeight = -1;
     }
 
@@ -92,14 +91,14 @@
             }
         }); // sorted by zoom level
-        String crs;
-        String identifier;
+        private String crs;
+        private String identifier;
     }
 
     private static class Layer {
-        String format;
-        String name;
-        Map<String, TileMatrixSet> tileMatrixSetByCRS = new ConcurrentHashMap<>();
-        public String baseUrl;
-        public String style;
+        private String format;
+        private String name;
+        private Map<String, TileMatrixSet> tileMatrixSetByCRS = new ConcurrentHashMap<>();
+        private String baseUrl;
+        private String style;
     }
 
@@ -129,8 +128,8 @@
 
     private static final class SelectLayerDialog extends ExtendedDialog {
-        private Layer[] layers;
-        private JList<String> list;
-
-        private SelectLayerDialog(Collection<Layer> layers) {
+        private final Layer[] layers;
+        private final JList<String> list;
+
+        public SelectLayerDialog(Collection<Layer> layers) {
             super(Main.parent, tr("Select WMTS layer"), new String[]{tr("Add layers"), tr("Cancel")});
             this.layers = layers.toArray(new Layer[]{});
@@ -143,5 +142,5 @@
         }
 
-        private String[] getLayerNames(Collection<Layer> layers) {
+        private static final String[] getLayerNames(Collection<Layer> layers) {
             Collection<String> ret = new ArrayList<>();
             for (Layer layer: layers) {
@@ -160,5 +159,5 @@
     }
 
-    private Map<String, String> headers = new HashMap<>();
+    private final Map<String, String> headers = new ConcurrentHashMap<>();
     private Collection<Layer> layers;
     private Layer currentLayer;
@@ -177,8 +176,8 @@
         this.layers = getCapabilities();
         if (layers.size() > 1) {
-            SelectLayerDialog layerSelection = new SelectLayerDialog(layers);
+            final SelectLayerDialog layerSelection = new SelectLayerDialog(layers);
             if (layerSelection.showDialog().getValue() == 1) {
                 this.currentLayer = layerSelection.getSelectedLayer();
-                // TODO: save layer information into ImageryInfo / ImageryPreferences
+                // TODO: save layer information into ImageryInfo / ImageryPreferences?
             } else {
                 throw new IllegalArgumentException(); //user canceled operation
@@ -205,8 +204,14 @@
     }
 
-    private Collection<Layer> getCapabilities() throws IOException  {
+    private Collection<Layer> getCapabilities() throws IOException {
         DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
         builderFactory.setValidating(false);
         builderFactory.setNamespaceAware(false);
+        try {
+            builderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+        } catch (ParserConfigurationException e) {
+            //this should not happen
+            throw new IllegalArgumentException(e);
+        }
         DocumentBuilder builder = null;
         InputStream in = new CachedFile(baseUrl).
@@ -266,5 +271,5 @@
     }
 
-    private Map<String, TileMatrixSet> parseMatrices(NodeList nodeList) throws DOMException, XPathExpressionException {
+    private Map<String, TileMatrixSet> parseMatrices(NodeList nodeList) throws XPathExpressionException {
         Map<String, TileMatrixSet> ret = new ConcurrentHashMap<>();
         for (int matrixSetId = 0; matrixSetId < nodeList.getLength(); matrixSetId++) {
@@ -314,5 +319,5 @@
     }
 
-    private int getOptionalIntegerByXpath(Node document, String xpathQuery) throws XPathExpressionException {
+    private static int getOptionalIntegerByXpath(Node document, String xpathQuery) throws XPathExpressionException {
         String ret = getStringByXpath(document, xpathQuery);
         if (ret == null || "".equals(ret)) {
@@ -374,5 +379,5 @@
 
     @Override
-    public String getTileUrl(int zoom, int tilex, int tiley) throws IOException {
+    public String getTileUrl(int zoom, int tilex, int tiley) {
         String url;
         switch (transferMode) {
@@ -471,10 +476,11 @@
     @Override
     public TileXY latLonToTileXY(double lat, double lon, int zoom) {
-        Projection proj = Main.getProjection();
-        EastNorth enPoint = proj.latlon2eastNorth(new LatLon(lat, lon));
         TileMatrix matrix = getTileMatrix(zoom);
         if (matrix == null) {
             return new TileXY(0, 0);
         }
+
+        Projection proj = Main.getProjection();
+        EastNorth enPoint = proj.latlon2eastNorth(new LatLon(lat, lon));
         double scale = matrix.scaleDenominator * this.crsScale;
         return new TileXY(
@@ -576,4 +582,10 @@
         return 0;
     }
+
+    @Override
+    public String getTileId(int zoom, int tilex, int tiley) {
+        return getTileUrl(zoom, tilex, tiley);
+    }
+
 
     /**
@@ -617,7 +629,6 @@
 
         double scale = matrix.scaleDenominator * this.crsScale;
-        Bounds bounds = proj.getWorldBoundsLatLon();
-        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
-        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
+        EastNorth min = matrix.topLeftCorner;
+        EastNorth max = proj.latlon2eastNorth(proj.getWorldBoundsLatLon().getMax());
         return (int) Math.ceil(Math.abs(max.north() - min.north()) / scale);
     }
@@ -633,7 +644,6 @@
 
         double scale = matrix.scaleDenominator * this.crsScale;
-        Bounds bounds = proj.getWorldBoundsLatLon();
-        EastNorth min = proj.latlon2eastNorth(bounds.getMin());
-        EastNorth max = proj.latlon2eastNorth(bounds.getMax());
+        EastNorth min = matrix.topLeftCorner;
+        EastNorth max = proj.latlon2eastNorth(proj.getWorldBoundsLatLon().getMax());
         return (int) Math.ceil(Math.abs(max.east() - min.east()) / scale);
     }
Index: /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java	(revision 8598)
@@ -40,5 +40,7 @@
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
+import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader;
 import org.openstreetmap.josm.data.preferences.StringProperty;
+import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer;
 import org.openstreetmap.josm.gui.layer.TMSLayer;
 
@@ -139,5 +141,5 @@
         headers.put("User-Agent", Version.getInstance().getFullAgentString());
 
-        cachedLoader = TMSLayer.loaderFactory.makeTileLoader(this, headers);
+        cachedLoader = AbstractCachedTileSourceLayer.getTileLoaderFactory("TMS", TMSCachedTileLoader.class).makeTileLoader(this,  headers);
 
         uncachedLoader = new OsmTileLoader(this);
Index: /trunk/src/org/openstreetmap/josm/gui/layer/AbstractCachedTileSourceLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/AbstractCachedTileSourceLayer.java	(revision 8598)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/AbstractCachedTileSourceLayer.java	(revision 8598)
@@ -0,0 +1,148 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.access.behavior.ICacheAccess;
+import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
+import org.openstreetmap.josm.data.cache.JCSCacheManager;
+import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
+import org.openstreetmap.josm.data.imagery.ImageryInfo;
+import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
+import org.openstreetmap.josm.data.preferences.IntegerProperty;
+
+/**
+ *
+ * Class providing cache to other layers
+ *
+ * @author Wiktor Niesiobędzki
+ *
+ */
+public abstract class AbstractCachedTileSourceLayer extends AbstractTileSourceLayer {
+    /** loader factory responsible for loading tiles for all layers */
+    private static Map<String, TileLoaderFactory> loaderFactories = new ConcurrentHashMap<>();
+
+    private static final String PREFERENCE_PREFIX = "imagery.cache.";
+
+    private static volatile TileLoaderFactory loaderFactoryOverride = null;
+
+    /**
+     * how many object on disk should be stored for TMS region in MB. 500 MB is default value
+     */
+    public static final IntegerProperty MAX_DISK_CACHE_SIZE = new IntegerProperty(PREFERENCE_PREFIX + "max_disk_size", 512);
+
+    /**
+     * use fairly small memory cache, as cached objects are quite big, as they contain BufferedImages
+     */
+    public static final IntegerProperty MEMORY_CACHE_SIZE = new IntegerProperty(PREFERENCE_PREFIX + "cache.max_objects_ram", 200);
+
+    private ICacheAccess<String, BufferedImageCacheEntry> cache;
+    private TileLoaderFactory loaderFactory;
+
+
+    /**
+     * Creates an instance of class based on InageryInfo
+     *
+     * @param info ImageryInfo describing the layer
+     */
+    public AbstractCachedTileSourceLayer(ImageryInfo info) {
+        super(info);
+
+        if (loaderFactoryOverride != null) {
+            loaderFactory = loaderFactoryOverride;
+        } else {
+            String key = this.getClass().getCanonicalName();
+            loaderFactory = loaderFactories.get(key);
+            if (loaderFactory == null) {
+                synchronized (AbstractCachedTileSourceLayer.class) {
+                    // check again, maybe another thread initialized factory
+                    loaderFactory = loaderFactories.get(key);
+                    if (loaderFactory == null) {
+                        loaderFactory = new CachedTileLoaderFactory(getCache(), getTileLoaderClass());
+                        loaderFactories.put(key, loaderFactory);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    protected synchronized TileLoaderFactory getTileLoaderFactory() {
+        if (loaderFactory == null) {
+            loaderFactory = new CachedTileLoaderFactory(getCache(), getTileLoaderClass());
+        }
+        return loaderFactory;
+    }
+
+    /**
+     * @return cache used by this layer
+     */
+    private synchronized ICacheAccess<String, BufferedImageCacheEntry> getCache() {
+        if (cache != null) {
+            return cache;
+        }
+        try {
+            cache = JCSCacheManager.getCache(getCacheName(),
+                    getMemoryCacheSize(),
+                    getDiskCacheSize(),
+                    CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
+            return cache;
+        } catch (IOException e) {
+            Main.warn(e);
+            return null;
+        }
+    }
+
+
+    /**
+     * Plugins that wish to set custom tile loader should call this method
+     * @param newLoaderFactory that will be used to load tiles
+     */
+
+    public static synchronized void setTileLoaderFactory(TileLoaderFactory newLoaderFactory) {
+        loaderFactoryOverride = newLoaderFactory;
+    }
+
+    /**
+     * Returns tile loader factory for cache region and specified TileLoader class
+     * @param name of the cache region
+     * @param klazz type of the TileLoader
+     * @return factory returning cached tile loaders using specified cache and TileLoaders
+     */
+    public static TileLoaderFactory getTileLoaderFactory(String name, Class<? extends TileLoader> klazz) {
+        return new CachedTileLoaderFactory(getCache(name), klazz);
+    }
+
+    /**
+     * @param name of cache region
+     * @return cache configured object for specified cache region
+     */
+    public static CacheAccess<String, BufferedImageCacheEntry> getCache(String name) {
+            try {
+                return JCSCacheManager.getCache(name,
+                        MEMORY_CACHE_SIZE.get(),
+                        MAX_DISK_CACHE_SIZE.get() * 1024, // MAX_DISK_CACHE_SIZE is in MB
+                        CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
+            } catch (IOException e) {
+                Main.warn(e);
+                return null;
+            }
+    }
+
+    protected abstract Class<? extends TileLoader> getTileLoaderClass();
+
+    protected int getMemoryCacheSize() {
+        return MEMORY_CACHE_SIZE.get();
+    }
+
+    protected int getDiskCacheSize() {
+        return MAX_DISK_CACHE_SIZE.get();
+    }
+
+    protected abstract String getCacheName();
+}
Index: /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8598)
@@ -206,8 +206,13 @@
      */
     public void clearTileCache(ProgressMonitor monitor) {
-        tileCache.clear();
         if (tileLoader instanceof CachedTileLoader) {
             ((CachedTileLoader) tileLoader).clearCache(tileSource);
         }
+        // if we use TMSCachedTileLoader, we already cleared by tile source, this is needed
+        // to prevent removal of additional objects
+        if (!(tileLoader instanceof TMSCachedTileLoader)) {
+            tileCache.clear();
+        }
+
     }
 
@@ -1409,4 +1414,9 @@
             }
         }
+
+        if (zoom < getMinZoomLvl() && (ts.insane() || ts.tooLarge())) {
+            myDrawString(g, tr("zoom in to load any tiles"), 120, 120);
+        }
+
         if (noTilesAtZoom) {
             myDrawString(g, tr("No tiles at this zoom level"), 120, 120);
Index: /trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 8598)
@@ -4,19 +4,15 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import java.io.IOException;
-import java.util.Map;
-
+import org.apache.commons.jcs.access.CacheAccess;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
-import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
 import org.openstreetmap.gui.jmapviewer.tilesources.ScanexTileSource;
 import org.openstreetmap.gui.jmapviewer.tilesources.TMSTileSource;
 import org.openstreetmap.gui.jmapviewer.tilesources.TemplatedTMSTileSource;
+import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.imagery.CachedAttributionBingAerialTileSource;
-import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
 import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader;
-import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
@@ -32,5 +28,7 @@
  *
  */
-public class TMSLayer extends AbstractTileSourceLayer {
+public class TMSLayer extends AbstractCachedTileSourceLayer {
+    private static final String CACHE_REGION_NAME = "TMS";
+
     private static final String PREFERENCE_PREFIX = "imagery.tms";
 
@@ -45,15 +43,4 @@
             true);
 
-    /** loader factory responsible for loading tiles for this layer */
-    public static TileLoaderFactory loaderFactory = new CachedTileLoaderFactory("TMS"){
-
-        @Override
-        protected TileLoader getLoader(TileLoaderListener listener, String cacheName, int connectTimeout,
-                int readTimeout, Map<String, String> headers, String cacheDir) throws IOException {
-            return new TMSCachedTileLoader(listener, cacheName, connectTimeout, readTimeout, headers, cacheDir);
-        }
-
-    };
-
     /**
      * Create a layer based on ImageryInfo
@@ -64,17 +51,4 @@
     }
 
-    /**
-     * Plugins that wish to set custom tile loader should call this method
-     * @param newLoaderFactory that will be used to load tiles
-     */
-
-    public static void setTileLoaderFactory(TileLoaderFactory newLoaderFactory) {
-        loaderFactory = newLoaderFactory;
-    }
-
-    @Override
-    protected TileLoaderFactory getTileLoaderFactory() {
-        return loaderFactory;
-    }
 
     /**
@@ -136,5 +110,20 @@
     }
 
+    @Override
+    protected Class<? extends TileLoader> getTileLoaderClass() {
+        return TMSCachedTileLoader.class;
+    }
 
+    @Override
+    protected String getCacheName() {
+        return CACHE_REGION_NAME;
+    }
+
+    /**
+     * @return cache for TMS region
+     */
+    public static CacheAccess<String, BufferedImageCacheEntry> getCache() {
+        return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME);
+    }
 
 }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/WMSLayer.java	(revision 8598)
@@ -5,23 +5,23 @@
 
 import java.awt.event.ActionEvent;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
 
 import javax.swing.AbstractAction;
 import javax.swing.Action;
 
+import org.apache.commons.jcs.access.CacheAccess;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
-import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
+import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
 import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
 import org.openstreetmap.josm.data.imagery.TemplatedWMSTileSource;
-import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
 import org.openstreetmap.josm.data.imagery.WMSCachedTileLoader;
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
@@ -34,10 +34,19 @@
  *
  */
-public class WMSLayer extends AbstractTileSourceLayer {
+public class WMSLayer extends AbstractCachedTileSourceLayer {
+    private static final String PREFERENCE_PREFIX   = "imagery.wms.";
+
     /** default tile size for WMS Layer */
-    public static final IntegerProperty PROP_IMAGE_SIZE = new IntegerProperty("imagery.wms.imageSize", 512);
+    public static final IntegerProperty PROP_IMAGE_SIZE = new IntegerProperty(PREFERENCE_PREFIX + "imageSize", 512);
+
     /** should WMS layer autozoom in default mode */
-    public static final BooleanProperty PROP_DEFAULT_AUTOZOOM = new BooleanProperty("imagery.wms.default_autozoom", true);
-    private List<String> supportedProjections;
+    public static final BooleanProperty PROP_DEFAULT_AUTOZOOM = new BooleanProperty(PREFERENCE_PREFIX + "default_autozoom", true);
+
+    /** limit of concurrent connections to WMS tile source (per source) */
+    public static final IntegerProperty THREAD_LIMIT = new IntegerProperty(PREFERENCE_PREFIX + "simultaneousConnections", 3);
+
+    private static final String CACHE_REGION_NAME = "WMS";
+
+    private Set<String> supportedProjections;
 
     /**
@@ -47,5 +56,7 @@
     public WMSLayer(ImageryInfo info) {
         super(info);
-        this.supportedProjections = info.getServerProjections();
+        this.supportedProjections = new TreeSet<>(info.getServerProjections());
+        this.autoZoom = PROP_DEFAULT_AUTOZOOM.get();
+
     }
 
@@ -62,5 +73,5 @@
 
     @Override
-    protected TileSource getTileSource(ImageryInfo info) throws IllegalArgumentException {
+    protected TileSource getTileSource(ImageryInfo info) {
         if (info.getImageryType() == ImageryType.WMS && info.getUrl() != null) {
             TemplatedWMSTileSource.checkUrl(info.getUrl());
@@ -89,26 +100,4 @@
             ImageryLayerInfo.addLayer(new ImageryInfo(info));
         }
-    }
-
-    /**
-     * Checks that WMS layer is a grabber-compatible one (HTML or WMS).
-     * @throws IllegalStateException if imagery time is neither HTML nor WMS
-     * @since 8068
-     */
-    public void checkGrabberType() {
-    }
-
-    private static TileLoaderFactory loaderFactory = new CachedTileLoaderFactory("WMS") {
-        @Override
-        protected TileLoader getLoader(TileLoaderListener listener, String cacheName, int connectTimeout,
-                int readTimeout, Map<String, String> headers, String cacheDir) throws IOException {
-            return new WMSCachedTileLoader(listener, cacheName, connectTimeout, readTimeout, headers, cacheDir);
-        }
-
-    };
-
-    @Override
-    protected TileLoaderFactory getTileLoaderFactory() {
-        return loaderFactory;
     }
 
@@ -149,3 +138,31 @@
         }
     }
+
+    /**
+     * Checks that WMS layer is a grabber-compatible one (HTML or WMS).
+     * @throws IllegalStateException if imagery time is neither HTML nor WMS
+     * @since 8068
+     * @deprecated not implemented anymore
+     */
+    @Deprecated
+    public void checkGrabberType() {
+        // not implemented
+    }
+
+    @Override
+    protected Class<? extends TileLoader> getTileLoaderClass() {
+        return WMSCachedTileLoader.class;
+    }
+
+    @Override
+    protected String getCacheName() {
+        return CACHE_REGION_NAME;
+    }
+
+    /**
+     * @return cache region for WMS layer
+     */
+    public static CacheAccess<String, BufferedImageCacheEntry> getCache() {
+        return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME);
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/WMTSLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/WMTSLayer.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/WMTSLayer.java	(revision 8598)
@@ -3,17 +3,15 @@
 
 import java.io.IOException;
-import java.util.Map;
 
+import org.apache.commons.jcs.access.CacheAccess;
 import org.openstreetmap.gui.jmapviewer.TileXY;
 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
-import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
-import org.openstreetmap.josm.data.imagery.TileLoaderFactory;
 import org.openstreetmap.josm.data.imagery.WMSCachedTileLoader;
 import org.openstreetmap.josm.data.imagery.WMTSTileSource;
@@ -32,9 +30,10 @@
  *
  */
-public class WMTSLayer extends AbstractTileSourceLayer {
+public class WMTSLayer extends AbstractCachedTileSourceLayer {
     /**
      * default setting of autozoom per layer
      */
     public static final BooleanProperty PROP_DEFAULT_AUTOZOOM = new BooleanProperty("imagery.wmts.default_autozoom", true);
+    private static final String CACHE_REGION_NAME = "WMTS";
 
 
@@ -45,18 +44,5 @@
     public WMTSLayer(ImageryInfo info) {
         super(info);
-    }
-
-    private static TileLoaderFactory loaderFactory = new CachedTileLoaderFactory("WMTS") {
-        @Override
-        protected TileLoader getLoader(TileLoaderListener listener, String cacheName, int connectTimeout,
-                int readTimeout, Map<String, String> headers, String cacheDir) throws IOException {
-            return new WMSCachedTileLoader(listener, cacheName, connectTimeout, readTimeout, headers, cacheDir);
-        }
-
-    };
-
-    @Override
-    protected TileLoaderFactory getTileLoaderFactory() {
-        return loaderFactory;
+        autoZoom = PROP_DEFAULT_AUTOZOOM.get();
     }
 
@@ -126,3 +112,20 @@
         ((WMTSTileSource) tileSource).initProjection(newValue);
     }
+
+    @Override
+    protected Class<? extends TileLoader> getTileLoaderClass() {
+        return WMSCachedTileLoader.class;
+    }
+
+    @Override
+    protected String getCacheName() {
+        return CACHE_REGION_NAME;
+    }
+
+    /**
+     * @return cache region for WMTS layer
+     */
+    public static CacheAccess<String, BufferedImageCacheEntry> getCache() {
+        return AbstractCachedTileSourceLayer.getCache(CACHE_REGION_NAME);
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CacheContentsPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CacheContentsPanel.java	(revision 8598)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CacheContentsPanel.java	(revision 8598)
@@ -0,0 +1,242 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.preferences.imagery;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Component;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractCellEditor;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.UIManager;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+
+import org.apache.commons.jcs.access.CacheAccess;
+import org.apache.commons.jcs.engine.stats.behavior.ICacheStats;
+import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
+import org.apache.commons.jcs.engine.stats.behavior.IStats;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
+import org.openstreetmap.josm.gui.layer.TMSLayer;
+import org.openstreetmap.josm.gui.layer.WMSLayer;
+import org.openstreetmap.josm.gui.layer.WMTSLayer;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.Pair;
+
+/**
+ * Panel for cache content management.
+ *
+ * @author Wiktor Niesiobędzki
+ *
+ */
+public class CacheContentsPanel extends JPanel {
+
+    private static class ButtonColumn extends AbstractCellEditor implements TableCellRenderer, TableCellEditor, ActionListener {
+        private Action action;
+        private JButton renderButton;
+
+        public ButtonColumn(Action action) {
+            this.action = action;
+            renderButton = new JButton();
+            renderButton.addActionListener(this);
+        }
+
+        @Override
+        public Object getCellEditorValue() {
+            return renderButton;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            this.action.actionPerformed(e);
+        }
+
+        @Override
+        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
+            return getTableCellRendererComponent(table, value, isSelected, false, row, column);
+        }
+
+        @Override
+        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
+                boolean hasFocus, int row, int column) {
+
+            if (isSelected) {
+                    renderButton.setForeground(table.getSelectionForeground());
+                    renderButton.setBackground(table.getSelectionBackground());
+            } else {
+                    renderButton.setForeground(table.getForeground());
+                    renderButton.setBackground(UIManager.getColor("Button.background"));
+            }
+
+            renderButton.setFocusPainted(hasFocus);
+
+            if (value == null) {
+                    renderButton.setText("");
+                    renderButton.setIcon(null);
+            } else if (value instanceof Icon) {
+                    renderButton.setText("");
+                    renderButton.setIcon((Icon) value);
+            } else {
+                    renderButton.setText(value.toString());
+                    renderButton.setIcon(null);
+            }
+            return renderButton;
+        }
+
+    }
+
+    private ExecutorService executor = Executors.newSingleThreadExecutor();
+
+    /**
+     * Creates cache content panel
+     */
+    public CacheContentsPanel() {
+        super(new GridBagLayout());
+        CacheAccess<String, BufferedImageCacheEntry> cache = TMSLayer.getCache();
+        add(
+                new JLabel(tr("TMS cache, total cache size: {0} bytes", getCacheSize(cache))),
+                GBC.eol().insets(5, 5, 0, 0)
+                );
+        add(
+                new JScrollPane(getTableForCache(cache)),
+                GBC.eol().fill(GBC.BOTH));
+
+        cache = WMSLayer.getCache();
+        add(
+                new JLabel(tr("WMS cache, total cache size: {0} bytes", getCacheSize(cache))),
+                GBC.eol().insets(5, 5, 0, 0));
+        add(
+                new JScrollPane(getTableForCache(cache)),
+                GBC.eol().fill(GBC.BOTH));
+
+        cache = WMTSLayer.getCache();
+        add(
+                new JLabel(tr("WMTS cache, total cache size: {0} bytes", getCacheSize(cache))),
+                GBC.eol().insets(5, 5, 0, 0));
+
+        add(
+                new JScrollPane(getTableForCache(cache)),
+                GBC.eol().fill(GBC.BOTH));
+    }
+
+    private Long getCacheSize(CacheAccess<String, BufferedImageCacheEntry> cache) {
+        ICacheStats stats = cache.getStatistics();
+        for (IStats cacheStats: stats.getAuxiliaryCacheStats()) {
+            for (IStatElement<?> statElement: cacheStats.getStatElements()) {
+                if ("Data File Length".equals(statElement.getName())) {
+                    Object val = statElement.getData();
+                    if (val instanceof Long) {
+                        return (Long) val;
+                    }
+
+                }
+            }
+        }
+        return new Long(-1);
+    }
+
+    private Map<String, Integer> getCacheStats(CacheAccess<String, BufferedImageCacheEntry> cache) {
+        Set<String> keySet = cache.getCacheControl().getKeySet();
+        Map<String, int[]> temp = new ConcurrentHashMap<>(); // use int[] as a Object reference to int, gives better performance
+        for (String key: keySet) {
+            String[] keyParts = key.split(":", 2);
+            if (keyParts.length == 2) {
+                int[] counter = temp.get(keyParts[0]);
+                if (counter == null) {
+                    temp.put(keyParts[0], new int[]{1});
+                } else {
+                    counter[0]++;
+                }
+            } else {
+                Main.warn("Could not parse the key: {0}. No colon found", key);
+            }
+        }
+
+        // convert to standard Map<String, Integer>
+        Map<String, Integer> ret = new ConcurrentHashMap<>();
+        for (Entry<String, int[]> e: temp.entrySet()) {
+            ret.put(e.getKey(), e.getValue()[0]);
+        }
+        return ret;
+    }
+
+    private void backgroundUpdateModel(final CacheAccess<String, BufferedImageCacheEntry> cache, final DefaultTableModel tableModel) {
+        // fetch statistics in background thread as this may take some time
+        executor.submit(new Runnable() {
+            @Override
+            public void run() {
+                final List<Pair<String, Integer>> sortedStats = new ArrayList<>();
+                for (Entry<String, Integer> e: getCacheStats(cache).entrySet()) {
+                    sortedStats.add(new Pair<>(e.getKey(), e.getValue()));
+                }
+                Collections.sort(sortedStats, new Comparator<Pair<String, Integer>>() {
+                    @Override
+                    public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
+                        return -1 * o1.b.compareTo(o2.b);
+                    }
+                });
+                // once statistics are ready, update the model in EDT thread
+                GuiHelper.runInEDT(new Runnable() {
+                    @Override
+                    public void run() {
+                        tableModel.removeRow(0);
+                        for (Pair<String, Integer> e: sortedStats) {
+                            tableModel.addRow(new String[]{e.a, e.b.toString(), tr("Clear")});
+                        }
+                    }
+                });
+            }
+        });
+    }
+
+    private JTable getTableForCache(CacheAccess<String, BufferedImageCacheEntry> cache) {
+        final DefaultTableModel tableModel = new DefaultTableModel(
+                new String[][]{{tr("Loading data"), tr("Please wait"), ""}},
+                new String[]{"Cache name", "Object Count", "Clear"}) {
+            @Override
+            public boolean isCellEditable(int row, int column) {
+                return column == 2;
+            }
+        };
+
+        backgroundUpdateModel(cache, tableModel);
+
+        final JTable ret = new JTable(tableModel);
+
+        ButtonColumn buttonColumn = new ButtonColumn(
+                new AbstractAction() {
+                    @Override
+                    public void actionPerformed(ActionEvent e) {
+                        int row = ret.convertRowIndexToModel(ret.getEditingRow());
+                        tableModel.setValueAt("0", row, 1);
+                    }
+                });
+        TableColumn tableColumn = ret.getColumnModel().getColumn(2);
+        tableColumn.setCellRenderer(buttonColumn);
+        tableColumn.setCellEditor(buttonColumn);
+        return ret;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CommonSettingsPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CommonSettingsPanel.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CommonSettingsPanel.java	(revision 8598)
@@ -15,7 +15,12 @@
 import javax.swing.JPanel;
 import javax.swing.JSlider;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
 
+import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
+import org.openstreetmap.josm.gui.layer.AbstractCachedTileSourceLayer;
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
 import org.openstreetmap.josm.gui.widgets.JosmComboBox;
+import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.tools.ColorHelper;
 import org.openstreetmap.josm.tools.GBC;
@@ -31,4 +36,8 @@
     private final JSlider fadeAmount = new JSlider(0, 100);
     private final JosmComboBox<String> sharpen;
+    private final JosmTextField tilecacheDir = new JosmTextField();
+    private final JSpinner maxElementsOnDisk;
+    private final JSpinner maxElementsInRam;
+
 
     /**
@@ -37,5 +46,11 @@
     public CommonSettingsPanel() {
         super(new GridBagLayout());
-        
+
+        this.maxElementsInRam = new JSpinner(new SpinnerNumberModel(
+                AbstractCachedTileSourceLayer.MEMORY_CACHE_SIZE.get().intValue(), 0, Integer.MAX_VALUE, 1));
+        this.maxElementsOnDisk = new JSpinner(new SpinnerNumberModel(
+                AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get().intValue(), 0, Integer.MAX_VALUE, 1));
+
+
         this.btnFadeColor = new JButton();
 
@@ -72,6 +87,18 @@
         add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
         add(this.sharpen, GBC.eol().fill(GBC.HORIZONTAL));
+
+        add(new JLabel(tr("Tile cache directory: ")), GBC.std());
+        add(GBC.glue(5, 0), GBC.std());
+        add(tilecacheDir, GBC.eol().fill(GBC.HORIZONTAL));
+
+        add(new JLabel(tr("Maximum size of disk cache (per imagery) in MB: ")), GBC.std());
+        add(GBC.glue(5, 0), GBC.std());
+        add(this.maxElementsOnDisk, GBC.eol());
+
+        add(new JLabel(tr("Maximum number of objects in memory cache: ")), GBC.std());
+        add(GBC.glue(5, 0), GBC.std());
+        add(this.maxElementsInRam, GBC.eol());
     }
-    
+
     /**
      * Loads the common settings.
@@ -83,6 +110,10 @@
         this.fadeAmount.setValue(ImageryLayer.PROP_FADE_AMOUNT.get());
         this.sharpen.setSelectedIndex(Math.max(0, Math.min(2, ImageryLayer.PROP_SHARPEN_LEVEL.get())));
+        this.tilecacheDir.setText(CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
+        this.maxElementsOnDisk.setValue(AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get());
+        this.maxElementsInRam.setValue(AbstractCachedTileSourceLayer.MEMORY_CACHE_SIZE.get());
+
     }
-    
+
     /**
      * Saves the common settings.
@@ -93,5 +124,22 @@
         ImageryLayer.PROP_FADE_COLOR.put(this.btnFadeColor.getBackground());
         ImageryLayer.PROP_SHARPEN_LEVEL.put(sharpen.getSelectedIndex());
-        return false;
+        boolean restartRequired = false;
+        if (!AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.get().equals(this.maxElementsOnDisk.getValue())) {
+            AbstractCachedTileSourceLayer.MAX_DISK_CACHE_SIZE.put((Integer) this.maxElementsOnDisk.getValue());
+            restartRequired = true;
+        }
+
+
+        if (!CachedTileLoaderFactory.PROP_TILECACHE_DIR.get().equals(this.tilecacheDir.getText())) {
+            restartRequired = true;
+            CachedTileLoaderFactory.PROP_TILECACHE_DIR.put(this.tilecacheDir.getText());
+        }
+
+        if (!AbstractCachedTileSourceLayer.MEMORY_CACHE_SIZE.get().equals(this.maxElementsInRam.getValue())) {
+            AbstractCachedTileSourceLayer.MEMORY_CACHE_SIZE.put((Integer) this.maxElementsInRam.getValue());
+        }
+
+
+        return restartRequired;
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 8598)
@@ -73,4 +73,11 @@
 public final class ImageryPreference extends DefaultTabPreferenceSetting {
 
+    private ImageryProvidersPanel imageryProviders;
+    private ImageryLayerInfo layerInfo;
+
+    private CommonSettingsPanel commonSettings;
+    private WMSSettingsPanel wmsSettings;
+    private TMSSettingsPanel tmsSettings;
+
     /**
      * Factory used to create a new {@code ImageryPreference}.
@@ -87,11 +94,4 @@
                 false, new JTabbedPane());
     }
-
-    private ImageryProvidersPanel imageryProviders;
-    private ImageryLayerInfo layerInfo;
-
-    private CommonSettingsPanel commonSettings;
-    private WMSSettingsPanel wmsSettings;
-    private TMSSettingsPanel tmsSettings;
 
     private void addSettingsSection(final JPanel p, String name, JPanel section) {
@@ -131,4 +131,5 @@
         pane.addTab(tr("Settings"), buildSettingsPanel());
         pane.addTab(tr("Offset bookmarks"), new OffsetBookmarksPanel(gui));
+        pane.addTab(tr("Cache contents") , new CacheContentsPanel());
         loadSettings();
         p.add(pane, GBC.std().fill(GBC.BOTH));
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/TMSSettingsPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/TMSSettingsPanel.java	(revision 8597)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/imagery/TMSSettingsPanel.java	(revision 8598)
@@ -12,9 +12,7 @@
 import javax.swing.SpinnerNumberModel;
 
-import org.openstreetmap.josm.data.imagery.CachedTileLoaderFactory;
 import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader;
 import org.openstreetmap.josm.data.imagery.TMSCachedTileLoaderJob;
 import org.openstreetmap.josm.gui.layer.TMSLayer;
-import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.tools.GBC;
 
@@ -31,6 +29,5 @@
     private final JSpinner maxZoomLvl;
     private final JCheckBox addToSlippyMapChosser = new JCheckBox();
-    private final JosmTextField tilecacheDir = new JosmTextField();
-    private final JSpinner maxElementsOnDisk;
+
     private final JSpinner maxConcurrentDownloads;
     private final JSpinner maxDownloadsPerHost;
@@ -46,10 +43,9 @@
         maxZoomLvl = new JSpinner(new SpinnerNumberModel(
                 TMSLayer.PROP_MAX_ZOOM_LVL.get().intValue(), TMSLayer.MIN_ZOOM, TMSLayer.MAX_ZOOM, 1));
-        maxElementsOnDisk = new JSpinner(new SpinnerNumberModel(
-                TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.get().intValue(), 0, Integer.MAX_VALUE, 1));
         maxConcurrentDownloads = new JSpinner(new SpinnerNumberModel(
                 TMSCachedTileLoaderJob.THREAD_LIMIT.get().intValue(), 0, Integer.MAX_VALUE, 1));
         maxDownloadsPerHost = new JSpinner(new SpinnerNumberModel(
                 TMSCachedTileLoader.HOST_LIMIT.get().intValue(), 0, Integer.MAX_VALUE, 1));
+
 
         add(new JLabel(tr("Auto zoom by default: ")), GBC.std());
@@ -73,8 +69,4 @@
         add(addToSlippyMapChosser, GBC.eol().fill(GBC.HORIZONTAL));
 
-        add(new JLabel(tr("Tile cache directory: ")), GBC.std());
-        add(GBC.glue(5, 0), GBC.std());
-        add(tilecacheDir, GBC.eol().fill(GBC.HORIZONTAL));
-
         add(new JLabel(tr("Maximum concurrent downloads: ")), GBC.std());
         add(GBC.glue(5, 0), GBC.std());
@@ -84,9 +76,4 @@
         add(GBC.glue(5, 0), GBC.std());
         add(maxDownloadsPerHost, GBC.eol());
-
-
-        add(new JLabel(tr("Maximum elements in disk cache: ")), GBC.std());
-        add(GBC.glue(5, 0), GBC.std());
-        add(this.maxElementsOnDisk, GBC.eol());
 
     }
@@ -101,6 +88,4 @@
         this.maxZoomLvl.setValue(TMSLayer.getMaxZoomLvl(null));
         this.minZoomLvl.setValue(TMSLayer.getMinZoomLvl(null));
-        this.tilecacheDir.setText(CachedTileLoaderFactory.PROP_TILECACHE_DIR.get());
-        this.maxElementsOnDisk.setValue(TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.get());
         this.maxConcurrentDownloads.setValue(TMSCachedTileLoaderJob.THREAD_LIMIT.get());
         this.maxDownloadsPerHost.setValue(TMSCachedTileLoader.HOST_LIMIT.get());
@@ -114,5 +99,5 @@
         boolean restartRequired = false;
 
-        if (TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get() != this.addToSlippyMapChosser.isSelected()) {
+        if (!TMSLayer.PROP_ADD_TO_SLIPPYMAP_CHOOSER.get().equals(this.addToSlippyMapChosser.isSelected())) {
             restartRequired = true;
         }
@@ -122,9 +107,4 @@
         TMSLayer.setMaxZoomLvl((Integer) this.maxZoomLvl.getValue());
         TMSLayer.setMinZoomLvl((Integer) this.minZoomLvl.getValue());
-
-        if (!TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.get().equals(this.maxElementsOnDisk.getValue())) {
-            TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.put((Integer) this.maxElementsOnDisk.getValue());
-            restartRequired = true;
-        }
 
         if (!TMSCachedTileLoader.THREAD_LIMIT.get().equals(this.maxConcurrentDownloads.getValue())) {
@@ -138,9 +118,4 @@
         }
 
-        if (!CachedTileLoaderFactory.PROP_TILECACHE_DIR.get().equals(this.tilecacheDir.getText())) {
-            restartRequired = true;
-            CachedTileLoaderFactory.PROP_TILECACHE_DIR.put(this.tilecacheDir.getText());
-        }
-
         return restartRequired;
     }
Index: /trunk/test/data/wmts/WMTSCapabilities-Wallonie-nomatrixdimension.xml
===================================================================
--- /trunk/test/data/wmts/WMTSCapabilities-Wallonie-nomatrixdimension.xml	(revision 8598)
+++ /trunk/test/data/wmts/WMTSCapabilities-Wallonie-nomatrixdimension.xml	(revision 8598)
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Capabilities xmlns="http://www.opengis.net/wmts/1.0"
+	xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml"
+	xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd"
+	version="1.0.0">
+	<!-- Service Identification -->
+	<ows:ServiceIdentification>
+		<ows:Title>DONNEES_BASE_FOND_PLAN_ANNOTATIONS_2012_RW_NB</ows:Title>
+		<ows:ServiceType>OGC WMTS</ows:ServiceType>
+		<ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion>
+	</ows:ServiceIdentification> <!-- Operations Metadata -->
+	<ows:OperationsMetadata>
+		<ows:Operation name="GetCapabilities">
+			<ows:DCP>
+				<ows:HTTP>
+					<ows:Get
+						xlink:href="http://geoservices.wallonie.be/arcgis/rest/services/DONNEES_BASE/FOND_PLAN_ANNOTATIONS_2012_RW_NB/MapServer/WMTS/1.0.0/WMTSCapabilities.xml">
+						<ows:Constraint name="GetEncoding">
+							<ows:AllowedValues>
+								<ows:Value>RESTful</ows:Value>
+							</ows:AllowedValues>
+						</ows:Constraint>
+					</ows:Get>
+					<!-- add KVP binding in 10.1 -->
+					<ows:Get
+						xlink:href="http://geoservices.wallonie.be/arcgis/rest/services/DONNEES_BASE/FOND_PLAN_ANNOTATIONS_2012_RW_NB/MapServer/WMTS?">
+						<ows:Constraint name="GetEncoding">
+							<ows:AllowedValues>
+								<ows:Value>KVP</ows:Value>
+							</ows:AllowedValues>
+						</ows:Constraint>
+					</ows:Get>
+				</ows:HTTP>
+			</ows:DCP>
+		</ows:Operation>
+		<ows:Operation name="GetTile">
+			<ows:DCP>
+				<ows:HTTP>
+					<ows:Get
+						xlink:href="http://geoservices.wallonie.be/arcgis/rest/services/DONNEES_BASE/FOND_PLAN_ANNOTATIONS_2012_RW_NB/MapServer/WMTS/tile/1.0.0/">
+						<ows:Constraint name="GetEncoding">
+							<ows:AllowedValues>
+								<ows:Value>RESTful</ows:Value>
+							</ows:AllowedValues>
+						</ows:Constraint>
+					</ows:Get>
+					<ows:Get
+						xlink:href="http://geoservices.wallonie.be/arcgis/rest/services/DONNEES_BASE/FOND_PLAN_ANNOTATIONS_2012_RW_NB/MapServer/WMTS?">
+						<ows:Constraint name="GetEncoding">
+							<ows:AllowedValues>
+								<ows:Value>KVP</ows:Value>
+							</ows:AllowedValues>
+						</ows:Constraint>
+					</ows:Get>
+				</ows:HTTP>
+			</ows:DCP>
+		</ows:Operation>
+	</ows:OperationsMetadata>
+	<Contents>
+		<!--Layer -->
+		<Layer>
+			<ows:Title>DONNEES_BASE_FOND_PLAN_ANNOTATIONS_2012_RW_NB</ows:Title>
+			<ows:Identifier>DONNEES_BASE_FOND_PLAN_ANNOTATIONS_2012_RW_NB</ows:Identifier>
+			<ows:BoundingBox crs="urn:ogc:def:crs:EPSG::31370">
+				<ows:LowerCorner>42300.92807390103 21237.786800000817</ows:LowerCorner>
+				<ows:UpperCorner>295130.32186220103 167836.14678540602</ows:UpperCorner>
+			</ows:BoundingBox>
+			<ows:WGS84BoundingBox crs="urn:ogc:def:crs:OGC:2:84">
+				<ows:LowerCorner>2.840548314430268 49.485372459967245</ows:LowerCorner>
+				<ows:UpperCorner>6.427849693016202 50.820959517561256</ows:UpperCorner>
+			</ows:WGS84BoundingBox>
+			<Style isDefault="true">
+				<ows:Title>Default Style</ows:Title>
+				<ows:Identifier>default</ows:Identifier>
+			</Style>
+			<Format>image/png</Format>
+			<TileMatrixSetLink>
+				<TileMatrixSet>default028mm</TileMatrixSet>
+			</TileMatrixSetLink>
+			<ResourceURL format="image/png" resourceType="tile"
+				template="http://geoservices.wallonie.be/arcgis/rest/services/DONNEES_BASE/FOND_PLAN_ANNOTATIONS_2012_RW_NB/MapServer/WMTS/tile/1.0.0/DONNEES_BASE_FOND_PLAN_ANNOTATIONS_2012_RW_NB/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png" />
+		</Layer> <!--TileMatrixSet -->
+		<TileMatrixSet>
+			<ows:Title>TileMatrix using 0.28mm</ows:Title>
+			<ows:Abstract>The tile matrix set that has scale values calculated based on the dpi defined by OGC specification (dpi assumes 0.28mm as the physical distance of a pixel).</ows:Abstract>
+			<ows:Identifier>default028mm</ows:Identifier>
+			<ows:SupportedCRS>urn:ogc:def:crs:EPSG::31370</ows:SupportedCRS>
+			<TileMatrix>
+				<ows:Identifier>0</ows:Identifier>
+				<ScaleDenominator>1417413.5491126343</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>1</ows:Identifier>
+				<ScaleDenominator>1181177.957593862</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>2</ows:Identifier>
+				<ScaleDenominator>944942.3660750897</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>3</ows:Identifier>
+				<ScaleDenominator>708706.7745563171</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>4</ows:Identifier>
+				<ScaleDenominator>472471.18303754483</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>5</ows:Identifier>
+				<ScaleDenominator>236235.59151877242</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>6</ows:Identifier>
+				<ScaleDenominator>94494.23660750895</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>7</ows:Identifier>
+				<ScaleDenominator>70870.67745563173</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>8</ows:Identifier>
+				<ScaleDenominator>47247.118303754476</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>9</ows:Identifier>
+				<ScaleDenominator>23623.559151877238</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>10</ows:Identifier>
+				<ScaleDenominator>14174.135491126344</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>11</ows:Identifier>
+				<ScaleDenominator>9449.423660750896</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>12</ows:Identifier>
+				<ScaleDenominator>4724.711830375448</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>13</ows:Identifier>
+				<ScaleDenominator>2362.355915187724</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>14</ows:Identifier>
+				<ScaleDenominator>944.9423660750896</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>15</ows:Identifier>
+				<ScaleDenominator>472.4711830375448</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+			<TileMatrix>
+				<ows:Identifier>16</ows:Identifier>
+				<ScaleDenominator>236.2355915187724</ScaleDenominator>
+				<TopLeftCorner>-3.58727E7 4.14227E7</TopLeftCorner>
+				<TileWidth>512</TileWidth>
+				<TileHeight>512</TileHeight>
+			</TileMatrix>
+		</TileMatrixSet>
+	</Contents>
+	<ServiceMetadataURL
+		xlink:href="http://geoservices.wallonie.be/arcgis/rest/services/DONNEES_BASE/FOND_PLAN_ANNOTATIONS_2012_RW_NB/MapServer/WMTS/1.0.0/WMTSCapabilities.xml" />
+</Capabilities>
Index: /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java	(revision 8597)
+++ /trunk/test/unit/org/openstreetmap/josm/data/imagery/WMTSTileSourceTest.java	(revision 8598)
@@ -89,6 +89,17 @@
         verifyBounds(wallonieBounds, testSource, 6, 1063, 1219);
         verifyBounds(wallonieBounds, testSource, 11, 17724, 20324);
-        LatLon ll = new LatLon(testSource.tileXYToLatLon(1063, 1219, 6));
-
+    }
+
+    @Test
+    public void testWALLONIENoMatrixDimension() throws MalformedURLException, IOException {
+        Main.setProjection(Projections.getProjectionByCode("EPSG:31370"));
+        WMTSTileSource testSource = new WMTSTileSource(getImagery("test/data/wmts/WMTSCapabilities-Wallonie-nomatrixdimension.xml"));
+        Bounds wallonieBounds = new Bounds(
+                new LatLon(49.485372459967245, 2.840548314430268),
+                new LatLon(50.820959517561256, 6.427849693016202)
+                );
+
+        verifyBounds(wallonieBounds, testSource, 6, 1063, 1219);
+        verifyBounds(wallonieBounds, testSource, 11, 17724, 20324);
     }
 
