18b811e397f09c241af4ebaef49a2a1662c4c8a0
1 package be
.nikiroo
.utils
;
4 import java
.io
.FileInputStream
;
5 import java
.io
.FileNotFoundException
;
6 import java
.io
.IOException
;
7 import java
.io
.InputStream
;
12 * A generic cache system, with special support for {@link URL}s.
14 * This cache also manages timeout information.
20 private long tooOldChanging
;
21 private long tooOldStable
;
22 private TraceHandler tracer
= new TraceHandler();
25 * Only for inheritance.
31 * Create a new {@link Cache} object.
34 * the directory to use as cache
35 * @param hoursChanging
36 * the number of hours after which a cached file that is thought
37 * to change ~often is considered too old (or -1 for
40 * the number of hours after which a cached file that is thought
41 * to change rarely is considered too old (or -1 for
45 * in case of I/O error
47 public Cache(File dir
, int hoursChanging
, int hoursStable
)
50 this.tooOldChanging
= 1000L * 60 * 60 * hoursChanging
;
51 this.tooOldStable
= 1000L * 60 * 60 * hoursStable
;
53 if (dir
!= null && !dir
.exists()) {
57 if (dir
== null || !dir
.exists()) {
58 throw new IOException("Cannot create the cache directory: "
59 + (dir
== null ?
"null" : dir
.getAbsolutePath()));
64 * The traces handler for this {@link Cache}.
66 * @return the traces handler
68 public TraceHandler
getTraceHandler() {
73 * The traces handler for this {@link Cache}.
76 * the new traces handler
78 public void setTraceHandler(TraceHandler tracer
) {
80 tracer
= new TraceHandler(false, false, false);
87 * Check the resource to see if it is in the cache.
90 * the resource to check
92 * allow files even if they are considered too old
94 * a stable file (that dones't change too often) -- parameter
95 * used to check if the file is too old to keep or not
97 * @return TRUE if it is
100 public boolean check(String uniqueID
, boolean allowTooOld
, boolean stable
) {
101 return check(getCached(uniqueID
), allowTooOld
, stable
);
105 * Check the resource to see if it is in the cache.
108 * the resource to check
110 * allow files even if they are considered too old
112 * a stable file (that dones't change too often) -- parameter
113 * used to check if the file is too old to keep or not
115 * @return TRUE if it is
118 public boolean check(URL url
, boolean allowTooOld
, boolean stable
) {
119 return check(getCached(url
), allowTooOld
, stable
);
123 * Check the resource to see if it is in the cache.
126 * the resource to check
128 * allow files even if they are considered too old
130 * a stable file (that dones't change too often) -- parameter
131 * used to check if the file is too old to keep or not
133 * @return TRUE if it is
136 private boolean check(File cached
, boolean allowTooOld
, boolean stable
) {
137 if (cached
.exists() && cached
.isFile()) {
138 if (!allowTooOld
&& isOld(cached
, stable
)) {
139 if (!cached
.delete()) {
140 tracer
.error("Cannot delete temporary file: "
141 + cached
.getAbsolutePath());
152 * Clean the cache (delete the cached items).
155 * only clean the files that are considered too old for a stable
158 * @return the number of cleaned items
160 public int clean(boolean onlyOld
) {
161 return clean(onlyOld
, dir
);
165 * Clean the cache (delete the cached items) in the given cache directory.
168 * only clean the files that are considered too old for stable
171 * the cache directory to clean
173 * @return the number of cleaned items
175 private int clean(boolean onlyOld
, File cacheDir
) {
176 long ms
= System
.currentTimeMillis();
178 tracer
.trace("Cleaning cache from old files...");
180 int num
= doClean(onlyOld
, cacheDir
);
182 tracer
.trace("Cache cleaned in " + (System
.currentTimeMillis() - ms
)
189 * Actual work done for {@link Cache#clean(boolean, File)}.
192 * only clean the files that are considered too old for stable
195 * the cache directory to clean
197 * @return the number of cleaned items
199 private int doClean(boolean onlyOld
, File cacheDir
) {
201 File
[] files
= cacheDir
.listFiles();
203 for (File file
: files
) {
204 if (file
.isDirectory()) {
205 num
+= doClean(onlyOld
, file
);
207 if (!onlyOld
|| isOld(file
, true)) {
211 tracer
.error("Cannot delete temporary file: "
212 + file
.getAbsolutePath());
223 * Open a resource from the cache if it exists.
228 * allow files even if they are considered too old
230 * a stable file (that dones't change too often) -- parameter
231 * used to check if the file is too old to keep or not
233 * @return the opened resource if found, NULL if not
235 public InputStream
load(String uniqueID
, boolean allowTooOld
, boolean stable
) {
236 return load(getCached(uniqueID
), allowTooOld
, stable
);
240 * Open a resource from the cache if it exists.
243 * the resource to open
245 * allow files even if they are considered too old
247 * a stable file (that doesn't change too often) -- parameter
248 * used to check if the file is too old to keep or not in the
251 * @return the opened resource if found, NULL if not
253 public InputStream
load(URL url
, boolean allowTooOld
, boolean stable
) {
254 return load(getCached(url
), allowTooOld
, stable
);
258 * Open a resource from the cache if it exists.
261 * the resource to open
263 * allow files even if they are considered too old
265 * a stable file (that dones't change too often) -- parameter
266 * used to check if the file is too old to keep or not
268 * @return the opened resource if found, NULL if not
270 private InputStream
load(File cached
, boolean allowTooOld
, boolean stable
) {
271 if (cached
.exists() && cached
.isFile()
272 && (allowTooOld
|| !isOld(cached
, stable
))) {
274 return new MarkableFileInputStream(new FileInputStream(cached
));
275 } catch (FileNotFoundException e
) {
284 * Save the given resource to the cache.
289 * a unique ID used to locate the cached resource
291 * @throws IOException
292 * in case of I/O error
294 public void save(InputStream in
, String uniqueID
) throws IOException
{
295 File cached
= getCached(uniqueID
);
296 cached
.getParentFile().mkdirs();
301 * Save the given resource to the cache.
306 * the {@link URL} used to locate the cached resource
308 * @throws IOException
309 * in case of I/O error
311 public void save(InputStream in
, URL url
) throws IOException
{
312 File cached
= getCached(url
);
317 * Save the given resource to the cache.
319 * Will also clean the {@link Cache} from old files.
324 * the cached {@link File} to save to
326 * @throws IOException
327 * in case of I/O error
329 private void save(InputStream in
, File cached
) throws IOException
{
331 IOUtils
.write(in
, cached
);
335 * Remove the given resource from the cache.
338 * a unique ID used to locate the cached resource
340 * @return TRUE if it was removed
342 public boolean remove(String uniqueID
) {
343 File cached
= getCached(uniqueID
);
344 return cached
.delete();
348 * Remove the given resource from the cache.
351 * the {@link URL} used to locate the cached resource
353 * @return TRUE if it was removed
355 public boolean remove(URL url
) {
356 File cached
= getCached(url
);
357 return cached
.delete();
361 * Check if the {@link File} is too old according to
362 * {@link Cache#tooOldChanging}.
367 * TRUE to denote stable files, that are not supposed to change
370 * @return TRUE if it is
372 private boolean isOld(File file
, boolean stable
) {
373 long max
= tooOldChanging
;
382 long time
= new Date().getTime() - file
.lastModified();
384 tracer
.error("Timestamp in the future for file: "
385 + file
.getAbsolutePath());
388 return time
< 0 || time
> max
;
392 * Return the associated cache {@link File} from this {@link URL}.
397 * @return the cached {@link File} version of this {@link URL}
399 private File
getCached(URL url
) {
402 String name
= url
.getHost();
403 if (name
== null || name
.isEmpty()) {
405 File file
= new File(url
.getFile());
406 if (file
.getParent() == null) {
407 subdir
= new File("+");
409 subdir
= new File(file
.getParent().replace("..", "__"));
411 subdir
= new File(dir
, allowedChars(subdir
.getPath()));
412 name
= allowedChars(url
.getFile());
415 File subsubDir
= new File(dir
, allowedChars(url
.getHost()));
416 subdir
= new File(subsubDir
, "_" + allowedChars(url
.getPath()));
417 name
= allowedChars("_" + url
.getQuery());
420 File cacheFile
= new File(subdir
, name
);
427 * Get the basic cache resource file corresponding to this unique ID.
429 * Note that you may need to add a sub-directory in some cases.
434 * @return the cached version if present, NULL if not
436 private File
getCached(String uniqueID
) {
437 File file
= new File(dir
, allowedChars(uniqueID
));
438 File subdir
= new File(file
.getParentFile(), "_");
439 return new File(subdir
, file
.getName());
443 * Replace not allowed chars (in a {@link File}) by "_".
446 * the raw {@link String}
448 * @return the sanitised {@link String}
450 private String
allowedChars(String raw
) {
451 return raw
.replace('/', '_').replace(':', '_').replace("\\", "_");