diff --git a/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java b/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
index bb94d5c..d722c49 100644
--- a/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
+++ b/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
@@ -3,10 +3,8 @@ package org.openstreetmap.gui.jmapviewer.tilesources;
 //License: GPL.
 
 import java.awt.Image;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
 import java.net.URL;
-import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
@@ -26,9 +24,12 @@ import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFactory;
 
 import org.openstreetmap.gui.jmapviewer.Coordinate;
+import org.openstreetmap.josm.io.CacheCustomContent;
+import org.openstreetmap.josm.io.UTFInputStreamReader;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
 
 public class BingAerialTileSource extends AbstractTMSTileSource {
@@ -44,27 +45,27 @@ public class BingAerialTileSource extends AbstractTMSTileSource {
 
     public BingAerialTileSource() {
         super("Bing Aerial Maps", "http://example.com/");
+    }
 
-        if (attributions == null) {
-            attributions = Executors.newSingleThreadExecutor().submit(new Callable<List<Attribution>>() {
-                public List<Attribution> call() throws Exception {
-                    List<Attribution> attrs = null;
-                    int waitTime = 1;
-                    do {
+    private static class BingAttributionData extends CacheCustomContent<IOException> {
 
-                        try {
-                            attrs = loadAttributionText();
-                            System.out.println("Successfully loaded Bing attribution data.");
-                            return attrs;
-                        } catch(IOException e) {
-                            System.err.println("Could not connect to Bing API. Will retry in " + waitTime + " seconds.");
-                            Thread.sleep(waitTime * 1000L);
-                            waitTime *= 2;
-                        }
+        public BingAttributionData() {
+            super("bing.attribution.xml", CacheCustomContent.INTERVAL_WEEKLY);
+        }
 
-                    } while(true);
-                }
-            });
+        @Override
+        protected byte[] updateData() throws IOException {
+            URL u = new URL("http://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&output=xml&key="
+                    + API_KEY);
+            BufferedReader in = new BufferedReader(new InputStreamReader(u.openStream()));
+            StringBuilder content = new StringBuilder(4096);
+            String line;
+            while ((line = in.readLine()) != null) {
+                content.append(line);
+            }
+            in.close();
+            System.out.println("Successfully loaded Bing attribution data.");
+            return content.toString().getBytes();
         }
     }
 
@@ -88,17 +89,12 @@ public class BingAerialTileSource extends AbstractTMSTileSource {
         return url;
     }
 
-    private List<Attribution> loadAttributionText() throws IOException {
+    private List<Attribution> parseAttributionText(String xml) throws IOException {
         try {
-            URL u = new URL("http://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&output=xml&key="
-                    + API_KEY);
-            URLConnection conn = u.openConnection();
-
-            InputStream stream = conn.getInputStream();
-
             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
             DocumentBuilder builder = factory.newDocumentBuilder();
-            Document document = builder.parse(stream);
+            Document document = builder.parse(new InputSource(
+                    UTFInputStreamReader.create(new ByteArrayInputStream(xml.getBytes()), "UTF-8")));
 
             XPathFactory xPathFactory = XPathFactory.newInstance();
             XPath xpath = xPathFactory.newXPath();
@@ -211,6 +207,25 @@ public class BingAerialTileSource extends AbstractTMSTileSource {
 
     @Override
     public String getAttributionText(int zoom, Coordinate topLeft, Coordinate botRight) {
+        if (attributions == null) {
+            attributions = Executors.newSingleThreadExecutor().submit(new Callable<List<Attribution>>() {
+
+                @Override
+                public List<Attribution> call() throws Exception {
+                    BingAttributionData attributionLoader = new BingAttributionData();
+                    int waitTimeSec = 1;
+                    while (true) {
+                        try {
+                            return parseAttributionText(attributionLoader.updateIfRequiredString());
+                        } catch (IOException ex) {
+                            System.err.println("Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds.");
+                            Thread.sleep(waitTimeSec * 1000L);
+                            waitTimeSec *= 2;
+                        }
+                    }
+                }
+            });
+        }
         try {
             if (!attributions.isDone())
                 return "Loading Bing attribution data...";
