Index: /trunk/src/org/openstreetmap/josm/data/cache/HostLimitQueue.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/cache/HostLimitQueue.java	(revision 8672)
+++ /trunk/src/org/openstreetmap/josm/data/cache/HostLimitQueue.java	(revision 8673)
@@ -2,4 +2,6 @@
 package org.openstreetmap.josm.data.cache;
 
+import java.io.IOException;
+import java.net.URL;
 import java.util.Iterator;
 import java.util.Map;
@@ -57,5 +59,10 @@
                     }
                 } else {
-                    Main.info("TMS - Skipping job {0} because host limit reached", job.getUrl());
+                    URL url = null;
+                    try {
+                        url = job.getUrl();
+                    } catch (IOException e) {
+                    }
+                    Main.info("TMS - Skipping job {0} because host limit reached", url);
                 }
             }
@@ -91,5 +98,11 @@
 
     private  Semaphore getSemaphore(JCSCachedTileLoaderJob<?, ?> job) {
-        String host = job.getUrl().getHost();
+        String host;
+        try {
+            host = job.getUrl().getHost();
+        } catch (IOException e) {
+            // do not pass me illegal URL's
+            throw new IllegalArgumentException(e);
+        }
         Semaphore limit = hostSemaphores.get(host);
         if (limit == null) {
Index: /trunk/src/org/openstreetmap/josm/data/cache/ICachedLoaderJob.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/cache/ICachedLoaderJob.java	(revision 8672)
+++ /trunk/src/org/openstreetmap/josm/data/cache/ICachedLoaderJob.java	(revision 8673)
@@ -2,4 +2,5 @@
 package org.openstreetmap.josm.data.cache;
 
+import java.io.IOException;
 import java.net.URL;
 
@@ -21,7 +22,8 @@
      * method to get download URL for Job
      * @return URL that should be fetched
+     * @throws IOException when could not determine the URL of the tile
      *
      */
-    URL getUrl();
+    URL getUrl() throws IOException;
 
     /**
@@ -42,5 +44,6 @@
      * @param listener cache loader listener
      * @param force true if the load should skip all the caches (local & remote)
+     * @throws IOException on failure from getUrl() call
      */
-    void submit(ICachedLoaderListener listener, boolean force);
+    void submit(ICachedLoaderListener listener, boolean force) throws IOException;
 }
Index: /trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java	(revision 8672)
+++ /trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java	(revision 8673)
@@ -149,5 +149,5 @@
 
     @Override
-    public void submit(ICachedLoaderListener listener, boolean force) {
+    public void submit(ICachedLoaderListener listener, boolean force) throws IOException {
         this.force = force;
         boolean first = false;
@@ -160,5 +160,5 @@
         if (deduplicationKey == null) {
             log.log(Level.WARNING, "No url returned for: {0}, skipping", getCacheKey());
-            return;
+            throw new IllegalArgumentException("No url returned");
         }
         synchronized (inProgress) {
@@ -173,5 +173,4 @@
 
         if (first || force) {
-            ensureCacheElement();
             // submit all jobs to separate thread, so calling thread is not blocked with IO when loading from disk
             downloadJobExecutor.execute(this);
@@ -214,5 +213,5 @@
      */
     protected String getServerKey() {
-        return getUrl().getHost();
+        return getUrlNoException().getHost();
     }
 
@@ -221,5 +220,6 @@
         final Thread currentThread = Thread.currentThread();
         final String oldName = currentThread.getName();
-        currentThread.setName("JCS Downloading: " + getUrl());
+        currentThread.setName("JCS Downloading: " + getUrlNoException());
+        ensureCacheElement();
         try {
             // try to fetch from cache
@@ -239,5 +239,5 @@
                     // try to get stale entry in cache
                     finishLoading(LoadResult.SUCCESS);
-                    log.log(Level.FINE, "JCS - found stale object in cache: {0}", getUrl());
+                    log.log(Level.FINE, "JCS - found stale object in cache: {0}", getUrlNoException());
                 } else {
                     // failed completely
@@ -254,8 +254,8 @@
         Set<ICachedLoaderListener> listeners = null;
         synchronized (inProgress) {
-            listeners = inProgress.remove(getUrl().toString());
+            listeners = inProgress.remove(getUrlNoException().toString());
         }
         if (listeners == null) {
-            log.log(Level.WARNING, "Listener not found for URL: {0}. Listener not notified!", getUrl());
+            log.log(Level.WARNING, "Listener not found for URL: {0}. Listener not notified!", getUrlNoException());
             return;
         }
@@ -275,5 +275,5 @@
             if (now > expires) {
                 log.log(Level.FINE, "JCS - Object {0} has expired -> valid to {1}, now is: {2}",
-                        new Object[]{getUrl(), Long.toString(expires), Long.toString(now)});
+                        new Object[]{getUrlNoException(), Long.toString(expires), Long.toString(now)});
                 return false;
             }
@@ -281,8 +281,8 @@
                 now - attributes.getLastModification() > DEFAULT_EXPIRE_TIME) {
             // check by file modification date
-            log.log(Level.FINE, "JCS - Object has expired, maximum file age reached {0}", getUrl());
+            log.log(Level.FINE, "JCS - Object has expired, maximum file age reached {0}", getUrlNoException());
             return false;
         } else if (now - attributes.getCreateTime() > DEFAULT_EXPIRE_TIME) {
-            log.log(Level.FINE, "JCS - Object has expired, maximum time since object creation reached {0}", getUrl());
+            log.log(Level.FINE, "JCS - Object has expired, maximum time since object creation reached {0}", getUrlNoException());
             return false;
         }
@@ -380,5 +380,5 @@
             }
         } catch (FileNotFoundException e) {
-            log.log(Level.FINE, "JCS - Caching empty object as server returned 404 for: {0}", getUrl());
+            log.log(Level.FINE, "JCS - Caching empty object as server returned 404 for: {0}", getUrlNoException());
             attributes.setResponseCode(404);
             attributes.setErrorMessage(e.toString());
@@ -390,5 +390,5 @@
             return doCache;
         } catch (IOException e) {
-            log.log(Level.FINE, "JCS - IOExecption during communication with server for: {0}", getUrl());
+            log.log(Level.FINE, "JCS - IOExecption during communication with server for: {0}", getUrlNoException());
             attributes.setErrorMessage(e.toString());
             attributes.setResponseCode(499); // set dummy error code
@@ -401,8 +401,8 @@
         } catch (Exception e) {
             attributes.setErrorMessage(e.toString());
-            log.log(Level.WARNING, "JCS - Exception during download {0}",  getUrl());
+            log.log(Level.WARNING, "JCS - Exception during download {0}",  getUrlNoException());
             Main.warn(e);
         }
-        log.log(Level.WARNING, "JCS - Silent failure during download: {0}", getUrl());
+        log.log(Level.WARNING, "JCS - Silent failure during download: {0}", getUrlNoException());
         return false;
 
@@ -517,3 +517,11 @@
         finishLoading(LoadResult.CANCELED);
     }
+
+    private URL getUrlNoException() {
+        try {
+            return getUrl();
+        } catch (IOException e) {
+            return null;
+        }
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java	(revision 8672)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java	(revision 8673)
@@ -106,14 +106,9 @@
      */
     @Override
-    public URL getUrl() {
+    public URL getUrl() throws IOException {
         if (url == null) {
-            try {
-                synchronized (this) {
-                    if (url == null)
-                        url = new URL(tile.getUrl());
-                }
-            } catch (IOException e) {
-                LOG.log(Level.WARNING, "JCS TMS Cache - error creating URL for tile {0}: {1}", new Object[] {tile.getKey(), e.getMessage()});
-                LOG.log(Level.INFO, "Exception: ", e);
+            synchronized (this) {
+                if (url == null)
+                    url = new URL(tile.getUrl());
             }
         }
@@ -152,5 +147,11 @@
     public void submit(boolean force) {
         tile.initLoading();
-        super.submit(this, force);
+        try {
+            super.submit(this, force);
+        } catch (Exception e) {
+            // if we fail to submit the job, mark tile as loaded and set error message
+            tile.finishLoading();
+            tile.setError(e.getMessage());
+        }
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8672)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 8673)
@@ -1305,12 +1305,13 @@
         result.hasLoadingTiles = allTiles.size() < ts.size();
         for (Tile t : allTiles) {
+            if ("no-tile".equals(t.getValue("tile-info"))) {
+                result.hasOverzoomedTiles = true;
+            }
+
             if (t.isLoaded()) {
                 if (!t.hasError()) {
                     result.hasVisibleTiles = true;
                 }
-                if ("no-tile".equals(t.getValue("tile-info"))) {
-                    result.hasOverzoomedTiles = true;
-                }
-            } else {
+            } else if (t.isLoading()) {
                 result.hasLoadingTiles = true;
             }
@@ -1410,4 +1411,5 @@
             // If all tiles at displayZoomLevel is loaded, load all tiles at next zoom level
             // to make sure there're really no more zoom levels
+            // loading is done in the next if section
             if (zoom == displayZoomLevel && !tsi.hasLoadingTiles && zoom < dts.maxZoom) {
                 zoom++;
@@ -1416,4 +1418,5 @@
             // When we have overzoomed tiles and all tiles at current zoomlevel is loaded,
             // load tiles at previovus zoomlevels until we have all tiles on screen is loaded.
+            // loading is done in the next if section
             while (zoom > dts.minZoom && tsi.hasOverzoomedTiles && !tsi.hasLoadingTiles) {
                 zoom--;
