Index: /applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java
===================================================================
--- /applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java	(revision 24846)
+++ /applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java	(revision 24847)
@@ -3,15 +3,21 @@
 //License: GPL. Copyright 2008 by Jan Peter Stotz
 
+import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
 import java.nio.charset.Charset;
+import java.util.Map.Entry;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -36,6 +42,7 @@
 
     private static final String ETAG_FILE_EXT = ".etag";
-
-    private static final Charset ETAG_CHARSET = Charset.forName("UTF-8");
+    private static final String TAGS_FILE_EXT = ".tags";
+
+    private static final Charset TAGS_CHARSET = Charset.forName("UTF-8");
 
     public static final long FILE_AGE_ONE_DAY = 1000 * 60 * 60 * 24;
@@ -167,26 +174,21 @@
                 }
                 if (source.getTileUpdate() == TileUpdate.ETag || source.getTileUpdate() == TileUpdate.IfNoneMatch) {
-                    if (tileFile != null) {
-                        String fileETag = loadETagfromFile();
-                        if (fileETag != null) {
-                            switch (source.getTileUpdate()) {
-                            case IfNoneMatch:
-                                urlConn.addRequestProperty("If-None-Match", fileETag);
-                                break;
-                            case ETag:
-                                if (hasOsmTileETag(fileETag)) {
-                                    tile.setLoaded(true);
-                                    tileFile.setLastModified(System.currentTimeMillis() - maxCacheFileAge
-                                            + recheckAfter);
-                                    return;
-                                }
+                    String fileETag = tile.getValue("etag");
+                    if (fileETag != null) {
+                        switch (source.getTileUpdate()) {
+                        case IfNoneMatch:
+                            urlConn.addRequestProperty("If-None-Match", fileETag);
+                            break;
+                        case ETag:
+                            if (hasOsmTileETag(fileETag)) {
+                                tile.setLoaded(true);
+                                tileFile.setLastModified(System.currentTimeMillis() - maxCacheFileAge
+                                        + recheckAfter);
+                                return;
                             }
                         }
                     }
-
-                    String eTag = urlConn.getHeaderField("ETag");
-                    saveETagToFile(eTag);
-                }
-                loadTileMetadata(tile, urlConn);
+                    tile.putValue("etag", urlConn.getHeaderField("ETag"));
+                }
                 if (urlConn instanceof HttpURLConnection && ((HttpURLConnection)urlConn).getResponseCode() == 304) {
                     // If we are isModifiedSince or If-None-Match has been set
@@ -197,4 +199,7 @@
                     return;
                 }
+
+                loadTileMetadata(tile, urlConn);
+                saveTagsToFile();
 
                 byte[] buffer = loadTileInBuffer(urlConn);
@@ -229,4 +234,7 @@
                 tile.loadImage(fin);
                 fin.close();
+
+                loadTagsFromFile();
+
                 fileAge = tileFile.lastModified();
                 boolean oldTile = System.currentTimeMillis() - fileAge > maxCacheFileAge;
@@ -337,25 +345,60 @@
         }
 
-        protected void saveETagToFile(String eTag) {
-            try {
-                FileOutputStream f = new FileOutputStream(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile()
-                        + "_" + tile.getYtile() + ETAG_FILE_EXT);
-                f.write(eTag.getBytes(ETAG_CHARSET.name()));
+        protected void saveTagsToFile() {
+            File tagsFile = new File(tileCacheDir, tile.getZoom() + "_"
+                    + tile.getXtile() + "_" + tile.getYtile() + TAGS_FILE_EXT);
+            if (tile.getMetadata() == null) {
+                tagsFile.delete();
+                return;
+            }
+            try {
+                final PrintWriter f = new PrintWriter(new OutputStreamWriter(new FileOutputStream(tagsFile)));
+                for (Entry<String, String> entry : tile.getMetadata().entrySet()) {
+                    f.println(entry.getKey() + "=" + entry.getValue());
+                }
                 f.close();
             } catch (Exception e) {
-                System.err.println("Failed to save ETag: " + e.getLocalizedMessage());
-            }
-        }
-
-        protected String loadETagfromFile() {
-            try {
-                FileInputStream f = new FileInputStream(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile()
-                        + "_" + tile.getYtile() + ETAG_FILE_EXT);
+                System.err.println("Failed to save tile tags: " + e.getLocalizedMessage());
+            }
+        }
+
+        /** Load backward-compatiblity .etag file and if it exists move it to new .tags file*/
+        private void loadOldETagfromFile() {
+            File etagFile = new File(tileCacheDir, tile.getZoom() + "_"
+                    + tile.getXtile() + "_" + tile.getYtile() + ETAG_FILE_EXT);
+            if (!etagFile.exists()) return;
+            try {
+                FileInputStream f = new FileInputStream(etagFile);
                 byte[] buf = new byte[f.available()];
                 f.read(buf);
                 f.close();
-                return new String(buf, ETAG_CHARSET.name());
+                String etag = new String(buf, TAGS_CHARSET.name());
+                tile.putValue("etag", etag);
+                if (etagFile.delete()) {
+                    saveTagsToFile();
+                }
+            } catch (IOException e) {
+                System.err.println("Failed to load compatiblity etag: " + e.getLocalizedMessage());
+            }
+        }
+
+        protected void loadTagsFromFile() {
+            loadOldETagfromFile();
+            File tagsFile = new File(tileCacheDir, tile.getZoom() + "_"
+                    + tile.getXtile() + "_" + tile.getYtile() + TAGS_FILE_EXT);
+            try {
+                final BufferedReader f = new BufferedReader(new InputStreamReader(new FileInputStream(tagsFile)));
+                for (String line = f.readLine(); line != null; line = f.readLine()) {
+                    final int i = line.indexOf('=');
+                    if (i == -1 || i == 0) {
+                        System.err.println("Malformed tile tag in file '" + tagsFile.getName() + "':" + line);
+                        continue;
+                    }
+                    tile.putValue(line.substring(0,i),line.substring(i+1));
+                }
+                f.close();
+            } catch (FileNotFoundException e) {
             } catch (Exception e) {
-                return null;
+                System.err.println("Failed to load tile tags: " + e.getLocalizedMessage());
             }
         }
Index: /applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java
===================================================================
--- /applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java	(revision 24846)
+++ /applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java	(revision 24847)
@@ -51,5 +51,5 @@
     protected boolean error = false;
 
-    /** TileSource-specific tile metadata */
+    /** TileLoader-specific tile metadata */
     protected Map<String, String> metadata;
 
@@ -272,4 +272,10 @@
 
     public void putValue(String key, String value) {
+        if (value == null || "".equals(value)) {
+            if (metadata != null) {
+                metadata.remove(key);
+            }
+            return;
+        }
         if (metadata == null) {
             metadata = new HashMap<String,String>();
