File: DiskTileCacher.java

package info (click to toggle)
gpsprune 17-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 3,984 kB
  • ctags: 5,218
  • sloc: java: 39,403; sh: 25; makefile: 17; python: 15
file content (178 lines) | stat: -rw-r--r-- 5,515 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package tim.prune.gui.map;

import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;

import tim.prune.GpsPrune;

/**
 * Class to control the reading and saving of map tiles
 * to a cache on disk
 */
public class DiskTileCacher implements Runnable
{
	/** URL to get image from */
	private URL _url = null;
	/** File to save image to */
	private File _file = null;
	/** Observer to be notified */
	private ImageObserver _observer = null;
	/** Time limit to cache images for */
	private static final long CACHE_TIME_LIMIT = 20 * 24 * 60 * 60 * 1000; // 20 days in ms
	/** Hashset of all blocked / 404 tiles to avoid requesting them again */
	private static final HashSet<String> BLOCKED_URLS = new HashSet<String>();

	/**
	 * Private constructor
	 * @param inUrl URL to get
	 * @param inFile file to save to
	 */
	private DiskTileCacher(URL inUrl, File inFile, ImageObserver inObserver)
	{
		_url = inUrl;
		_file = inFile;
		_observer = inObserver;
		new Thread(this).start();
	}

	/**
	 * Get the specified tile from the disk cache
	 * @param inBasePath base path to whole disk cache
	 * @param inTilePath relative path to requested tile
	 * @param inCheckAge true to check age of file, false to ignore
	 * @return tile image if available, or null if not there
	 */
	public static Image getTile(String inBasePath, String inTilePath, boolean inCheckAge)
	{
		if (inBasePath == null) {return null;}
		File tileFile = new File(inBasePath, inTilePath);
		Image image = null;
		if (tileFile.exists() && tileFile.canRead() && tileFile.length() > 0)
		{
			long fileStamp = tileFile.lastModified();
			if (!inCheckAge || ((System.currentTimeMillis()-fileStamp) < CACHE_TIME_LIMIT))
			{
				try {
					image = Toolkit.getDefaultToolkit().createImage(tileFile.getAbsolutePath());
				}
				catch (Exception e) {
					System.err.println("createImage: " + e.getClass().getName() + " _ " + e.getMessage());
				}
			}
		}
		return image;
	}

	/**
	 * Save the specified image tile to disk
	 * @param inUrl url to get image from
	 * @param inBasePath base path to disk cache
	 * @param inTilePath relative path to this tile
	 * @param inObserver observer to inform when load complete
	 * @return true if successful, false for failure
	 */
	public static boolean saveTile(URL inUrl, String inBasePath, String inTilePath, ImageObserver inObserver)
	{
		if (inBasePath == null || inTilePath == null) {return false;}
		// save file if possible
		File basePath = new File(inBasePath);
		if (!basePath.exists() || !basePath.isDirectory() || !basePath.canWrite()) {
			// Can't write to base path
			return false;
		}
		File tileFile = new File(basePath, inTilePath);
		// Check if this file is already being loaded
		if (isBeingLoaded(tileFile)) {return true;}
		// Check if it has already failed
		if (BLOCKED_URLS.contains(inUrl.toString())) {return true;}

		File dir = tileFile.getParentFile();
		// Start a new thread to load the image if necessary
		if ((dir.exists() || dir.mkdirs()) && dir.canWrite())
		{
			new DiskTileCacher(inUrl, tileFile, inObserver);
			return true;
		}
		return false; // couldn't write the file
	}

	/**
	 * Check whether the given tile is already being loaded
	 * @param inFile desired file
	 * @return true if temporary file with this name exists
	 */
	private static boolean isBeingLoaded(File inFile)
	{
		File tempFile = new File(inFile.getAbsolutePath() + ".temp");
		if (!tempFile.exists()) {
			return false;
		}
		// File exists, so check if it was created recently
		final long fileAge = System.currentTimeMillis() - tempFile.lastModified();
		return fileAge < 1000000L; // overwrite if the temp file is still there after 1000s
	}

	/**
	 * Run method for loading URL asynchronously and saving to file
	 */
	public void run()
	{
		boolean finished = false;
		InputStream in = null;
		FileOutputStream out = null;
		File tempFile = new File(_file.getAbsolutePath() + ".temp");
		// Use a synchronized block across all threads to make sure this url is only fetched once
		synchronized (DiskTileCacher.class)
		{
			if (tempFile.exists()) {tempFile.delete();}
			try {
				if (!tempFile.createNewFile()) {return;}
			}
			catch (Exception e) {return;}
		}
		try
		{
			// Open streams from URL and to file
			out = new FileOutputStream(tempFile);
			// Set http user agent on connection
			URLConnection conn = _url.openConnection();
			conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
			in = conn.getInputStream();
			int d = 0;
			// Loop over each byte in the stream (maybe buffering is more efficient?)
			while ((d = in.read()) >= 0) {
				out.write(d);
			}
			finished = true;
		} catch (IOException e) {
			System.err.println("ioe: " + e.getClass().getName() + " - " + e.getMessage());
			BLOCKED_URLS.add(_url.toString());
		}
		finally
		{
			// clean up files
			try {in.close();} catch (Exception e) {} // ignore
			try {out.close();} catch (Exception e) {} // ignore
			if (!finished) {
				tempFile.delete();
			}
		}
		// Move temp file to desired file location
		if (!tempFile.renameTo(_file))
		{
			// File couldn't be moved - delete both to be sure
			tempFile.delete();
			_file.delete();
		}
		// Tell parent that load is finished (parameters ignored)
		_observer.imageUpdate(null, ImageObserver.ALLBITS, 0, 0, 0, 0);
	}
}