public static byte[] getImage(int x, int y, int zoom, HttpMapSource mapSource) throws IOException, InterruptedException, UnrecoverableDownloadException { MapSpace mapSpace = mapSource.getMapSpace(); int maxTileIndex = mapSpace.getMaxPixels(zoom) / mapSpace.getTileSize(); if (x > maxTileIndex) throw new RuntimeException("Invalid tile index x=" + x + " for zoom " + zoom); if (y > maxTileIndex) throw new RuntimeException("Invalid tile index y=" + y + " for zoom " + zoom); TileStore ts = TileStore.getInstance(); // Thread.sleep(2000); // Test code for creating random download failures // if (Math.random()>0.7) throw new // IOException("intentionally download error"); Settings s = Settings.getInstance(); TileStoreEntry tile = null; if (s.tileStoreEnabled) { // Copy the file from the persistent tilestore instead of // downloading it from internet. tile = ts.getTile(x, y, zoom, mapSource); boolean expired = isTileExpired(tile); if (tile != null) { if (expired) { log.trace("Expired: " + mapSource.getName() + " " + tile); } else { log.trace("Tile of map source " + mapSource.getName() + " used from tilestore"); byte[] data = tile.getData(); notifyCachedTileUsed(data.length); return data; } } } byte[] data = null; if (tile == null) { data = downloadTileAndUpdateStore(x, y, zoom, mapSource); notifyTileDownloaded(data.length); } else { byte[] updatedData = updateStoredTile(tile, mapSource); if (updatedData != null) { data = updatedData; notifyTileDownloaded(data.length); } else { data = tile.getData(); notifyCachedTileUsed(data.length); } } return data; }
public static boolean isTileExpired(TileStoreEntry tileStoreEntry) { if (tileStoreEntry == null) return true; long expiredTime = tileStoreEntry.getTimeExpires(); if (expiredTime >= 0) { // server had set an expiration time long maxExpirationTime = settings.tileMaxExpirationTime + tileStoreEntry.getTimeDownloaded(); long minExpirationTime = settings.tileMinExpirationTime + tileStoreEntry.getTimeDownloaded(); expiredTime = Math.max(minExpirationTime, Math.min(maxExpirationTime, expiredTime)); } else { // no expiration time set by server - use the default one expiredTime = tileStoreEntry.getTimeDownloaded() + settings.tileDefaultExpirationTime; } return (expiredTime < System.currentTimeMillis()); }
protected static boolean hasTileETag(TileStoreEntry tile, HttpMapSource mapSource) throws IOException { String eTag = tile.geteTag(); if (eTag == null || eTag.length() == 0) { log.warn("ETag check not possible: " + "tile in tilestore does not contain ETag attribute"); return true; } HttpURLConnection conn = mapSource.getTileUrlConnection(tile.getZoom(), tile.getX(), tile.getY()); conn.setRequestMethod("HEAD"); conn.setRequestProperty("Accept", ACCEPT); String onlineETag = conn.getHeaderField("ETag"); if (onlineETag == null || onlineETag.length() == 0) return true; return (onlineETag.equals(eTag)); }
/** * Performs a <code>HEAD</code> request for retrieving the <code>LastModified</code> header value. */ protected static boolean isTileNewer(TileStoreEntry tile, HttpMapSource mapSource) throws IOException { long oldLastModified = tile.getTimeLastModified(); if (oldLastModified <= 0) { log.warn( "Tile age comparison not possible: " + "tile in tilestore does not contain lastModified attribute"); return true; } HttpURLConnection conn = mapSource.getTileUrlConnection(tile.getZoom(), tile.getX(), tile.getY()); conn.setRequestMethod("HEAD"); conn.setRequestProperty("Accept", ACCEPT); long newLastModified = conn.getLastModified(); if (newLastModified == 0) return true; return (newLastModified > oldLastModified); }
public static byte[] updateStoredTile(TileStoreEntry tile, HttpMapSource mapSource) throws UnrecoverableDownloadException, IOException, InterruptedException { final int x = tile.getX(); final int y = tile.getY(); final int zoom = tile.getZoom(); final HttpMapSource.TileUpdate tileUpdate = mapSource.getTileUpdate(); switch (tileUpdate) { case ETag: { boolean unchanged = hasTileETag(tile, mapSource); if (unchanged) { if (log.isTraceEnabled()) log.trace("Data unchanged on server (eTag): " + mapSource + " " + tile); return null; } break; } case LastModified: { boolean isNewer = isTileNewer(tile, mapSource); if (!isNewer) { if (log.isTraceEnabled()) log.trace("Data unchanged on server (LastModified): " + mapSource + " " + tile); return null; } break; } } HttpURLConnection conn = mapSource.getTileUrlConnection(zoom, x, y); if (conn == null) throw new UnrecoverableDownloadException( "Tile x=" + x + " y=" + y + " zoom=" + zoom + " is not a valid tile in map source " + mapSource); if (log.isTraceEnabled()) log.trace(String.format("Checking %s %s", mapSource.getName(), tile)); prepareConnection(conn); boolean conditionalRequest = false; switch (tileUpdate) { case IfNoneMatch: { if (tile.geteTag() != null) { conn.setRequestProperty("If-None-Match", tile.geteTag()); conditionalRequest = true; } break; } case IfModifiedSince: { if (tile.getTimeLastModified() > 0) { conn.setIfModifiedSince(tile.getTimeLastModified()); conditionalRequest = true; } break; } } conn.connect(); Settings s = Settings.getInstance(); int code = conn.getResponseCode(); if (conditionalRequest && code == HttpURLConnection.HTTP_NOT_MODIFIED) { // Data unchanged on server if (s.tileStoreEnabled) { tile.update(conn.getExpiration()); TileStore.getInstance().putTile(tile, mapSource); } if (log.isTraceEnabled()) log.trace("Data unchanged on server: " + mapSource + " " + tile); return null; } byte[] data = loadBodyDataInBuffer(conn); if (code != HttpURLConnection.HTTP_OK) throw new DownloadFailedException(conn, code); checkContentType(conn, data); checkContentLength(conn, data); String eTag = conn.getHeaderField("ETag"); long timeLastModified = conn.getLastModified(); long timeExpires = conn.getExpiration(); Utilities.checkForInterruption(); TileImageType imageType = Utilities.getImageType(data); if (imageType == null) throw new UnrecoverableDownloadException("The returned image is of unknown format"); if (s.tileStoreEnabled) { TileStore.getInstance() .putTileData(data, x, y, zoom, mapSource, timeLastModified, timeExpires, eTag); } Utilities.checkForInterruption(); return data; }