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 * Create a new {@link Cache} object.
28 * the directory to use as cache
29 * @param hoursChanging
30 * the number of hours after which a cached file that is thought
31 * to change ~often is considered too old (or -1 for
34 * the number of hours after which a cached file that is thought
35 * to change rarely is considered too old (or -1 for
39 * in case of I/O error
41 public Cache(File dir
, int hoursChanging
, int hoursStable
)
44 this.tooOldChanging
= 1000 * 60 * 60 * hoursChanging
;
45 this.tooOldStable
= 1000 * 60 * 60 * hoursStable
;
47 if (dir
!= null && !dir
.exists()) {
51 if (dir
== null || !dir
.exists()) {
52 throw new IOException("Cannot create the cache directory: "
53 + (dir
== null ?
"null" : dir
.getAbsolutePath()));
58 * The traces handler for this {@link Cache}.
60 * @return the traces handler
62 public TraceHandler
getTraceHandler() {
67 * The traces handler for this {@link Cache}.
70 * the new traces handler
72 public void setTraceHandler(TraceHandler tracer
) {
74 tracer
= new TraceHandler(false, false, false);
81 * Check the resource to see if it is in the cache.
84 * the resource to check
86 * allow files even if they are considered too old
88 * a stable file (that dones't change too often) -- parameter
89 * used to check if the file is too old to keep or not
91 * @return TRUE if it is
94 public boolean check(URL url
, boolean allowTooOld
, boolean stable
) {
95 File file
= getCached(url
);
96 if (file
.exists() && file
.isFile()) {
97 if (allowTooOld
|| !isOld(file
, stable
)) {
106 * Clean the cache (delete the cached items).
109 * only clean the files that are considered too old for a stable
112 * @return the number of cleaned items
114 public int clean(boolean onlyOld
) {
115 return clean(onlyOld
, dir
);
119 * Clean the cache (delete the cached items) in the given cache directory.
122 * only clean the files that are considered too old for stable
125 * the cache directory to clean
127 * @return the number of cleaned items
129 private int clean(boolean onlyOld
, File cacheDir
) {
131 for (File file
: cacheDir
.listFiles()) {
132 if (file
.isDirectory()) {
133 num
+= clean(onlyOld
, file
);
135 if (!onlyOld
|| isOld(file
, true)) {
139 tracer
.error("Cannot delete temporary file: "
140 + file
.getAbsolutePath());
150 * Open a resource from the cache if it exists.
155 * allow files even if they are considered too old
157 * a stable file (that dones't change too often) -- parameter
158 * used to check if the file is too old to keep or not
160 * @return the opened resource if found, NULL if not
162 public InputStream
load(String uniqueID
, boolean allowTooOld
, boolean stable
) {
163 return load(getCached(uniqueID
), allowTooOld
, stable
);
167 * Open a resource from the cache if it exists.
170 * the resource to open
172 * allow files even if they are considered too old
174 * a stable file (that dones't change too often) -- parameter
175 * used to check if the file is too old to keep or not
177 * @return the opened resource if found, NULL if not
179 public InputStream
load(URL url
, boolean allowTooOld
, boolean stable
) {
180 return load(getCached(url
), allowTooOld
, stable
);
184 * Open a resource from the cache if it exists.
187 * the resource to open
189 * allow files even if they are considered too old
191 * a stable file (that dones't change too often) -- parameter
192 * used to check if the file is too old to keep or not
194 * @return the opened resource if found, NULL if not
196 private InputStream
load(File cached
, boolean allowTooOld
, boolean stable
) {
197 if (cached
.exists() && cached
.isFile()
198 && (allowTooOld
|| !isOld(cached
, stable
))) {
200 return new MarkableFileInputStream(new FileInputStream(cached
));
201 } catch (FileNotFoundException e
) {
210 * Save the given resource to the cache.
215 * a unique ID used to locate the cached resource
217 * @return the resulting {@link File}
219 * @throws IOException
220 * in case of I/O error
222 public File
save(InputStream in
, String uniqueID
) throws IOException
{
223 File cached
= getCached(uniqueID
);
224 cached
.getParentFile().mkdirs();
225 return save(in
, cached
);
229 * Save the given resource to the cache.
234 * the {@link URL} used to locate the cached resource
236 * @return the actual cache file
238 * @throws IOException
239 * in case of I/O error
241 public File
save(InputStream in
, URL url
) throws IOException
{
242 File cached
= getCached(url
);
243 return save(in
, cached
);
247 * Save the given resource to the cache.
252 * the cached {@link File} to save to
254 * @return the actual cache file
256 * @throws IOException
257 * in case of I/O error
259 private File
save(InputStream in
, File cached
) throws IOException
{
260 IOUtils
.write(in
, cached
);
265 * Remove the given resource from the cache.
268 * a unique ID used to locate the cached resource
270 * @return TRUE if it was removed
272 public boolean remove(String uniqueID
) {
273 File cached
= getCached(uniqueID
);
274 return cached
.delete();
278 * Remove the given resource from the cache.
281 * the {@link URL} used to locate the cached resource
283 * @return TRUE if it was removed
285 public boolean remove(URL url
) {
286 File cached
= getCached(url
);
287 return cached
.delete();
291 * Check if the {@link File} is too old according to
292 * {@link Cache#tooOldChanging}.
297 * TRUE to denote stable files, that are not supposed to change
300 * @return TRUE if it is
302 private boolean isOld(File file
, boolean stable
) {
303 long max
= tooOldChanging
;
312 long time
= new Date().getTime() - file
.lastModified();
314 tracer
.error("Timestamp in the future for file: "
315 + file
.getAbsolutePath());
318 return time
< 0 || time
> max
;
322 * Return the associated cache {@link File} from this {@link URL}.
327 * @return the cached {@link File} version of this {@link URL}
329 private File
getCached(URL url
) {
332 String name
= url
.getHost();
333 if (name
== null || name
.isEmpty()) {
335 File file
= new File(url
.getFile());
336 if (file
.getParent() == null) {
337 subdir
= new File("+");
339 subdir
= new File(file
.getParent().replace("..", "__"));
341 subdir
= new File(dir
, allowedChars(subdir
.getPath()));
342 name
= allowedChars(url
.getFile());
345 File subsubDir
= new File(dir
, allowedChars(url
.getHost()));
346 subdir
= new File(subsubDir
, "_" + allowedChars(url
.getPath()));
347 name
= allowedChars("_" + url
.getQuery());
350 File cacheFile
= new File(subdir
, name
);
357 * Get the basic cache resource file corresponding to this unique ID.
359 * Note that you may need to add a sub-directory in some cases.
364 * @return the cached version if present, NULL if not
366 private File
getCached(String uniqueID
) {
367 File file
= new File(dir
, allowedChars(uniqueID
));
368 File subdir
= new File(file
.getParentFile(), "_");
369 return new File(subdir
, file
.getName());
373 * Replace not allowed chars (in a {@link File}) by "_".
376 * the raw {@link String}
378 * @return the sanitised {@link String}
380 private String
allowedChars(String raw
) {
381 return raw
.replace('/', '_').replace(':', '_').replace("\\", "_");