Mercurial > hg > digilib-old
comparison servlet/src/digilib/io/DocuDirectory.java @ 290:5d0c0da080ec gen2 scaleable_1
digilib servlet version 2 ("scaleable digilib")
- first stab at using thread pools to limit resource use
- using Dug Leas util.concurrent
- doesn't mix with tomcat :-(
author | robcast |
---|---|
date | Thu, 21 Oct 2004 20:53:37 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
289:9f7b864f955f | 290:5d0c0da080ec |
---|---|
1 /* DocuDirectory -- Directory of DocuFilesets. | |
2 | |
3 Digital Image Library servlet components | |
4 | |
5 Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) | |
6 | |
7 This program is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
9 Free Software Foundation; either version 2 of the License, or (at your | |
10 option) any later version. | |
11 | |
12 Please read license.txt for the full details. A copy of the GPL | |
13 may be found at http://www.gnu.org/copyleft/lgpl.html | |
14 | |
15 You should have received a copy of the GNU General Public License | |
16 along with this program; if not, write to the Free Software | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 * Created on 25.02.2003 | |
20 */ | |
21 | |
22 package digilib.io; | |
23 | |
24 import java.io.File; | |
25 import java.io.IOException; | |
26 import java.util.ArrayList; | |
27 import java.util.Arrays; | |
28 import java.util.Collections; | |
29 import java.util.Iterator; | |
30 import java.util.List; | |
31 import java.util.Map; | |
32 | |
33 import org.xml.sax.SAXException; | |
34 | |
35 /** | |
36 * @author casties | |
37 */ | |
38 public class DocuDirectory extends Directory { | |
39 | |
40 /** list of files (DocuDirent) */ | |
41 private ArrayList[] list = null; | |
42 | |
43 /** directory object is valid (exists on disk) */ | |
44 private boolean isValid = false; | |
45 | |
46 /** reference of the parent DocuDirCache */ | |
47 private DocuDirCache cache = null; | |
48 | |
49 /** directory name (digilib canonical form) */ | |
50 private String dirName = null; | |
51 | |
52 /** directory metadata */ | |
53 private Map dirMeta = null; | |
54 | |
55 /** state of metadata is valid */ | |
56 private boolean metaChecked = false; | |
57 | |
58 /** unresolved file metadata */ | |
59 private Map unresolvedFileMeta = null; | |
60 | |
61 /** time of last access of this object (not the filesystem) */ | |
62 private long objectATime = 0; | |
63 | |
64 /** time directory was last modified on the file system */ | |
65 private long dirMTime = 0; | |
66 | |
67 /** | |
68 * Constructor with digilib directory path and a parent DocuDirCache. | |
69 * | |
70 * Directory names at the given path are appended to the base directories | |
71 * from the cache. The directory is checked on disk and isValid is set. | |
72 * | |
73 * @see readDir | |
74 * | |
75 * @param path | |
76 * digilib directory path name | |
77 * @param cache | |
78 * parent DocuDirCache | |
79 */ | |
80 public DocuDirectory(String path, DocuDirCache cache) { | |
81 this.dirName = path; | |
82 this.cache = cache; | |
83 initDir(); | |
84 checkDir(); | |
85 } | |
86 | |
87 /** | |
88 * Sets and checks the dir object. | |
89 * | |
90 */ | |
91 protected void initDir() { | |
92 String baseDirName = cache.getBaseDirNames()[0]; | |
93 // clear directory first | |
94 list = new ArrayList[FileOps.NUM_CLASSES]; | |
95 isValid = false; | |
96 dirMTime = 0; | |
97 // the first directory has to exist | |
98 dir = new File(baseDirName, dirName); | |
99 } | |
100 | |
101 /** | |
102 * number of DocuFiles in this directory. | |
103 * | |
104 */ | |
105 public int size() { | |
106 return ((list != null) && (list[0] != null)) ? list[0].size() : 0; | |
107 } | |
108 | |
109 /** | |
110 * number of files of this class in this directory. | |
111 * | |
112 * @param fc | |
113 * fileClass | |
114 */ | |
115 public int size(int fc) { | |
116 return ((list != null) && (list[fc] != null)) ? list[fc].size() : 0; | |
117 } | |
118 | |
119 /** | |
120 * Returns the ImageFile at the index. | |
121 * | |
122 * @param index | |
123 * @return | |
124 */ | |
125 public ImageFileset get(int index) { | |
126 if ((list == null) || (list[0] == null) || (index >= list[0].size())) { | |
127 return null; | |
128 } | |
129 return (ImageFileset) list[0].get(index); | |
130 } | |
131 | |
132 /** | |
133 * Returns the file of the class at the index. | |
134 * | |
135 * @param index | |
136 * @param fc | |
137 * fileClass | |
138 * @return | |
139 */ | |
140 public DocuDirent get(int index, int fc) { | |
141 if ((list == null) || (list[fc] == null) || (index >= list[fc].size())) { | |
142 return null; | |
143 } | |
144 return (DocuDirent) list[fc].get(index); | |
145 } | |
146 | |
147 /** | |
148 * Checks if the directory exists on the filesystem. | |
149 * | |
150 * Sets isValid. | |
151 * | |
152 * @return | |
153 */ | |
154 public boolean checkDir() { | |
155 if (dir == null) { | |
156 initDir(); | |
157 } | |
158 isValid = dir.isDirectory(); | |
159 return isValid; | |
160 } | |
161 | |
162 /** | |
163 * Read the filesystem directory and fill this object. | |
164 * | |
165 * Clears the List and (re)reads all files. | |
166 * | |
167 * @return boolean the directory exists | |
168 */ | |
169 public synchronized boolean readDir() { | |
170 // check directory first | |
171 checkDir(); | |
172 if (!isValid) { | |
173 return false; | |
174 } | |
175 // first file extension to try for scaled directories | |
176 String scalext = null; | |
177 // read all filenames | |
178 //logger.debug("reading directory " + dir.getPath()); | |
179 /* | |
180 * using ReadableFileFilter is safer (we won't get directories with file | |
181 * extensions) but slower. | |
182 */ | |
183 File[] allFiles = null; | |
184 if (cache.safeDirIndex) { | |
185 allFiles = dir.listFiles(new FileOps.ReadableFileFilter()); | |
186 } else { | |
187 allFiles = dir.listFiles(); | |
188 } | |
189 //logger.debug(" done"); | |
190 if (allFiles == null) { | |
191 // not a directory | |
192 return false; | |
193 } | |
194 // list of base dirs from the parent cache | |
195 String[] baseDirNames = cache.getBaseDirNames(); | |
196 // number of base dirs | |
197 int nb = baseDirNames.length; | |
198 // array of base dirs | |
199 Directory[] dirs = new Directory[nb]; | |
200 // first entry is this directory | |
201 dirs[0] = this; | |
202 // fill array with the remaining directories | |
203 for (int j = 1; j < nb; j++) { | |
204 File d = new File(baseDirNames[j], dirName); | |
205 if (d.isDirectory()) { | |
206 dirs[j] = new Directory(d); | |
207 //logger.debug(" reading scaled directory " + d.getPath()); | |
208 dirs[j].readDir(); | |
209 //logger.debug(" done"); | |
210 } | |
211 } | |
212 | |
213 // go through all file classes | |
214 for (int classIdx = 0; classIdx < FileOps.NUM_CLASSES; classIdx++) { | |
215 int fileClass = cache.getFileClasses()[classIdx]; | |
216 //logger.debug("filtering directory "+dir.getPath()+" for class | |
217 // "+fc); | |
218 File[] fileList = FileOps.listFiles(allFiles, FileOps | |
219 .filterForClass(fileClass)); | |
220 //logger.debug(" done"); | |
221 // number of files in the directory | |
222 int numFiles = fileList.length; | |
223 if (numFiles > 0) { | |
224 // create new list | |
225 list[fileClass] = new ArrayList(numFiles); | |
226 // sort the file names alphabetically and iterate the list | |
227 Arrays.sort(fileList); | |
228 Map hints = FileOps.newHints(FileOps.HINT_BASEDIRS, dirs); | |
229 hints.put(FileOps.HINT_FILEEXT, scalext); | |
230 for (int i = 0; i < numFiles; i++) { | |
231 DocuDirent f = FileOps.fileForClass(fileClass, fileList[i], | |
232 hints); | |
233 // add the file to our list | |
234 list[fileClass].add(f); | |
235 f.setParent(this); | |
236 } | |
237 } | |
238 } | |
239 // clear the scaled directories | |
240 for (int j = 1; j < nb; j++) { | |
241 if (dirs[j] != null) { | |
242 dirs[j].clearFilenames(); | |
243 } | |
244 } | |
245 // update number of cached files if this was the first time | |
246 if (dirMTime == 0) { | |
247 cache.numFiles += size(); | |
248 } | |
249 dirMTime = dir.lastModified(); | |
250 // read metadata as well | |
251 readMeta(); | |
252 return isValid; | |
253 } | |
254 | |
255 /** | |
256 * Check to see if the directory has been modified and reread if necessary. | |
257 * | |
258 * @return boolean the directory is valid | |
259 */ | |
260 public boolean refresh() { | |
261 if (isValid) { | |
262 if (dir.lastModified() > dirMTime) { | |
263 // on-disk modification time is more recent | |
264 readDir(); | |
265 } | |
266 touch(); | |
267 } | |
268 return isValid; | |
269 } | |
270 | |
271 /** | |
272 * Read directory metadata. | |
273 * | |
274 */ | |
275 public void readMeta() { | |
276 // check for directory metadata... | |
277 File mf = new File(dir, "index.meta"); | |
278 if (mf.canRead()) { | |
279 XMLMetaLoader ml = new XMLMetaLoader(); | |
280 try { | |
281 // read directory meta file | |
282 Map fileMeta = ml.loadURL(mf.getAbsolutePath()); | |
283 if (fileMeta == null) { | |
284 return; | |
285 } | |
286 // meta for the directory itself is in the "" bin | |
287 dirMeta = (Map) fileMeta.remove(""); | |
288 // read meta for files in this directory | |
289 readFileMeta(fileMeta, null); | |
290 // is there meta for other files left? | |
291 if (fileMeta.size() > 0) { | |
292 unresolvedFileMeta = fileMeta; | |
293 } | |
294 } catch (SAXException e) { | |
295 logger.warn("error parsing index.meta", e); | |
296 } catch (IOException e) { | |
297 logger.warn("error reading index.meta", e); | |
298 } | |
299 } | |
300 readParentMeta(); | |
301 metaChecked = true; | |
302 } | |
303 | |
304 /** | |
305 * Read metadata from all known parent directories. | |
306 * | |
307 */ | |
308 public void readParentMeta() { | |
309 // check the parent directories for additional file meta | |
310 Directory dd = parent; | |
311 String path = dir.getName(); | |
312 while (dd != null) { | |
313 if (((DocuDirectory) dd).hasUnresolvedFileMeta()) { | |
314 readFileMeta(((DocuDirectory) dd).unresolvedFileMeta, path); | |
315 } | |
316 // prepend parent dir path | |
317 path = dd.dir.getName() + "/" + path; | |
318 // become next parent | |
319 dd = dd.parent; | |
320 } | |
321 } | |
322 | |
323 /** | |
324 * Read metadata for the files in this directory. | |
325 * | |
326 * Takes a Map with meta-information, adding the relative path before the | |
327 * lookup. | |
328 * | |
329 * @param fileMeta | |
330 * @param relPath | |
331 * @param fc | |
332 * fileClass | |
333 */ | |
334 protected void readFileMeta(Map fileMeta, String relPath) { | |
335 if (list == null) { | |
336 // there are no files | |
337 return; | |
338 } | |
339 String path = (relPath != null) ? (relPath + "/") : ""; | |
340 // go through all file classes | |
341 for (int nc = 0; nc < list.length; nc++) { | |
342 int fc = cache.getFileClasses()[nc]; | |
343 if (list[fc] == null) { | |
344 continue; | |
345 } | |
346 // iterate through the list of files in this directory | |
347 for (Iterator i = list[fc].iterator(); i.hasNext();) { | |
348 DocuDirent f = (DocuDirent) i.next(); | |
349 // prepend path to the filename | |
350 String fn = path + f.getName(); | |
351 // look up meta for this file and remove from dir | |
352 Map meta = (Map) fileMeta.remove(fn); | |
353 if (meta != null) { | |
354 // store meta in file | |
355 f.setFileMeta(meta); | |
356 } | |
357 } | |
358 } | |
359 } | |
360 | |
361 protected void notifyChildMeta(Map childmeta) { | |
362 List children = cache.getChildren(this.dirName, true); | |
363 if (children.size() > 0) { | |
364 for (Iterator i = children.iterator(); i.hasNext();) { | |
365 // TODO: finish this! | |
366 //((DocuDirectory) i.next()).readFileMeta() | |
367 } | |
368 } | |
369 } | |
370 | |
371 /** | |
372 * Update access time. | |
373 * | |
374 * @return long time of last access. | |
375 */ | |
376 public long touch() { | |
377 long t = objectATime; | |
378 objectATime = System.currentTimeMillis(); | |
379 return t; | |
380 } | |
381 | |
382 /** | |
383 * Searches for the file with the name <code>fn</code>. | |
384 * | |
385 * Searches the directory for the file with the name <code>fn</code> and | |
386 * returns its index. Returns -1 if the file cannot be found. | |
387 * | |
388 * @param fn | |
389 * filename | |
390 * @param fc | |
391 * file class | |
392 * @return int index of file <code>fn</code> | |
393 */ | |
394 public int indexOf(String fn) { | |
395 int fc = FileOps.classForFilename(fn); | |
396 return indexOf(fn, fc); | |
397 } | |
398 | |
399 /** | |
400 * Searches for the file with the name <code>fn</code> and class fc. | |
401 * | |
402 * Searches the directory for the file with the name <code>fn</code> and | |
403 * returns its index. Returns -1 if the file cannot be found. | |
404 * | |
405 * @param fn | |
406 * filename | |
407 * @return int index of file <code>fn</code> | |
408 */ | |
409 public int indexOf(String fn, int fc) { | |
410 if (!isRead()) { | |
411 // read directory now | |
412 if (!readDir()) { | |
413 return -1; | |
414 } | |
415 } | |
416 List fileList = list[fc]; | |
417 // empty directory? | |
418 if (fileList == null) { | |
419 return -1; | |
420 } | |
421 // search for exact match | |
422 int idx = Collections.binarySearch(fileList, fn); | |
423 if (idx >= 0) { | |
424 return idx; | |
425 } else { | |
426 // try closest matches without extension | |
427 idx = -idx - 1; | |
428 String fb = FileOps.basename(fn); | |
429 DocuDirent fs; | |
430 if ((idx < fileList.size()) | |
431 && (FileOps.basename(((DocuDirent) fileList.get(idx)) | |
432 .getName()).equals(fb))) { | |
433 // idx matches | |
434 return idx; | |
435 } else if ((idx > 0) | |
436 && (FileOps.basename(((DocuDirent) fileList.get(idx - 1)) | |
437 .getName()).equals(fb))) { | |
438 // idx-1 matches | |
439 return idx - 1; | |
440 } else if ((idx + 1 < fileList.size()) | |
441 && (FileOps.basename(((DocuDirent) fileList.get(idx - 1)) | |
442 .getName()).equals(fb))) { | |
443 // idx+1 matches | |
444 return idx + 1; | |
445 } | |
446 | |
447 } | |
448 return -1; | |
449 } | |
450 | |
451 /** | |
452 * Finds the DocuDirent with the name <code>fn</code>. | |
453 * | |
454 * Searches the directory for the DocuDirent with the name <code>fn</code> | |
455 * and returns it. Returns null if the file cannot be found. | |
456 * | |
457 * @param fn | |
458 * filename | |
459 * @return DocuDirent | |
460 */ | |
461 public DocuDirent find(String fn) { | |
462 int fc = FileOps.classForFilename(fn); | |
463 int i = indexOf(fn, fc); | |
464 if (i >= 0) { | |
465 return (DocuDirent) list[0].get(i); | |
466 } | |
467 return null; | |
468 } | |
469 | |
470 /** | |
471 * Finds the DocuDirent with the name <code>fn</code> and class | |
472 * <code>fc</code>. | |
473 * | |
474 * Searches the directory for the DocuDirent with the name <code>fn</code> | |
475 * and returns it. Returns null if the file cannot be found. | |
476 * | |
477 * @param fn | |
478 * filename | |
479 * @return DocuDirent | |
480 */ | |
481 public DocuDirent find(String fn, int fc) { | |
482 int i = indexOf(fn, fc); | |
483 if (i >= 0) { | |
484 return (DocuDirent) list[fc].get(i); | |
485 } | |
486 return null; | |
487 } | |
488 | |
489 /** | |
490 * Returns the digilib canonical name. | |
491 * | |
492 * @return | |
493 */ | |
494 public String getDirName() { | |
495 return dirName; | |
496 } | |
497 | |
498 /** | |
499 * The directory is valid (exists on disk). | |
500 * | |
501 * @return boolean | |
502 */ | |
503 public boolean isValid() { | |
504 return isValid; | |
505 } | |
506 | |
507 /** | |
508 * The directory has been read from disk. | |
509 * | |
510 * @return | |
511 */ | |
512 public boolean isRead() { | |
513 return (dirMTime != 0); | |
514 } | |
515 | |
516 /** | |
517 * @return long | |
518 */ | |
519 public long getAccessTime() { | |
520 return objectATime; | |
521 } | |
522 | |
523 /** | |
524 * @return Hashtable | |
525 */ | |
526 public Map getDirMeta() { | |
527 return dirMeta; | |
528 } | |
529 | |
530 /** | |
531 * Checks metadata | |
532 * | |
533 */ | |
534 public void checkMeta() { | |
535 if (metaChecked) { | |
536 return; | |
537 } else { | |
538 readMeta(); | |
539 } | |
540 } | |
541 | |
542 /** | |
543 * @return long | |
544 */ | |
545 public long getDirMTime() { | |
546 return dirMTime; | |
547 } | |
548 | |
549 /** | |
550 * Sets the dirMeta. | |
551 * | |
552 * @param dirMeta | |
553 * The dirMeta to set | |
554 */ | |
555 public void setDirMeta(Map dirMeta) { | |
556 this.dirMeta = dirMeta; | |
557 } | |
558 | |
559 public boolean hasUnresolvedFileMeta() { | |
560 return (this.unresolvedFileMeta != null); | |
561 } | |
562 | |
563 } |