Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/BingAerialTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/BingAerialTileSource.java	(revision 24751)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/BingAerialTileSource.java	(revision 24763)
@@ -7,4 +7,7 @@
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 
 import javax.imageio.ImageIO;
@@ -19,5 +22,5 @@
 public class BingAerialTileSource extends OsmTileSource.AbstractOsmTileSource {
     private static String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU";
-    private static List<Attribution> attributions;
+    private static Future<List<Attribution>> attributions;
 
     public BingAerialTileSource() {
@@ -25,12 +28,10 @@
 
         if (attributions == null) {
-            Thread t = new Thread(new Runnable() {
+            attributions = Executors.newSingleThreadExecutor().submit(new Callable<List<Attribution>>() {
                 @Override
-                public void run() {
-                    attributions = loadAttributionText();
+                public List<Attribution> call() throws Exception {
+                    return loadAttributionText();
                 }
             });
-            t.setDaemon(true);
-            t.start();
         }
     }
@@ -130,7 +131,13 @@
 
     @Override
-    public String getTilePath(int zoom, int tilex, int tiley) {
-        String quadtree = computeQuadTree(zoom, tilex, tiley);
-        return "/tiles/a" + quadtree + "." + getExtension() + "?g=587";
+    public String getTilePath(int zoom, int tilex, int tiley) throws IOException {
+        try {
+            if (attributions.get() == null)
+                throw new IOException("Cannot load Bing attribution");
+            String quadtree = computeQuadTree(zoom, tilex, tiley);
+            return "/tiles/a" + quadtree + "." + getExtension() + "?g=587";
+        } catch (Exception e) {
+            throw new IOException("Cannot load Bing attribution", e);
+        }
     }
 
@@ -169,20 +176,26 @@
     @Override
     public String getAttributionText(int zoom, Coordinate topLeft, Coordinate botRight) {
-        if (attributions == null)
-            // TODO: don't show Bing tiles until attribution data is loaded
-            return "";
-        StringBuilder a = new StringBuilder();
-        for (Attribution attr : attributions) {
-            if(zoom <= attr.maxZoom && zoom >= attr.minZoom) {
-                if(topLeft.getLon() < attr.max.getLon()
-                        && botRight.getLon() > attr.min.getLon()
-                        && topLeft.getLat() > attr.min.getLat()
-                        && botRight.getLat() < attr.max.getLat()) {
-                    a.append(attr.attribution);
-                    a.append(" ");
+        try {
+            if (!attributions.isDone())
+                return "Loading Bing attribution data...";
+            if (attributions.get() == null)
+                return "Error loading Bing attribution data";
+            StringBuilder a = new StringBuilder();
+            for (Attribution attr : attributions.get()) {
+                if(zoom <= attr.maxZoom && zoom >= attr.minZoom) {
+                    if(topLeft.getLon() < attr.max.getLon()
+                            && botRight.getLon() > attr.min.getLon()
+                            && topLeft.getLat() > attr.min.getLat()
+                            && botRight.getLat() < attr.max.getLat()) {
+                        a.append(attr.attribution);
+                        a.append(" ");
+                    }
                 }
             }
-        }
-        return a.toString();
+            return a.toString();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return "Error loading Bing attribution data";
     }
 
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java	(revision 24751)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java	(revision 24763)
@@ -63,5 +63,5 @@
             log.log(Level.WARNING,
                     "Failed to access system property ''java.io.tmpdir'' for security reasons. Exception was: "
-                            + e.toString());
+                    + e.toString());
             throw e; // rethrow
         }
@@ -73,6 +73,7 @@
                 // On Linux/Unix systems we do not have a per user tmp directory.
                 // Therefore we add the user name for getting a unique dir name.
-                if (userName != null && userName.length() > 0)
+                if (userName != null && userName.length() > 0) {
                     subDirName += "_" + userName;
+                }
                 cacheDir = new File(tempDir, subDirName);
             }
@@ -208,4 +209,5 @@
             } catch (Exception e) {
                 tile.setImage(Tile.ERROR_IMAGE);
+                tile.error = true;
                 listener.tileLoadingFinished(tile, false);
                 if (input == null) {
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmTileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmTileSource.java	(revision 24751)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmTileSource.java	(revision 24763)
@@ -2,4 +2,5 @@
 
 import java.awt.Image;
+import java.io.IOException;
 
 import javax.swing.ImageIcon;
@@ -48,5 +49,8 @@
         }
 
-        public String getTilePath(int zoom, int tilex, int tiley) {
+        /**
+         * @throws IOException when subclass cannot return the tile URL
+         */
+        public String getTilePath(int zoom, int tilex, int tiley) throws IOException {
             return "/" + zoom + "/" + tilex + "/" + tiley + "." + getExtension();
         }
@@ -56,5 +60,5 @@
         }
 
-        public String getTileUrl(int zoom, int tilex, int tiley) {
+        public String getTileUrl(int zoom, int tilex, int tiley) throws IOException {
             return this.getBaseUrl() + getTilePath(zoom, tilex, tiley);
         }
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java	(revision 24751)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java	(revision 24763)
@@ -184,5 +184,5 @@
     }
 
-    public String getUrl() {
+    public String getUrl() throws IOException {
         return source.getTileUrl(zoom, xtile, ytile);
     }
@@ -254,4 +254,7 @@
     public String getStatus() {
         String status = "new";
+        if (this.error) {
+            status = "error";
+        }
         if (this.loading) {
             status = "loading";
@@ -260,7 +263,4 @@
             status = "loaded";
         }
-        if (this.error) {
-            status = "error";
-        }
         return status;
     }
Index: applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java
===================================================================
--- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java	(revision 24751)
+++ applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java	(revision 24763)
@@ -2,4 +2,5 @@
 
 import java.awt.Image;
+import java.io.IOException;
 
 import org.openstreetmap.gui.jmapviewer.Coordinate;
@@ -80,5 +81,5 @@
      * @return fully qualified url for downloading the specified tile image
      */
-    public String getTileUrl(int zoom, int tilex, int tiley);
+    public String getTileUrl(int zoom, int tilex, int tiley) throws IOException;
 
     /**
