changeset 259:beed92ee6022

Servlet version 1.21b1 - directory indexing got faster but less safe (configurable by "safe-dir-index") - mo=rawfile supplies filename - DigilibConfig takes File parameters - some SerialVersionUIDs (suggested by Eclipse)
author robcast
date Mon, 11 Oct 2004 21:23:00 +0200
parents 7a89d105f526
children 46fc3f0bd7c2
files servlet/src/digilib/auth/XMLAuthOps.java servlet/src/digilib/io/AliasingDocuDirCache.java servlet/src/digilib/io/Directory.java servlet/src/digilib/io/DocuDirCache.java servlet/src/digilib/io/DocuDirectory.java servlet/src/digilib/io/DocuDirent.java servlet/src/digilib/io/FileOps.java servlet/src/digilib/io/ImageFileset.java servlet/src/digilib/servlet/DigilibConfiguration.java
diffstat 9 files changed, 350 insertions(+), 228 deletions(-) [+]
line wrap: on
line diff
--- a/servlet/src/digilib/auth/XMLAuthOps.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/auth/XMLAuthOps.java	Mon Oct 11 21:23:00 2004 +0200
@@ -36,28 +36,27 @@
  */
 public class XMLAuthOps extends AuthOpsImpl {
 
-	private String configFile =
-		"/docuserver/www/digitallibrary/WEB-INF/digilib-auth.xml";
+	private File configFile;
 	private HashTree authPaths;
 	private HashTree authIPs;
 
-	/** Constructor taking an XML config file name and utils object.
+	/** Constructor taking an XML config file.
 	 *
 	 * @param u utils object
-	 * @param confFile Configuration file name.
+	 * @param confFile Configuration file.
 	 * @throws AuthOpException Exception thrown on error.
 	 */
-	public XMLAuthOps(String confFile) throws AuthOpException {
+	public XMLAuthOps(File confFile) throws AuthOpException {
 		configFile = confFile;
 		init();
 	}
 
-	/** Set configuration file and utils object.
+	/** Set configuration file.
 	 *
-	 * @param confFile XML config file name.
+	 * @param confFile XML config file.
 	 * @throws AuthOpException Exception thrown on error.
 	 */
-	public void setConfig(String confFile) throws AuthOpException {
+	public void setConfig(File confFile) throws AuthOpException {
 		configFile = confFile;
 		init();
 	}
@@ -73,16 +72,14 @@
 		HashMap pathList = null;
 		HashMap ipList = null;
 		try {
-			// create data loader for auth-path file
-			File confFile = new File(configFile);
 			// load authPaths
 			XMLListLoader pathLoader =
 				new XMLListLoader("digilib-paths", "path", "name", "role");
-			pathList = pathLoader.loadURL(confFile.toURL().toString());
+			pathList = pathLoader.loadURL(configFile.toURL().toString());
 			// load authIPs
 			XMLListLoader ipLoader =
 				new XMLListLoader("digilib-addresses", "address", "ip", "role");
-			ipList = ipLoader.loadURL(confFile.toURL().toString());
+			ipList = ipLoader.loadURL(configFile.toURL().toString());
 		} catch (Exception e) {
 			throw new AuthOpException(
 				"ERROR loading authorization config file: " + e);
--- a/servlet/src/digilib/io/AliasingDocuDirCache.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/io/AliasingDocuDirCache.java	Mon Oct 11 21:23:00 2004 +0200
@@ -26,6 +26,8 @@
 import java.util.HashMap;
 import java.util.Iterator;
 
+import digilib.servlet.DigilibConfiguration;
+
 /**
  * @author casties
  *  
@@ -38,21 +40,17 @@
 	 * @param confFileName
 	 * @throws FileOpException
 	 */
-	public AliasingDocuDirCache(
-		String[] baseDirs,
-		int[] fileClasses,
-		String confFileName)
-		throws FileOpException {
+	public AliasingDocuDirCache(String[] baseDirs, int[] fileClasses,
+			File confFile, DigilibConfiguration dlConfig)
+			throws FileOpException {
 		// create standard DocuDirCache
-		super(baseDirs, fileClasses);
+		super(baseDirs, fileClasses, dlConfig);
 		HashMap pathMap = null;
 		// read alias config file
 		try {
-			// create data loader for mapping-file
-			File confFile = new File(confFileName);
 			// load into pathMap
-			XMLListLoader mapLoader =
-				new XMLListLoader("digilib-aliases", "mapping", "link", "dir");
+			XMLListLoader mapLoader = new XMLListLoader("digilib-aliases",
+					"mapping", "link", "dir");
 			pathMap = mapLoader.loadURL(confFile.toURL().toString());
 		} catch (Exception e) {
 			throw new FileOpException("ERROR loading mapping file: " + e);
@@ -64,13 +62,13 @@
 		/*
 		 * load map entries into cache
 		 */
-		
+
 		for (Iterator i = pathMap.keySet().iterator(); i.hasNext();) {
-			String link = FileOps.normalName((String)i.next());
+			String link = FileOps.normalName((String) i.next());
 			String dir = (String) pathMap.get(link);
 			DocuDirectory destDir = new DocuDirectory(dir, this);
 			if (destDir.isValid()) {
-				logger.debug("Aliasing dir: "+link);
+				logger.debug("Aliasing dir: " + link);
 				// add the alias name
 				putName(link, destDir);
 				// add the real dir
@@ -79,17 +77,19 @@
 		}
 	}
 
-	/** Adds a DocuDirectory under another name to the cache.
+	/**
+	 * Adds a DocuDirectory under another name to the cache.
 	 * 
-	 * @param name 
+	 * @param name
 	 * @param newdir
 	 */
 	public void putName(String name, DocuDirectory newdir) {
 		if (map.containsKey(name)) {
-			logger.warn("Duplicate key in AliasingDocuDirCache.put -- ignored!");
+			logger
+					.warn("Duplicate key in AliasingDocuDirCache.put -- ignored!");
 		} else {
 			map.put(name, newdir);
 		}
 	}
-	
+
 }
--- a/servlet/src/digilib/io/Directory.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/io/Directory.java	Mon Oct 11 21:23:00 2004 +0200
@@ -22,16 +22,24 @@
 package digilib.io;
 
 import java.io.File;
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
 
 /** Class for filesystem directories
  * @author casties
  *
  */
 public class Directory {
-	// File object pointing to the directory
+
+	protected Logger logger = Logger.getLogger(this.getClass());
+
+	/** File object pointing to the directory */
 	File dir = null;
-	// parent directory
+	/** parent directory */
 	Directory parent = null;
+	/** list of filenames in the directory */
+	protected String[] list = null;
 
 	/** Default constructor.
 	 * 
@@ -66,6 +74,22 @@
 		dir = new File(dn);
 	}
 	
+	
+	/** Reads the names of the files in the directory.
+	 * Fills the filenames array. Returns if the operation was successful.
+	 * 
+	 * @return
+	 */
+	public boolean readDir() {
+		if (dir != null) {
+			System.out.println("start reading dir");
+			list = dir.list();
+			Arrays.sort(list);
+			System.out.println("done reading dir");
+		}
+		return (list != null);
+	}
+	
 	/**
 	 * @return
 	 */
@@ -95,4 +119,16 @@
 	}
 
 
+	/**
+	 * @return Returns the filenames.
+	 */
+	public String[] getFilenames() {
+		return list;
+	}
+	/**
+	 * @param filenames The filenames to set.
+	 */
+	public void setFilenames(String[] filenames) {
+		this.list = filenames;
+	}
 }
--- a/servlet/src/digilib/io/DocuDirCache.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/io/DocuDirCache.java	Mon Oct 11 21:23:00 2004 +0200
@@ -30,6 +30,8 @@
 
 import org.apache.log4j.Logger;
 
+import digilib.servlet.DigilibConfiguration;
+
 /**
  * @author casties
  */
@@ -49,6 +51,8 @@
 	private long hits = 0;
 	/** number of cache misses */
 	private long misses = 0;
+	/** use safe (but slow) indexing */
+	boolean safeDirIndex = false;
 
 	/**
 	 * Constructor with array of base directory names and file classes.
@@ -56,10 +60,11 @@
 	 * @param bd
 	 *            base directory names
 	 */
-	public DocuDirCache(String[] bd, int[] fileClasses) {
+	public DocuDirCache(String[] bd, int[] fileClasses, DigilibConfiguration dlConfig) {
 		baseDirNames = bd;
 		map = new HashMap();
 		this.fileClasses = fileClasses;
+		safeDirIndex = dlConfig.getAsBoolean("safe-dir-index");
 	}
 	/**
 	 * Constructor with array of base directory names.
--- a/servlet/src/digilib/io/DocuDirectory.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/io/DocuDirectory.java	Mon Oct 11 21:23:00 2004 +0200
@@ -28,6 +28,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import org.xml.sax.SAXException;
 
@@ -179,17 +180,33 @@
 			File d = new File(baseDirNames[j], dirName);
 			if (d.isDirectory()) {
 				dirs[j] = new Directory(d);
+				dirs[j].readDir();
 			}
 		}
 
+		// read all filenames
+		logger.debug("reading directory "+dir.getPath());
+		/*
+		 * using ReadableFileFilter is safer (we won't get directories
+		 * with file extensions) but slower.
+		 */
+		File[] allFiles = null;
+		if (cache.safeDirIndex) {
+			allFiles = dir.listFiles(new FileOps.ReadableFileFilter());
+		} else {
+			allFiles = dir.listFiles();
+		}
+		logger.debug("  done");
+		if (allFiles == null) {
+			// not a directory
+			return false;
+		}
 		// go through all file classes
 		for (int nc = 0; nc < FileOps.NUM_CLASSES; nc++) {
 			int fc = cache.getFileClasses()[nc];
-			File[] fl = dir.listFiles(FileOps.filterForClass(fc));
-			if (fl == null) {
-				// not a directory
-				return false;
-			}
+			//logger.debug("filtering directory "+dir.getPath()+" for class "+fc);
+			File[] fl = FileOps.listFiles(allFiles, FileOps.filterForClass(fc));
+			//logger.debug("  done");
 			// number of files in the directory
 			int nf = fl.length;
 			if (nf > 0) {
@@ -197,8 +214,10 @@
 				list[fc] = new ArrayList(nf);
 				// sort the file names alphabetically and iterate the list
 				Arrays.sort(fl);
+				Map hints = FileOps.newHints(FileOps.HINT_BASEDIRS, dirs);
+				hints.put(FileOps.HINT_FILEEXT, scalext);
 				for (int i = 0; i < nf; i++) {
-					DocuDirent f = FileOps.fileForClass(fc, fl[i], dirs, scalext);
+					DocuDirent f = FileOps.fileForClass(fc, fl[i], hints);
 					// add the file to our list
 					list[fc].add(f);
 					f.setParent(this);
@@ -381,7 +400,7 @@
 		// try again without extension
 		for (int i = 0; i < n; i++) {
 			DocuDirent fs = (DocuDirent) list[fc].get(i);
-			if (fs.getBasename().equals(FileOps.basename(fn))) {
+			if (FileOps.basename(fs.getName()).equals(FileOps.basename(fn))) {
 				// basename matches
 				return i;
 			}
--- a/servlet/src/digilib/io/DocuDirent.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/io/DocuDirent.java	Mon Oct 11 21:23:00 2004 +0200
@@ -95,55 +95,56 @@
 	public String getName() {
 		File f = getFile();
 		return (f != null) ? f.getName() : null;
-	} /**
-	   * The filename sans extension.
-	   * 
-	   * @return
-	   */
-	public String getBasename() {
-		File f = getFile();
-		if (f == null) {
-			return null;
-		}
-		return FileOps.basename(f.getName());
-	} /**
-	   * Returns the parent Directory.
-	   * 
-	   * @return DocuDirectory
-	   */
+	} 
+	
+	/**
+	 * Returns the parent Directory.
+	 * 
+	 * @return DocuDirectory
+	 */
 	public Directory getParent() {
 		return parent;
-	} /**
-	   * Sets the parent Directory.
-	   * 
-	   * @param parent
-	   *            The parent to set
-	   */
+	}
+	
+	/**
+	 * Sets the parent Directory.
+	 * 
+	 * @param parent
+	 *            The parent to set
+	 */
 	public void setParent(Directory parent) {
 		this.parent = parent;
-	} /**
-	   * Returns the meta-data for this file(set).
-	   * 
-	   * @return HashMap
-	   */
+	} 
+	
+	/**
+	 * Returns the meta-data for this file(set).
+	 * 
+	 * @return HashMap
+	 */
 	public HashMap getFileMeta() {
 		return fileMeta;
-	} /**
-	   * Sets the meta-data for this file(set) .
-	   * 
-	   * @param fileMeta
-	   *            The fileMeta to set
-	   */
+	} 
+	
+	/**
+	 * Sets the meta-data for this file(set) .
+	 * 
+	 * @param fileMeta
+	 *            The fileMeta to set
+	 */
 	public void setFileMeta(HashMap fileMeta) {
 		this.fileMeta = fileMeta;
-	} /**
-	   * @return
-	   */
+	} 
+	
+	/**
+	 * @return
+	 */
 	public boolean isMetaChecked() {
 		return metaChecked;
-	} /**
-	   * @return
-	   */
+	} 
+	
+	/**
+	 * @return
+	 */
 	public static int getFileClass() {
 		return fileClass;
 	}
--- a/servlet/src/digilib/io/FileOps.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/io/FileOps.java	Mon Oct 11 21:23:00 2004 +0200
@@ -23,63 +23,97 @@
 
 import java.io.File;
 import java.io.FileFilter;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 import java.util.StringTokenizer;
 
 public class FileOps {
 
-	public static String[] fileTypes =
-		{
-			"jpg",
-			"image/jpeg",
-			"jpeg",
-			"image/jpeg",
-			"jp2",
-			"image/jp2",
-			"png",
-			"image/png",
-			"gif",
-			"image/gif",
-			"tif",
-			"image/tiff",
-			"tiff",
-			"image/tiff",
-			"txt",
-			"text/plain",
-			"html",
-			"text/html",
-			"htm",
-			"text/html",
-			"xml",
-			"text/xml",
-			"svg",
-			"image/svg+xml" };
+	/**
+	 * Array of file extensions and corresponding mime-types.
+	 */
+	private static String[][] ft = { { "jpg", "image/jpeg" },
+			{ "jpeg", "image/jpeg" }, { "jp2", "image/jp2" },
+			{ "png", "image/png" }, { "gif", "image/gif" },
+			{ "tif", "image/tiff" }, { "tiff", "image/tiff" },
+			{ "txt", "text/plain" }, { "html", "text/html" },
+			{ "htm", "text/html" }, { "xml", "text/xml" },
+			{ "svg", "image/svg+xml" } };
 
-	public static String[] imageExtensions =
-		{ "jpg", "jpeg", "jp2", "png", "gif", "tif", "tiff" };
+	public static Map fileTypes;
+
+	public static List imageExtensions;
 
-	public static String[] textExtensions = { "txt", "html", "htm", "xml" };
+	public static List textExtensions;
 
-	public static String[] svgExtensions = { "svg" };
+	public static List svgExtensions;
 
 	public static final int CLASS_NONE = -1;
+
 	public static final int CLASS_IMAGE = 0;
+
 	public static final int CLASS_TEXT = 1;
+
 	public static final int CLASS_SVG = 2;
+
 	public static final int NUM_CLASSES = 3;
 
+	public static final Integer HINT_BASEDIRS = new Integer(1);
+
+	public static final Integer HINT_FILEEXT = new Integer(2);
+
+	public static final Integer HINT_DIRS = new Integer(3);
+
+	/**
+	 * static initializer for FileOps
+	 */
+	static {
+		fileTypes = new HashMap();
+		imageExtensions = new ArrayList();
+		textExtensions = new ArrayList();
+		svgExtensions = new ArrayList();
+		for (int i = 0; i < ft.length; i++) {
+			String ext = ft[i][0];
+			String mt = ft[i][1];
+			fileTypes.put(ext, mt);
+			if (classForMimetype(mt) == CLASS_IMAGE) {
+				imageExtensions.add(ext);
+			} else if (classForMimetype(mt) == CLASS_TEXT) {
+				textExtensions.add(ext);
+			} else if (classForMimetype(mt) == CLASS_SVG) {
+				svgExtensions.add(ext);
+			}
+		}
+	}
+
+	/**
+	 * returns the file class for a mime-type
+	 * 
+	 * @param mt
+	 * @return
+	 */
+	public static int classForMimetype(String mt) {
+		if (mt == null) {
+			return CLASS_NONE;
+		}
+		if (mt.startsWith("image/svg")) {
+			return CLASS_SVG;
+		} else if (mt.startsWith("image")) {
+			return CLASS_IMAGE;
+		} else if (mt.startsWith("text")) {
+			return CLASS_TEXT;
+		}
+		return CLASS_NONE;
+	}
+
 	/**
 	 * get the mime type for a file format (by extension)
 	 */
 	public static String mimeForFile(File f) {
-		String fn = f.getName();
-		for (int i = 0; i < fileTypes.length; i += 2) {
-			if (fn.toLowerCase().endsWith(fileTypes[i])) {
-				return fileTypes[i + 1];
-			}
-		}
-		return null;
+		return (String) fileTypes.get(extname(f.getName().toLowerCase()));
 	}
 
 	/**
@@ -89,32 +123,20 @@
 	 * @return
 	 */
 	public static int classForFilename(String fn) {
-		int n = imageExtensions.length;
-		for (int i = 0; i < n; i++) {
-			if (fn.toLowerCase().endsWith(imageExtensions[i])) {
-				return CLASS_IMAGE;
-			}
-		}
-		n = textExtensions.length;
-		for (int i = 0; i < n; i++) {
-			if (fn.toLowerCase().endsWith(textExtensions[i])) {
-				return CLASS_TEXT;
-			}
-		}
-		return CLASS_NONE;
-
+		String mt = (String) fileTypes.get(extname(fn).toLowerCase());
+		return classForMimetype(mt);
 	}
 
 	public static Iterator getImageExtensionIterator() {
-		return Arrays.asList(imageExtensions).iterator();
+		return imageExtensions.iterator();
 	}
 
 	public static Iterator getTextExtensionIterator() {
-		return Arrays.asList(textExtensions).iterator();
+		return textExtensions.iterator();
 	}
 
 	public static Iterator getSVGExtensionIterator() {
-		return Arrays.asList(svgExtensions).iterator();
+		return svgExtensions.iterator();
 	}
 
 	/**
@@ -164,8 +186,8 @@
 	 * Extract the extension of a file name.
 	 * 
 	 * Returns the extension of a file name. The extension is the part behind
-	 * the last dot in the filename. If the filename has no dot the empty
-	 * string is returned.
+	 * the last dot in the filename. If the filename has no dot the empty string
+	 * is returned.
 	 * 
 	 * @param fn
 	 * @return
@@ -181,9 +203,9 @@
 	/**
 	 * Extract the parent directory of a (digilib) path name.
 	 * 
-	 * Returns the parent directory of a path name. The parent is the part before
-	 * the last slash in the path name. If the path name has no slash the empty
-	 * string is returned.
+	 * Returns the parent directory of a path name. The parent is the part
+	 * before the last slash in the path name. If the path name has no slash the
+	 * empty string is returned.
 	 * 
 	 * @param fn
 	 * @return
@@ -196,10 +218,11 @@
 		return "";
 	}
 
-	/** Normalize a path name.
+	/**
+	 * Normalize a path name.
 	 * 
 	 * Removes leading and trailing slashes. Returns null if there is other
-	 * unwanted stuff in the path name. 
+	 * unwanted stuff in the path name.
 	 * 
 	 * @param pathname
 	 * @return
@@ -219,25 +242,28 @@
 			a++;
 		}
 		while ((a < e) && (pathname.charAt(e) == '/')) {
-			e--; 
+			e--;
 		}
 		return pathname.substring(a, e + 1);
 	}
-	
-	
+
+	/**
+	 * FileFilter for general files
+	 */
+	static class ReadableFileFilter implements FileFilter {
+
+		public boolean accept(File f) {
+			return f.canRead();
+		}
+	}
+
 	/**
 	 * FileFilter for image types (helper class for getFile)
 	 */
 	static class ImageFileFilter implements FileFilter {
 
 		public boolean accept(File f) {
-			if (f.isFile()) {
-				return (
-					(mimeForFile(f) != null)
-						&& (mimeForFile(f).startsWith("image")));
-			} else {
-				return false;
-			}
+			return (classForFilename(f.getName()) == CLASS_IMAGE);
 		}
 	}
 
@@ -247,13 +273,7 @@
 	static class TextFileFilter implements FileFilter {
 
 		public boolean accept(File f) {
-			if (f.isFile()) {
-				return (
-					(mimeForFile(f) != null)
-						&& (mimeForFile(f).startsWith("text")));
-			} else {
-				return false;
-			}
+			return (classForFilename(f.getName()) == CLASS_TEXT);
 		}
 	}
 
@@ -264,13 +284,7 @@
 	static class SVGFileFilter implements FileFilter {
 
 		public boolean accept(File f) {
-			if (f.isFile()) {
-				return (
-					(mimeForFile(f) != null)
-						&& (mimeForFile(f).startsWith("image/svg")));
-			} else {
-				return false;
-			}
+			return (classForFilename(f.getName()) == CLASS_SVG);
 		}
 	}
 
@@ -293,33 +307,70 @@
 		return null;
 	}
 
-	/** Factory for DocuDirents based on file class.
+	/**
+	 * Factory for DocuDirents based on file class.
 	 * 
-	 * Returns an ImageFileset, TextFile or SVGFile. 
-	 * baseDirs and scalext are only for ImageFilesets.
+	 * Returns an ImageFileset, TextFile or SVGFile. baseDirs and scalext are
+	 * only for ImageFilesets.
 	 * 
 	 * @param fileClass
 	 * @param file
-	 * @param baseDirs array of base directories (for ImageFileset)
-	 * @param scalext first extension to try for scaled images (for ImageFileset)
+	 * @param hints
+	 *            optional additional parameters
 	 * @return
 	 */
-	public static DocuDirent fileForClass(
-		int fileClass,
-		File file,
-		Directory[] baseDirs,
-		String scalext) {
+	public static DocuDirent fileForClass(int fileClass, File file, Map hints) {
 		// what class of file do we have?
 		if (fileClass == CLASS_IMAGE) {
 			// image file
-			return new ImageFileset(baseDirs, file, scalext);
+			return new ImageFileset(file, hints);
 		} else if (fileClass == CLASS_TEXT) {
 			// text file
-			return  new TextFile(file);
+			return new TextFile(file);
 		} else if (fileClass == CLASS_SVG) {
 			// text file
-			return  new SVGFile(file);
+			return new SVGFile(file);
 		}
 		return null;
 	}
+
+	/**
+	 * Filters a list of Files through a FileFilter.
+	 * 
+	 * @param files
+	 * @param filter
+	 * @return
+	 */
+	public static File[] listFiles(File[] files, FileFilter filter) {
+		if (files == null) {
+			return null;
+		}
+		File[] ff = new File[files.length];
+		int ffi = 0;
+		for (int i = 0; i < files.length; i++) {
+			if (filter.accept(files[i])) {
+				ff[ffi] = files[i];
+				ffi++;
+			}
+		}
+		File[] fff = new File[ffi];
+		System.arraycopy(ff, 0, fff, 0, ffi);
+		return fff;
+	}
+
+	/**
+	 * Creates a new hints Map with the given first element.
+	 * 
+	 * @param type
+	 * @param value
+	 * @return
+	 */
+	public static Map newHints(Integer type, Object value) {
+		HashMap m = new HashMap();
+		if (type != null) {
+			m.put(type, value);
+		}
+		return m;
+	}
+
 }
--- a/servlet/src/digilib/io/ImageFileset.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/io/ImageFileset.java	Mon Oct 11 21:23:00 2004 +0200
@@ -20,8 +20,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.ListIterator;
+import java.util.Map;
 
 import digilib.image.DocuInfo;
 import digilib.image.ImageSize;
@@ -47,39 +49,32 @@
 	private double resY = 0;
 
 	/**
-	 * Creator for empty fileset with size for file list.
+	 * Creator for empty fileset.
 	 * 
 	 * 
 	 * @param initialCapacity
 	 */
-	public ImageFileset(int initialCapacity) {
-		list = new ArrayList(initialCapacity);
+	public ImageFileset() {
+		list = new ArrayList();
 	}
 
 	/**
-	 * Creator with a file and base directories.
+	 * Constructor with a file and hints.
 	 * 
-	 * Reads the file and fills the fileset with corresponding files from the
-	 * other base directories. First entry in dirs is the parent of this
-	 * fileset.
-	 * 
-	 * 
-	 * @see fill
+	 * The hints are expected to contain 'basedirs' and 'scaledfilext' keys.
 	 * 
-	 * @param dirs
-	 *            array of base directories
 	 * @param file
-	 *            first file to read
-	 * @param scalext
-	 *            extension for scaled images
+	 * @param hints
 	 */
-	public ImageFileset(Directory[] dirs, File file, String scalext) {
+	public ImageFileset(File file, Map hints) {
+		Directory[] dirs = (Directory[]) hints.get(FileOps.HINT_BASEDIRS);
 		int nb = dirs.length;
 		list = new ArrayList(nb);
 		parent = dirs[0];
-		fill(dirs, file, scalext);
+		fill(dirs, file, hints);		
 	}
-
+	
+	
 	/**
 	 * Adds an ImageFile to this Fileset.
 	 * 
@@ -234,13 +229,14 @@
 	 *            list of base directories
 	 * @param fl
 	 *            file (from first base dir)
-	 * @param scalext
-	 *            first extension to try in other base dirs
+	 * @param hints
+	 *            
 	 */
-	void fill(Directory[] dirs, File fl, String scalext) {
+	void fill(Directory[] dirs, File fl, Map hints) {
+		String scalext = (String) hints.get(FileOps.HINT_FILEEXT);
 		int nb = dirs.length;
 		String fn = fl.getName();
-		String fnx = fn.substring(0, fn.lastIndexOf('.') + 1);
+		String baseFn = FileOps.basename(fn);
 		// add the first ImageFile to the ImageFileset
 		add(new ImageFile(fn, this, parent));
 		// iterate the remaining base directories
@@ -248,31 +244,41 @@
 			if (dirs[j] == null) {
 				continue;
 			}
-			File f;
+			// read the directories
+			if (dirs[j].getFilenames() == null) {
+				dirs[j].readDir();
+			}
+			File f = null;
+			String[] dirFiles = dirs[j].getFilenames();
 			if (scalext != null) {
 				// use the last extension
-				f = new File(dirs[j].getDir(), fnx + scalext);
+				int i = Arrays.binarySearch(dirFiles, baseFn + scalext);
+				if (i >= 0) {
+					f = new File(dirs[j].getDir(), dirFiles[i]);
+				}
 			} else {
 				// try the same filename as the original
-				f = new File(dirs[j].getDir(), fn);
+				int i = Arrays.binarySearch(dirFiles, fn);
+				if (i >= 0) {
+					f = new File(dirs[j].getDir(), dirFiles[i]);
+				}
 			}
-			// if the file exists, add to the ImageFileset
-			if (f.canRead()) {
-				add(new ImageFile(f.getName(), this, dirs[j]));
-			} else {
+			// if the file doesn't exists, try other file extensions
+			if (f == null) {
 				// try other file extensions
-				Iterator exts = FileOps.getImageExtensionIterator();
-				while (exts.hasNext()) {
+				for (Iterator exts = FileOps.getImageExtensionIterator(); exts.hasNext();) {
 					String s = (String) exts.next();
-					f = new File(dirs[j].getDir(), fnx + s);
-					// if the file exists, add to the ImageFileset
-					if (f.canRead()) {
-						add(new ImageFile(f.getName(), this, dirs[j]));
-						scalext = s;
+					int i = Arrays.binarySearch(dirFiles, baseFn + s);
+					if (i >= 0) {
+						hints.put(FileOps.HINT_FILEEXT, s);
+						f = new File(dirs[j].getDir(), dirFiles[i]);
 						break;
 					}
 				}
 			}
+			if (f != null) {
+				add(new ImageFile(f.getName(), this, dirs[j]));
+			}
 		}
 	}
 
--- a/servlet/src/digilib/servlet/DigilibConfiguration.java	Mon Oct 11 21:17:37 2004 +0200
+++ b/servlet/src/digilib/servlet/DigilibConfiguration.java	Mon Oct 11 21:23:00 2004 +0200
@@ -55,6 +55,8 @@
  */
 public class DigilibConfiguration extends ParameterMap {
 
+	private static final long serialVersionUID = -6630487070791637120L;
+
 	/** DocuImage class instance */
 	private Class docuImageClass = null;
 
@@ -102,13 +104,13 @@
 		// image file to send in case of error
 		newParameter(
 			"error-image",
-			"/docuserver/images/icons/scalerror.gif",
+			new File("/docuserver/images/icons/scalerror.gif"),
 			null,
 			'f');
 		// image file to send if access is denied
 		newParameter(
 			"denied-image",
-			"/docuserver/images/icons/denied.gif",
+			new File("/docuserver/images/icons/denied.gif"),
 			null,
 			'f');
 		// base directories in order of preference (prescaled versions last)
@@ -117,7 +119,7 @@
 		// use authentication information
 		newParameter("use-authorization", Boolean.FALSE, null, 'f');
 		// authentication configuration file
-		newParameter("auth-file", "digilib-auth.xml", null, 'f');
+		newParameter("auth-file", new File("digilib-auth.xml"), null, 'f');
 		// sending image files as-is allowed
 		newParameter("sendfile-allowed", Boolean.TRUE, null, 'f');
 		// Debug level
@@ -137,11 +139,13 @@
 		// use mapping file to translate paths
 		newParameter("use-mapping", Boolean.FALSE, null, 'f');
 		// mapping file location
-		newParameter("mapping-file", "digilib-map.xml", null, 'f');
+		newParameter("mapping-file", new File("digilib-map.xml"), null, 'f');
 		// log4j config file location
-		newParameter("log-config-file", "log4j-config.xml", null, 'f');
+		newParameter("log-config-file", new File("log4j-config.xml"), null, 'f');
 		// maximum destination image size (0 means no limit)
 		newParameter("max-image-size", new Integer(0), null, 'f');
+		// use safe (but slower) directory indexing
+		newParameter("safe-dir-index", Boolean.FALSE, null, 'f');
 
 	}
 
@@ -226,9 +230,10 @@
 		 */
 
 		// set up logger
-		String logConfPath =
-			ServletOps.getConfigFile(getAsString("log-config-file"), c);
-		DOMConfigurator.configure(logConfPath);
+		File logConf =
+			ServletOps.getConfigFile((File)getValue("log-config-file"), c);
+		DOMConfigurator.configure(logConf.getAbsolutePath());
+		setValue("log-config-file", logConf);
 		// directory cache
 		String[] bd = (String[]) getValue("basedir-list");
 		int[] fcs =
@@ -236,12 +241,13 @@
 		DocuDirCache dirCache;
 		if (getAsBoolean("use-mapping")) {
 			// with mapping file
-			String mapConfPath =
-				ServletOps.getConfigFile(getAsString("mapping-file"), c);
-			dirCache = new AliasingDocuDirCache(bd, fcs, mapConfPath);
+			File mapConf =
+				ServletOps.getConfigFile((File)getValue("mapping-file"), c);
+			dirCache = new AliasingDocuDirCache(bd, fcs, mapConf, this);
+			setValue("mapping-file", mapConf);
 		} else {
 			// without mapping
-			dirCache = new DocuDirCache(bd, fcs);
+			dirCache = new DocuDirCache(bd, fcs, this);
 		}
 		setValue("servlet.dir.cache", dirCache);
 		// useAuthentication
@@ -249,10 +255,11 @@
 			// DB version
 			//authOp = new DBAuthOpsImpl(util);
 			// XML version
-			String authConfPath =
-				ServletOps.getConfigFile(getAsString("auth-file"), c);
-			AuthOps authOp = new XMLAuthOps(authConfPath);
+			File authConf =
+				ServletOps.getConfigFile((File)getValue("auth-file"), c);
+			AuthOps authOp = new XMLAuthOps(authConf);
 			setValue("servlet.auth.op", authOp);
+			setValue("auth-file", authConf);
 		}
 		// DocuImage class
 		String s = getAsString("docuimage-class");