Index: trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 4824)
+++ trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 4825)
@@ -18,11 +18,9 @@
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.*;
 import java.util.Map.Entry;
+import java.util.concurrent.Callable;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -44,5 +42,4 @@
 import org.openstreetmap.gui.jmapviewer.Tile;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileCache;
-import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
@@ -69,4 +66,7 @@
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
+import org.openstreetmap.josm.io.CacheCustomContent;
+import org.openstreetmap.josm.io.UTFInputStreamReader;
+import org.xml.sax.InputSource;
 
 /**
@@ -225,4 +225,45 @@
     }
 
+    private static class CachedAttributionBingAerialTileSource extends BingAerialTileSource {
+
+        class BingAttributionData extends CacheCustomContent<IOException> {
+
+            public BingAttributionData() {
+                super("bing.attribution.xml", CacheCustomContent.INTERVAL_WEEKLY);
+            }
+
+            @Override
+            protected byte[] updateData() throws IOException {
+                URL u = getAttributionUrl();
+                UTFInputStreamReader in = UTFInputStreamReader.create(u.openStream(), "utf-8");
+                String r = new Scanner(in).useDelimiter("\\A").next();
+                System.out.println("Successfully loaded Bing attribution data.");
+                return r.getBytes("utf-8");
+            }
+        }
+
+        @Override
+        protected Callable<List<Attribution>> getAttributionLoaderCallable() {
+            return new Callable<List<Attribution>>() {
+
+                @Override
+                public List<Attribution> call() throws Exception {
+                    BingAttributionData attributionLoader = new BingAttributionData();
+                    int waitTimeSec = 1;
+                    while (true) {
+                        try {
+                            String xml = attributionLoader.updateIfRequiredString();
+                            return parseAttributionText(new InputSource(new StringReader((xml))));
+                        } catch (IOException ex) {
+                            System.err.println("Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds.");
+                            Thread.sleep(waitTimeSec * 1000L);
+                            waitTimeSec *= 2;
+                        }
+                    }
+                }
+            };
+        }
+    }
+
     public static TileSource getTileSource(ImageryInfo info) throws IllegalArgumentException {
         if (info.getImageryType() == ImageryType.TMS) {
@@ -232,10 +273,11 @@
             return t;
         } else if (info.getImageryType() == ImageryType.BING)
-            return new BingAerialTileSource();
-        else if (info.getImageryType() == ImageryType.SCANEX)
+            return new CachedAttributionBingAerialTileSource();
+        else if (info.getImageryType() == ImageryType.SCANEX) {
             return new ScanexTileSource(info.getUrl());
+        }
         return null;
     }
-    
+
     public static void checkUrl(String url) throws IllegalArgumentException {
         if (url == null) {
