# HG changeset patch # User robcast # Date 1098384817 -7200 # Node ID 5d0c0da080ecfef3149cf92c5ab1ae7c30dbe797 # Parent 9f7b864f955f07e2d905416e5e22bd9e02d875e2 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 :-( diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/image/ImageLoaderDocuImage.java --- a/servlet/src/digilib/image/ImageLoaderDocuImage.java Mon Oct 18 15:40:54 2004 +0200 +++ b/servlet/src/digilib/image/ImageLoaderDocuImage.java Thu Oct 21 20:53:37 2004 +0200 @@ -1,164 +1,480 @@ /* ImageLoaderDocuImage -- Image class implementation using JDK 1.4 ImageLoader - Digital Image Library servlet components + Digital Image Library servlet components - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + Copyright (C) 2002, 2003 Robert Casties (robcast@mail.berlios.de) - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ package digilib.image; -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.util.*; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.ConvolveOp; +import java.awt.image.Kernel; +import java.awt.image.RescaleOp; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.util.Iterator; -import java.awt.*; -import java.awt.image.*; -import java.awt.geom.*; -import java.awt.image.renderable.*; +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.stream.FileImageInputStream; +import javax.imageio.stream.ImageInputStream; -import javax.imageio.*; +import digilib.io.FileOpException; +import digilib.io.ImageFile; -import digilib.*; -import digilib.io.*; - +/** Implementation of DocuImage using the ImageLoader API of Java 1.4 and Java2D. */ public class ImageLoaderDocuImage extends DocuImageImpl { - private BufferedImage img; + /** image object */ + protected BufferedImage img; + + /** interpolation type */ + protected RenderingHints renderHint; + + /** ImageIO image reader */ + protected ImageReader reader; + + /** File that was read */ + protected File imgFile; + + /* loadSubimage is supported. */ + public boolean isSubimageSupported() { + return true; + } + + public void setQuality(int qual) { + quality = qual; + renderHint = new RenderingHints(null); + //hint.put(RenderingHints.KEY_ANTIALIASING, + // RenderingHints.VALUE_ANTIALIAS_OFF); + // setup interpolation quality + if (qual > 0) { + logger.debug("quality q1"); + renderHint.put(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BICUBIC); + } else { + logger.debug("quality q0"); + renderHint.put(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + } + } - public ImageLoaderDocuImage() { - } + public int getHeight() { + int h = 0; + try { + if (img == null) { + h = reader.getHeight(0); + } else { + h = img.getHeight(); + } + } catch (IOException e) { + logger.debug("error in getHeight", e); + } + return h; + } - public ImageLoaderDocuImage(Utils u) { - util = u; - } + public int getWidth() { + int w = 0; + try { + if (img == null) { + w = reader.getWidth(0); + } else { + w = img.getWidth(); + } + } catch (IOException e) { + logger.debug("error in getHeight", e); + } + return w; + } + + /* load image file */ + public void loadImage(ImageFile f) throws FileOpException { + logger.debug("loadImage " + f.getFile()); + //System.gc(); + try { + img = ImageIO.read(f.getFile()); + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + } catch (IOException e) { + throw new FileOpException("Error reading image."); + } + } - /** - * load image file - */ - public void loadImage(File f) throws FileOpException { - util.dprintln(10, "loadImage!"); - System.gc(); - try { - for (int i = 0; i < ImageIO.getReaderFormatNames().length; i++) { - System.out.println("ImageLoader reader:"+ImageIO.getReaderFormatNames()[i]); - } - for (int i = 0; i < ImageIO.getWriterFormatNames().length; i++) { - System.out.println("ImageLoader writer:"+ImageIO.getWriterFormatNames()[i]); - } - img = ImageIO.read(f); - if (img == null) { - util.dprintln(3, "ERROR(loadImage): unable to load file"); - throw new FileOpException("Unable to load File!"); - } - } - catch (IOException e) { - throw new FileOpException("Error reading image."); - } - } + /** + * Get an ImageReader for the image file. + * + * @return + */ + public ImageReader getReader(ImageFile f) throws IOException { + logger.debug("preloadImage " + f.getFile()); + if (reader != null) { + logger.debug("Reader was not null!"); + // clean up old reader + dispose(); + } + //System.gc(); + RandomAccessFile rf = new RandomAccessFile(f.getFile(), "r"); + ImageInputStream istream = new FileImageInputStream(rf); + //Iterator readers = ImageIO.getImageReaders(istream); + String mt = f.getMimetype(); + logger.debug("File type:" + mt); + Iterator readers = ImageIO.getImageReadersByMIMEType(mt); + if (!readers.hasNext()) { + throw new FileOpException("Unable to load File!"); + } + reader = (ImageReader) readers.next(); + /* are there more readers? */ + logger.debug("ImageIO: this reader: " + reader.getClass()); + while (readers.hasNext()) { + logger.debug("ImageIO: next reader: " + readers.next().getClass()); + } + //*/ + reader.setInput(istream); + imgFile = f.getFile(); + return reader; + } + + /* Load an image file into the Object. */ + public void loadSubimage(ImageFile f, Rectangle region, int prescale) + throws FileOpException { + logger.debug("loadSubimage"); + //System.gc(); + try { + if ((reader == null) || (imgFile != f.getFile())) { + getReader(f); + } + // set up reader parameters + ImageReadParam readParam = reader.getDefaultReadParam(); + readParam.setSourceRegion(region); + if (prescale > 1) { + readParam.setSourceSubsampling(prescale, prescale, 0, 0); + } + // read image + logger.debug("loading.."); + img = reader.read(0, readParam); + logger.debug("loaded"); + } catch (IOException e) { + throw new FileOpException("Unable to load File!"); + } + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + } + + /* write image of type mt to Stream */ + public void writeImage(String mt, OutputStream ostream) + throws FileOpException { + logger.debug("writeImage"); + try { + // setup output + String type = "png"; + if (mt == "image/jpeg") { + type = "jpeg"; + } else if (mt == "image/png") { + type = "png"; + } else { + // unknown mime type + throw new FileOpException("Unknown mime type: " + mt); + } + + /* + * JPEG doesn't do transparency so we have to convert any RGBA image + * to RGB :-( *Java2D BUG* + */ + if ((type == "jpeg") && (img.getColorModel().hasAlpha())) { + logger.debug("BARF: JPEG with transparency!!"); + int w = img.getWidth(); + int h = img.getHeight(); + // BufferedImage.TYPE_INT_RGB seems to be fastest (JDK1.4.1, + // OSX) + int destType = BufferedImage.TYPE_INT_RGB; + BufferedImage img2 = new BufferedImage(w, h, destType); + img2.createGraphics().drawImage(img, null, 0, 0); + img = img2; + } + + // render output + logger.debug("writing"); + if (ImageIO.write(img, type, ostream)) { + // writing was OK + return; + } else { + throw new FileOpException( + "Error writing image: Unknown image format!"); + } + } catch (IOException e) { + throw new FileOpException("Error writing image."); + } + } - /** - * write image of type mt to Stream - */ - public void writeImage(String mt, ServletResponse res) - throws FileOpException { - util.dprintln(10, "writeImage!"); - try { - // setup output - String type = "png"; - if (mt == "image/jpeg") { - type = "jpeg"; - } else if (mt == "image/png") { - type = "png"; - } else { - // unknown mime type - util.dprintln(2, "ERROR(writeImage): Unknown mime type "+mt); - throw new FileOpException("Unknown mime type: "+mt); - } - res.setContentType(mt); - // render output - if (ImageIO.write(img, type, res.getOutputStream())) { - // writing was OK - return; - } else { - throw new FileOpException("Error writing image: Unknown image format!"); - } - } catch (IOException e) { - // e.printStackTrace(); - throw new FileOpException("Error writing image."); - } - } + public void scale(double scale, double scaleY) throws ImageOpException { + logger.debug("scale"); + /* for downscaling in high quality the image is blurred first */ + if ((scale <= 0.5) && (quality > 1)) { + int bl = (int) Math.floor(1 / scale); + blur(bl); + } + /* then scaled */ + AffineTransformOp scaleOp = new AffineTransformOp(AffineTransform + .getScaleInstance(scale, scale), renderHint); + BufferedImage scaledImg = null; + // enforce destination image type (*Java2D BUG*) + int type = img.getType(); + // FIXME: which type would be best? + if ((quality > 0) && (type != 0)) { + logger.debug("creating destination image"); + Rectangle2D dstBounds = scaleOp.getBounds2D(img); + scaledImg = new BufferedImage((int) dstBounds.getWidth(), + (int) dstBounds.getHeight(), type); + } + logger.debug("scaling..."); + scaledImg = scaleOp.filter(img, scaledImg); + logger.debug("destination image type " + scaledImg.getType()); + if (scaledImg == null) { + throw new ImageOpException("Unable to scale"); + } + //DEBUG + logger.debug("SCALE: " + scale + " ->" + scaledImg.getWidth() + "x" + + scaledImg.getHeight()); + img = scaledImg; + scaledImg = null; + } - public int getWidth() { - if (img != null) { - return img.getWidth(); - } - return 0; - } + public void blur(int radius) throws ImageOpException { + //DEBUG + logger.debug("blur: " + radius); + // minimum radius is 2 + int klen = Math.max(radius, 2); + // FIXME: use constant kernels for most common sizes + int ksize = klen * klen; + // kernel is constant 1/k + float f = 1f / ksize; + float[] kern = new float[ksize]; + for (int i = 0; i < ksize; i++) { + kern[i] = f; + } + Kernel blur = new Kernel(klen, klen, kern); + // blur with convolve operation + ConvolveOp blurOp = new ConvolveOp(blur, ConvolveOp.EDGE_NO_OP, + renderHint); + // blur needs explicit destination image type for color *Java2D BUG* + BufferedImage blurredImg = null; + if (img.getType() == BufferedImage.TYPE_3BYTE_BGR) { + blurredImg = new BufferedImage(img.getWidth(), img.getHeight(), img + .getType()); + } + blurredImg = blurOp.filter(img, blurredImg); + if (blurredImg == null) { + throw new ImageOpException("Unable to scale"); + } + img = blurredImg; + } + + public void crop(int x_off, int y_off, int width, int height) + throws ImageOpException { + // setup Crop + BufferedImage croppedImg = img.getSubimage(x_off, y_off, width, height); + logger.debug("CROP:" + croppedImg.getWidth() + "x" + + croppedImg.getHeight()); + //DEBUG + // util.dprintln(2, " time + // "+(System.currentTimeMillis()-startTime)+"ms"); + if (croppedImg == null) { + throw new ImageOpException("Unable to crop"); + } + img = croppedImg; + } + + public void enhance(float mult, float add) throws ImageOpException { + /* + * Only one constant should work regardless of the number of bands + * according to the JDK spec. Doesn't work on JDK 1.4 for OSX and Linux + * (at least). RescaleOp scaleOp = new RescaleOp( (float)mult, + * (float)add, null); scaleOp.filter(img, img); + */ + + /* The number of constants must match the number of bands in the image. */ + int ncol = img.getColorModel().getNumComponents(); + float[] dm = new float[ncol]; + float[] da = new float[ncol]; + for (int i = 0; i < ncol; i++) { + dm[i] = (float) mult; + da[i] = (float) add; + } + RescaleOp scaleOp = new RescaleOp(dm, da, null); + scaleOp.filter(img, img); + } + + public void enhanceRGB(float[] rgbm, float[] rgba) throws ImageOpException { + + /* + * The number of constants must match the number of bands in the image. + * We do only 3 (RGB) bands. + */ + + int ncol = img.getColorModel().getNumColorComponents(); + if ((ncol != 3) || (rgbm.length != 3) || (rgba.length != 3)) { + logger + .debug("ERROR(enhance): unknown number of color bands or coefficients (" + + ncol + ")"); + return; + } + RescaleOp scaleOp = new RescaleOp(rgbOrdered(rgbm), rgbOrdered(rgba), + null); + scaleOp.filter(img, img); + } - public int getHeight() { - if (img != null) { - return img.getHeight(); - } - return 0; - } - - /** - * crop and scale image - * take rectangle width,height at position x_off,y_off - * and scale by scale - */ - public void cropAndScale(int x_off, int y_off, int width, int height, - float scale, int qual) throws ImageOpException { - util.dprintln(10, "cropAndScale!"); - - int scaleInt = 0; - // setup interpolation quality - if (qual > 0) { - util.dprintln(4, "quality q1"); - scaleInt = AffineTransformOp.TYPE_BILINEAR; - } else { - util.dprintln(4, "quality q0"); - scaleInt = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; - } + /** + * Ensures that the array f is in the right order to map the images RGB + * components. (not shure what happens + */ + public float[] rgbOrdered(float[] fa) { + /* + * TODO: this is UGLY, UGLY!! + */ + float[] fb; + int t = img.getType(); + if (img.getColorModel().hasAlpha()) { + fb = new float[4]; + if ((t == BufferedImage.TYPE_INT_ARGB) + || (t == BufferedImage.TYPE_INT_ARGB_PRE)) { + // RGB Type + fb[0] = fa[0]; + fb[1] = fa[1]; + fb[2] = fa[2]; + fb[3] = 1f; + } else { + // this isn't tested :-( + fb[0] = 1f; + fb[1] = fa[0]; + fb[2] = fa[1]; + fb[3] = fa[2]; + } + } else { + fb = new float[3]; + if (t == BufferedImage.TYPE_3BYTE_BGR) { + // BGR Type (actually it looks like RBG...) + fb[0] = fa[0]; + fb[1] = fa[2]; + fb[2] = fa[1]; + } else { + fb[0] = fa[0]; + fb[1] = fa[1]; + fb[2] = fa[2]; + } + } + return fb; + } - // setup Crop - BufferedImage croppedImg = img.getSubimage(x_off, y_off, width, height); - - img = null; // free img - util.dprintln(3, "CROP:"+croppedImg.getWidth()+"x"+croppedImg.getHeight()); //DEBUG -// util.dprintln(2, " time "+(System.currentTimeMillis()-startTime)+"ms"); - - if (croppedImg == null) { - util.dprintln(2, "ERROR(cropAndScale): error in crop"); - throw new ImageOpException("Unable to crop"); - } + public void rotate(double angle) throws ImageOpException { + // setup rotation + double rangle = Math.toRadians(angle); + // create offset to make shure the rotated image has no negative + // coordinates + double w = img.getWidth(); + double h = img.getHeight(); + AffineTransform trafo = new AffineTransform(); + // center of rotation + double x = (w / 2); + double y = (h / 2); + trafo.rotate(rangle, x, y); + // try rotation to see how far we're out of bounds + AffineTransformOp rotOp = new AffineTransformOp(trafo, renderHint); + Rectangle2D rotbounds = rotOp.getBounds2D(img); + double xoff = rotbounds.getX(); + double yoff = rotbounds.getY(); + // move image back in line + trafo + .preConcatenate(AffineTransform.getTranslateInstance(-xoff, + -yoff)); + // transform image + rotOp = new AffineTransformOp(trafo, renderHint); + BufferedImage rotImg = rotOp.filter(img, null); + // calculate new bounding box + //Rectangle2D bounds = rotOp.getBounds2D(img); + if (rotImg == null) { + throw new ImageOpException("Unable to rotate"); + } + img = rotImg; + // crop new image (with self-made rounding) + /* + * img = rotImg.getSubimage( (int) (bounds.getX()+0.5), (int) + * (bounds.getY()+0.5), (int) (bounds.getWidth()+0.5), (int) + * (bounds.getHeight()+0.5)); + */ + } - // setup scale - AffineTransformOp scaleOp = new AffineTransformOp( - AffineTransform.getScaleInstance(scale, scale), - scaleInt); - BufferedImage scaledImg = scaleOp.filter(croppedImg, null); - croppedImg = null; // free opCrop + public void mirror(double angle) throws ImageOpException { + // setup mirror + double mx = 1; + double my = 1; + double tx = 0; + double ty = 0; + if (Math.abs(angle - 0) < epsilon) { // 0 degree + mx = -1; + tx = getWidth(); + } else if (Math.abs(angle - 90) < epsilon) { // 90 degree + my = -1; + ty = getHeight(); + } else if (Math.abs(angle - 180) < epsilon) { // 180 degree + mx = -1; + tx = getWidth(); + } else if (Math.abs(angle - 270) < epsilon) { // 270 degree + my = -1; + ty = getHeight(); + } else if (Math.abs(angle - 360) < epsilon) { // 360 degree + mx = -1; + tx = getWidth(); + } + AffineTransformOp mirOp = new AffineTransformOp(new AffineTransform(mx, + 0, 0, my, tx, ty), renderHint); + BufferedImage mirImg = mirOp.filter(img, null); + if (mirImg == null) { + throw new ImageOpException("Unable to mirror"); + } + img = mirImg; + } - if (scaledImg == null) { - util.dprintln(2, "ERROR(cropAndScale): error in scale"); - throw new ImageOpException("Unable to scale"); - } - img = scaledImg; - } + /* + * (non-Javadoc) + * + * @see java.lang.Object#finalize() + */ + protected void finalize() throws Throwable { + dispose(); + super.finalize(); + } + + public void dispose() { + // we must dispose the ImageReader because it keeps the filehandle + // open! + if (reader != null) { + reader.dispose(); + reader = null; + } + img = null; + } } diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/image/ImageSize.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/image/ImageSize.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,220 @@ +/* + * ImageFile.java -- digilib image file class. + * Digital Image Library servlet components + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. Please read license.txt for the full details. A copy of the GPL may + * be found at http://www.gnu.org/copyleft/lgpl.html You should have received a + * copy of the GNU General Public License along with this program; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA Created on 26.08.2003 + */ + +package digilib.image; + +/** Class for image size (width, height). + * + * A width or height of 0 is treated as a 'wildcard' that matches any size. + * + * @author casties + * + */ +public class ImageSize { + public int width; + public int height; + + public ImageSize() { + super(); + } + + public ImageSize(int width, int height) { + this.width = width; + this.height = height; + } + + public ImageSize(ImageSize i) { + this.width = i.width; + this.height = i.height; + } + + public void setSize(int width, int height) { + this.width = width; + this.height = height; + } + + /** + * Returns if the size of this image is smaller in every dimension than the + * other image. + * + * + * + * @param is + * @return + */ + public boolean isTotallySmallerThan(ImageSize is) { + if ((this.width == 0)||(is.width == 0)) { + // width wildcard + return (this.height <= is.height); + } + if ((this.height == 0)||(is.height == 0)) { + // height wildcard + return (this.width <= is.width); + } + return ((this.width <= is.width)&&(this.height <= is.height)); + } + + /** + * Returns if the size of this image is smaller in at least one dimension + * than the other image. + * + * @param is + * @return + */ + public boolean isSmallerThan(ImageSize is) { + if ((this.width == 0)||(is.width == 0)) { + // width wildcard + return (this.height <= is.height); + } + if ((this.height == 0)||(is.height == 0)) { + // height wildcard + return (this.width <= is.width); + } + return ((this.width <= is.width) || (this.height <= is.height)); + } + + /** + * Returns if the size of this image is bigger in every dimension than the + * other image. + * + * + * + * @param is + * @return + */ + public boolean isTotallyBiggerThan(ImageSize is) { + if ((this.width == 0)||(is.width == 0)) { + // width wildcard + return (this.height >= is.height); + } + if ((this.height == 0)||(is.height == 0)) { + // height wildcard + return (this.width >= is.width); + } + return ((this.width >= is.width) && (this.height >= is.height)); + } + + /** + * Returns if the size of this image is bigger in at least one dimension + * than the other image. + * + * + * + * @param is + * @return + */ + public boolean isBiggerThan(ImageSize is) { + if ((this.width == 0)||(is.width == 0)) { + // width wildcard + return (this.height >= is.height); + } + if ((this.height == 0)||(is.height == 0)) { + // height wildcard + return (this.width >= is.width); + } + return ((this.width >= is.width) || (this.height >= is.height)); + } + + /** + * Returns if this image has the same size or height as the other image. + * + * + * + * @param is + * @return + */ + public boolean fitsIn(ImageSize is) { + if ((this.width == 0)||(is.width == 0)) { + // width wildcard + return (this.height == is.height); + } + if ((this.height == 0)||(is.height == 0)) { + // height wildcard + return (this.width == is.width); + } + return ( + (this.width == is.width) + && (this.height <= is.height) + || (this.width <= is.width) + && (this.height == is.height)); + } + + /** + * Returns if the size of this image is the same as the other image. + * + * + * + * @param is + * @return + */ + public boolean equals(ImageSize is) { + if ((this.width == 0)||(is.width == 0)) { + // width wildcard + return (this.height == is.height); + } + if ((this.height == 0)||(is.height == 0)) { + // height wildcard + return (this.width == is.width); + } + return ((this.width == is.width) && (this.height == is.height)); + } + + /** + * @return + */ + public int getHeight() { + return height; + } + + /** + * @param height + */ + public void setHeight(int height) { + this.height = height; + } + + /** + * @return + */ + public int getWidth() { + return width; + } + + /** + * @param width + */ + public void setWidth(int width) { + this.width = width; + } + + /** + * Returns the aspect ratio. + * + * Aspect ratio is (width/height). So it's <1 for portrait and >1 for + * landscape. + * + * @return + */ + public float getAspect() { + return (height > 0) ? ((float) width / (float) height) : 0; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + String s = "[" + width + "x" + height + "]"; + return s; + } +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/image/JAIImageLoaderDocuImage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/image/JAIImageLoaderDocuImage.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,184 @@ +/* JAIImageLoaderDocuImage -- Image class implementation using JAI's ImageLoader Plugin + + Digital Image Library servlet components + + Copyright (C) 2002, 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +package digilib.image; + +import java.awt.Rectangle; +import java.awt.image.renderable.ParameterBlock; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.util.Iterator; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.stream.FileImageInputStream; +import javax.imageio.stream.ImageInputStream; +import javax.media.jai.JAI; + +import digilib.io.FileOpException; +import digilib.io.ImageFile; + +/** DocuImage implementation using the Java Advanced Imaging API and the ImageLoader + * API of Java 1.4. + */ +public class JAIImageLoaderDocuImage extends JAIDocuImage { + + /** ImageIO image reader */ + protected ImageReader reader; + /** current image file */ + protected File imgFile; + + /* loadSubimage is supported. */ + public boolean isSubimageSupported() { + return true; + } + + public int getHeight() { + int h = 0; + try { + if (img == null) { + h = reader.getHeight(0); + } else { + h = img.getHeight(); + } + } catch (IOException e) { + logger.debug("error in getHeight", e); + } + return h; + } + + public int getWidth() { + int w = 0; + try { + if (img == null) { + w = reader.getWidth(0); + } else { + w = img.getWidth(); + } + } catch (IOException e) { + logger.debug("error in getHeight", e); + } + return w; + } + + /* Load an image file into the Object. */ + public void loadImage(ImageFile f) throws FileOpException { + logger.debug("loadImage: "+f.getFile()); + //System.gc(); + img = JAI.create("ImageRead", f.getFile().getAbsolutePath()); + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + } + + /* Get an ImageReader for the image file. */ + public ImageReader getReader(ImageFile f) throws IOException { + logger.debug("preloadImage: "+f.getFile()); + //System.gc(); + RandomAccessFile rf = new RandomAccessFile(f.getFile(), "r"); + ImageInputStream istream = new FileImageInputStream(rf); + //Iterator readers = ImageIO.getImageReaders(istream); + Iterator readers = ImageIO.getImageReadersByMIMEType(f.getMimetype()); + if (! readers.hasNext()) { + throw new FileOpException("Unable to load File!"); + } + reader = (ImageReader) readers.next(); + logger.debug("JAIImageIO: this reader: " + reader.getClass()); + while (readers.hasNext()) { + logger.debug(" next reader: " + readers.next().getClass()); + } + reader.setInput(istream); + return reader; + } + + /* Load an image file into the Object. */ + public void loadSubimage(ImageFile f, Rectangle region, int prescale) + throws FileOpException { + logger.debug("loadSubimage: "+f.getFile()); + //System.gc(); + try { + if ((reader == null) || (imgFile != f.getFile())) { + getReader(f); + } + ImageReadParam readParam = reader.getDefaultReadParam(); + readParam.setSourceRegion(region); + readParam.setSourceSubsampling(prescale, prescale, 0, 0); + img = reader.read(0, readParam); + /* JAI imageread seems to ignore the readParam :-( + ImageInputStream istream = (ImageInputStream) reader.getInput(); + ParameterBlockJAI pb = new ParameterBlockJAI("imageread"); + pb.setParameter("Input", istream); + pb.setParameter("ReadParam", readParam); + pb.setParameter("Reader", reader); + img = JAI.create("imageread", pb); + */ + } catch (IOException e) { + throw new FileOpException("Unable to load File!"); + } + if (img == null) { + throw new FileOpException("Unable to load File!"); + } + imgFile = f.getFile(); + } + + + /* Write the current image to an OutputStream. */ + public void writeImage(String mt, OutputStream ostream) + throws FileOpException { + logger.debug("writeImage"); + try { + // setup output + ParameterBlock pb3 = new ParameterBlock(); + pb3.addSource(img); + pb3.add(ostream); + if (mt == "image/jpeg") { + pb3.add("JPEG"); + } else if (mt == "image/png") { + pb3.add("PNG"); + } else { + // unknown mime type + throw new FileOpException("Unknown mime type: " + mt); + } + // render output + JAI.create("ImageWrite", pb3); + } catch (IOException e) { + throw new FileOpException("Error writing image."); + } + } + + /* (non-Javadoc) + * @see java.lang.Object#finalize() + */ + protected void finalize() throws Throwable { + dispose(); + super.finalize(); + } + + public void dispose() { + // we must dispose the ImageReader because it keeps the filehandle open! + reader.dispose(); + reader = null; + img = null; + } + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/io/Directory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/Directory.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,139 @@ +/* Directory -- Filesystem directory object + + Digital Image Library servlet components + + Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * Created on 26.08.2003 + * + */ +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 { + + protected Logger logger = Logger.getLogger(this.getClass()); + + /** File object pointing to the directory */ + protected File dir = null; + /** parent directory */ + protected Directory parent = null; + /** list of filenames in the directory */ + protected String[] list = null; + + /** Default constructor. + * + */ + public Directory() { + super(); + } + + /** Constructor taking a File object. + * + * @param d + */ + public Directory(File d) { + dir = d; + } + + /** Constructor taking a File object and a parent. + * + * @param dir + * @param parent + */ + public Directory(File dir, Directory parent) { + this.dir = dir; + this.parent = parent; + } + + /** Constructor taking a directory name. + * + * @param d + */ + public Directory(String dn) { + 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) { + //logger.debug("reading dir: "+dir.getPath()); + list = dir.list(); + Arrays.sort(list); + //logger.debug(" done"); + } + return (list != null); + } + + /** + * @return + */ + public File getDir() { + return dir; + } + + /** + * @param dir + */ + public void setDir(File dir) { + this.dir = dir; + } + + /** + * @return + */ + Directory getParent() { + return parent; + } + + /** + * @param parent + */ + void setParent(Directory parent) { + this.parent = parent; + } + + + /** + * @return Returns the filenames. + */ + public String[] getFilenames() { + return list; + } + + /** + * @param filenames The filenames to set. + */ + public void setFilenames(String[] filenames) { + this.list = filenames; + } + + public void clearFilenames() { + this.list = null; + } +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/io/DocuDirCache.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/DocuDirCache.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,355 @@ +/* + * DocuDirCache.java + * + * Digital Image Library servlet components + * + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 03.03.2003 + */ + +package digilib.io; + +import java.io.File; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import digilib.servlet.DigilibConfiguration; + +/** + * @author casties + */ +public class DocuDirCache { + + /** general logger for this class */ + Logger logger = Logger.getLogger(this.getClass()); + + /** HashMap of directories */ + Map map = null; + + /** names of base directories */ + String[] baseDirNames = null; + + /** array of allowed file classes (image/text) */ + private int[] fileClasses = null; + + /** number of files in the whole cache (approximate) */ + long numFiles = 0; + + /** number of cache hits */ + long hits = 0; + + /** number of cache misses */ + long misses = 0; + + /** use safe (but slow) indexing */ + boolean safeDirIndex = false; + + /** the root directory element */ + public static Directory ROOT = null; + + /** + * Constructor with array of base directory names and file classes. + * + * @param bd + * base directory names + */ + 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. + * + * @param bd + * base directory names + */ + public DocuDirCache(String[] bd) { + baseDirNames = bd; + map = new HashMap(); + // default file class is CLASS_IMAGE + fileClasses = new int[1]; + fileClasses[0] = FileOps.CLASS_IMAGE; + } + + /** + * The number of directories in the cache. + * + * @return + */ + public int size() { + return (map != null) ? map.size() : 0; + } + + /** + * Add a DocuDirectory to the cache. + * + * @param newdir + */ + public void put(DocuDirectory newdir) { + String s = newdir.getDirName(); + if (map.containsKey(s)) { + logger.warn("Duplicate key in DocuDirCache.put -- ignoring!"); + } else { + map.put(s, newdir); + numFiles += newdir.size(); + } + } + + /** + * Add a directory to the cache and check its parents. + * + * @param newDir + */ + public synchronized void putDir(DocuDirectory newDir) { + put(newDir); + String parent = FileOps.parent(newDir.getDirName()); + if (parent != "") { + // check the parent in the cache + DocuDirectory pd = (DocuDirectory) map.get(parent); + if (pd == null) { + // the parent is unknown + pd = new DocuDirectory(parent, this); + putDir(pd); + } + newDir.setParent(pd); + } + } + + /** + * Get a list with all children of a directory. + * + * Returns a List of DocuDirectory's. Returns an empty List if the directory + * has no children. If recurse is false then only direct children are + * returned. + * + * @param dirname + * @param recurse + * find all children and their children. + * @return + */ + public List getChildren(String dirname, boolean recurse) { + List l = new LinkedList(); + for (Iterator i = map.keySet().iterator(); i.hasNext();) { + String n = (String) i.next(); + DocuDirectory dd = (DocuDirectory) map.get(n); + if (recurse) { + if (dd.getDirName().startsWith(dirname)) { + l.add(dd); + } + } else { + if (FileOps.parent(dd.getDirName()).equals(dirname)) { + l.add(dd); + } + } + } + return l; + } + + /** + * Returns the DocuDirent with the pathname fn and the index + * in and the class fc. + * + * If fn is a file then the corresponding DocuDirent is + * returned and the index is ignored. + * + * @param fn + * digilib pathname + * @param in + * file index + * @param fc + * file class + * @return + */ + public DocuDirent getFile(String fn, int in, int fc) { + DocuDirectory dd; + // file number is 1-based, vector index is 0-based + int n = in - 1; + // first, assume fn is a directory and look in the cache + dd = (DocuDirectory) map.get(fn); + if (dd == null) { + // cache miss + misses++; + /* + * see if fn is a directory + */ + File f = new File(baseDirNames[0], fn); + if (f.isDirectory()) { + dd = new DocuDirectory(fn, this); + if (dd.isValid()) { + // add to the cache + putDir(dd); + } + } else { + /* + * maybe it's a file + */ + // get the parent directory string (like we store it in the + // cache) + String d = FileOps.parent(fn); + // try it in the cache + dd = (DocuDirectory) map.get(d); + if (dd == null) { + // try to read from disk + dd = new DocuDirectory(d, this); + if (dd.isValid()) { + // add to the cache + putDir(dd); + } else { + // invalid path + return null; + } + } else { + // it was not a real cache miss + misses--; + } + // get the file's index + n = dd.indexOf(f.getName(), fc); + } + } else { + // cache hit + hits++; + } + dd.refresh(); + if (dd.isValid()) { + try { + return dd.get(n, fc); + } catch (IndexOutOfBoundsException e) { + } + } + return null; + } + + /** + * Returns the DocuDirectory indicated by the pathname fn. + * + * If fn is a file then its parent directory is returned. + * + * @param fn + * digilib pathname + * @return + */ + public DocuDirectory getDirectory(String fn) { + DocuDirectory dd; + // first, assume fn is a directory and look in the cache + dd = (DocuDirectory) map.get(fn); + if (dd == null) { + // cache miss + misses++; + // see if it's a directory + File f = new File(baseDirNames[0], fn); + if (f.isDirectory()) { + dd = new DocuDirectory(fn, this); + if (dd.isValid()) { + // add to the cache + putDir(dd); + } + } else { + // maybe it's a file + if (f.canRead()) { + // try the parent directory in the cache + dd = (DocuDirectory) map.get(f.getParent()); + if (dd == null) { + // try to read from disk + dd = new DocuDirectory(f.getParent(), this); + if (dd.isValid()) { + // add to the cache + putDir(dd); + } else { + // invalid path + return null; + } + } else { + // not a real cache miss then + misses--; + } + } else { + // it's not even a file :-( + return null; + } + } + } else { + // cache hit + hits++; + } + dd.refresh(); + if (dd.isValid()) { + return dd; + } + return null; + } + + /** + * @return String[] + */ + public String[] getBaseDirNames() { + return baseDirNames; + } + + /** + * @return long + */ + public long getNumFiles() { + return numFiles; + } + + /** + * Sets the baseDirNames. + * + * @param baseDirNames + * The baseDirNames to set + */ + public void setBaseDirNames(String[] baseDirNames) { + this.baseDirNames = baseDirNames; + } + + /** + * @return long + */ + public long getHits() { + return hits; + } + + /** + * @return long + */ + public long getMisses() { + return misses; + } + + /** + * @return + */ + public int[] getFileClasses() { + return fileClasses; + } + + /** + * @param fileClasses + */ + public void setFileClasses(int[] fileClasses) { + this.fileClasses = fileClasses; + } + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/io/DocuDirectory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/DocuDirectory.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,563 @@ +/* DocuDirectory -- Directory of DocuFilesets. + + Digital Image Library servlet components + + Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * Created on 25.02.2003 + */ + +package digilib.io; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.xml.sax.SAXException; + +/** + * @author casties + */ +public class DocuDirectory extends Directory { + + /** list of files (DocuDirent) */ + private ArrayList[] list = null; + + /** directory object is valid (exists on disk) */ + private boolean isValid = false; + + /** reference of the parent DocuDirCache */ + private DocuDirCache cache = null; + + /** directory name (digilib canonical form) */ + private String dirName = null; + + /** directory metadata */ + private Map dirMeta = null; + + /** state of metadata is valid */ + private boolean metaChecked = false; + + /** unresolved file metadata */ + private Map unresolvedFileMeta = null; + + /** time of last access of this object (not the filesystem) */ + private long objectATime = 0; + + /** time directory was last modified on the file system */ + private long dirMTime = 0; + + /** + * Constructor with digilib directory path and a parent DocuDirCache. + * + * Directory names at the given path are appended to the base directories + * from the cache. The directory is checked on disk and isValid is set. + * + * @see readDir + * + * @param path + * digilib directory path name + * @param cache + * parent DocuDirCache + */ + public DocuDirectory(String path, DocuDirCache cache) { + this.dirName = path; + this.cache = cache; + initDir(); + checkDir(); + } + + /** + * Sets and checks the dir object. + * + */ + protected void initDir() { + String baseDirName = cache.getBaseDirNames()[0]; + // clear directory first + list = new ArrayList[FileOps.NUM_CLASSES]; + isValid = false; + dirMTime = 0; + // the first directory has to exist + dir = new File(baseDirName, dirName); + } + + /** + * number of DocuFiles in this directory. + * + */ + public int size() { + return ((list != null) && (list[0] != null)) ? list[0].size() : 0; + } + + /** + * number of files of this class in this directory. + * + * @param fc + * fileClass + */ + public int size(int fc) { + return ((list != null) && (list[fc] != null)) ? list[fc].size() : 0; + } + + /** + * Returns the ImageFile at the index. + * + * @param index + * @return + */ + public ImageFileset get(int index) { + if ((list == null) || (list[0] == null) || (index >= list[0].size())) { + return null; + } + return (ImageFileset) list[0].get(index); + } + + /** + * Returns the file of the class at the index. + * + * @param index + * @param fc + * fileClass + * @return + */ + public DocuDirent get(int index, int fc) { + if ((list == null) || (list[fc] == null) || (index >= list[fc].size())) { + return null; + } + return (DocuDirent) list[fc].get(index); + } + + /** + * Checks if the directory exists on the filesystem. + * + * Sets isValid. + * + * @return + */ + public boolean checkDir() { + if (dir == null) { + initDir(); + } + isValid = dir.isDirectory(); + return isValid; + } + + /** + * Read the filesystem directory and fill this object. + * + * Clears the List and (re)reads all files. + * + * @return boolean the directory exists + */ + public synchronized boolean readDir() { + // check directory first + checkDir(); + if (!isValid) { + return false; + } + // first file extension to try for scaled directories + String scalext = null; + // 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; + } + // list of base dirs from the parent cache + String[] baseDirNames = cache.getBaseDirNames(); + // number of base dirs + int nb = baseDirNames.length; + // array of base dirs + Directory[] dirs = new Directory[nb]; + // first entry is this directory + dirs[0] = this; + // fill array with the remaining directories + for (int j = 1; j < nb; j++) { + File d = new File(baseDirNames[j], dirName); + if (d.isDirectory()) { + dirs[j] = new Directory(d); + //logger.debug(" reading scaled directory " + d.getPath()); + dirs[j].readDir(); + //logger.debug(" done"); + } + } + + // go through all file classes + for (int classIdx = 0; classIdx < FileOps.NUM_CLASSES; classIdx++) { + int fileClass = cache.getFileClasses()[classIdx]; + //logger.debug("filtering directory "+dir.getPath()+" for class + // "+fc); + File[] fileList = FileOps.listFiles(allFiles, FileOps + .filterForClass(fileClass)); + //logger.debug(" done"); + // number of files in the directory + int numFiles = fileList.length; + if (numFiles > 0) { + // create new list + list[fileClass] = new ArrayList(numFiles); + // sort the file names alphabetically and iterate the list + Arrays.sort(fileList); + Map hints = FileOps.newHints(FileOps.HINT_BASEDIRS, dirs); + hints.put(FileOps.HINT_FILEEXT, scalext); + for (int i = 0; i < numFiles; i++) { + DocuDirent f = FileOps.fileForClass(fileClass, fileList[i], + hints); + // add the file to our list + list[fileClass].add(f); + f.setParent(this); + } + } + } + // clear the scaled directories + for (int j = 1; j < nb; j++) { + if (dirs[j] != null) { + dirs[j].clearFilenames(); + } + } + // update number of cached files if this was the first time + if (dirMTime == 0) { + cache.numFiles += size(); + } + dirMTime = dir.lastModified(); + // read metadata as well + readMeta(); + return isValid; + } + + /** + * Check to see if the directory has been modified and reread if necessary. + * + * @return boolean the directory is valid + */ + public boolean refresh() { + if (isValid) { + if (dir.lastModified() > dirMTime) { + // on-disk modification time is more recent + readDir(); + } + touch(); + } + return isValid; + } + + /** + * Read directory metadata. + * + */ + public void readMeta() { + // check for directory metadata... + File mf = new File(dir, "index.meta"); + if (mf.canRead()) { + XMLMetaLoader ml = new XMLMetaLoader(); + try { + // read directory meta file + Map fileMeta = ml.loadURL(mf.getAbsolutePath()); + if (fileMeta == null) { + return; + } + // meta for the directory itself is in the "" bin + dirMeta = (Map) fileMeta.remove(""); + // read meta for files in this directory + readFileMeta(fileMeta, null); + // is there meta for other files left? + if (fileMeta.size() > 0) { + unresolvedFileMeta = fileMeta; + } + } catch (SAXException e) { + logger.warn("error parsing index.meta", e); + } catch (IOException e) { + logger.warn("error reading index.meta", e); + } + } + readParentMeta(); + metaChecked = true; + } + + /** + * Read metadata from all known parent directories. + * + */ + public void readParentMeta() { + // check the parent directories for additional file meta + Directory dd = parent; + String path = dir.getName(); + while (dd != null) { + if (((DocuDirectory) dd).hasUnresolvedFileMeta()) { + readFileMeta(((DocuDirectory) dd).unresolvedFileMeta, path); + } + // prepend parent dir path + path = dd.dir.getName() + "/" + path; + // become next parent + dd = dd.parent; + } + } + + /** + * Read metadata for the files in this directory. + * + * Takes a Map with meta-information, adding the relative path before the + * lookup. + * + * @param fileMeta + * @param relPath + * @param fc + * fileClass + */ + protected void readFileMeta(Map fileMeta, String relPath) { + if (list == null) { + // there are no files + return; + } + String path = (relPath != null) ? (relPath + "/") : ""; + // go through all file classes + for (int nc = 0; nc < list.length; nc++) { + int fc = cache.getFileClasses()[nc]; + if (list[fc] == null) { + continue; + } + // iterate through the list of files in this directory + for (Iterator i = list[fc].iterator(); i.hasNext();) { + DocuDirent f = (DocuDirent) i.next(); + // prepend path to the filename + String fn = path + f.getName(); + // look up meta for this file and remove from dir + Map meta = (Map) fileMeta.remove(fn); + if (meta != null) { + // store meta in file + f.setFileMeta(meta); + } + } + } + } + + protected void notifyChildMeta(Map childmeta) { + List children = cache.getChildren(this.dirName, true); + if (children.size() > 0) { + for (Iterator i = children.iterator(); i.hasNext();) { + // TODO: finish this! + //((DocuDirectory) i.next()).readFileMeta() + } + } + } + + /** + * Update access time. + * + * @return long time of last access. + */ + public long touch() { + long t = objectATime; + objectATime = System.currentTimeMillis(); + return t; + } + + /** + * Searches for the file with the name fn. + * + * Searches the directory for the file with the name fn and + * returns its index. Returns -1 if the file cannot be found. + * + * @param fn + * filename + * @param fc + * file class + * @return int index of file fn + */ + public int indexOf(String fn) { + int fc = FileOps.classForFilename(fn); + return indexOf(fn, fc); + } + + /** + * Searches for the file with the name fn and class fc. + * + * Searches the directory for the file with the name fn and + * returns its index. Returns -1 if the file cannot be found. + * + * @param fn + * filename + * @return int index of file fn + */ + public int indexOf(String fn, int fc) { + if (!isRead()) { + // read directory now + if (!readDir()) { + return -1; + } + } + List fileList = list[fc]; + // empty directory? + if (fileList == null) { + return -1; + } + // search for exact match + int idx = Collections.binarySearch(fileList, fn); + if (idx >= 0) { + return idx; + } else { + // try closest matches without extension + idx = -idx - 1; + String fb = FileOps.basename(fn); + DocuDirent fs; + if ((idx < fileList.size()) + && (FileOps.basename(((DocuDirent) fileList.get(idx)) + .getName()).equals(fb))) { + // idx matches + return idx; + } else if ((idx > 0) + && (FileOps.basename(((DocuDirent) fileList.get(idx - 1)) + .getName()).equals(fb))) { + // idx-1 matches + return idx - 1; + } else if ((idx + 1 < fileList.size()) + && (FileOps.basename(((DocuDirent) fileList.get(idx - 1)) + .getName()).equals(fb))) { + // idx+1 matches + return idx + 1; + } + + } + return -1; + } + + /** + * Finds the DocuDirent with the name fn. + * + * Searches the directory for the DocuDirent with the name fn + * and returns it. Returns null if the file cannot be found. + * + * @param fn + * filename + * @return DocuDirent + */ + public DocuDirent find(String fn) { + int fc = FileOps.classForFilename(fn); + int i = indexOf(fn, fc); + if (i >= 0) { + return (DocuDirent) list[0].get(i); + } + return null; + } + + /** + * Finds the DocuDirent with the name fn and class + * fc. + * + * Searches the directory for the DocuDirent with the name fn + * and returns it. Returns null if the file cannot be found. + * + * @param fn + * filename + * @return DocuDirent + */ + public DocuDirent find(String fn, int fc) { + int i = indexOf(fn, fc); + if (i >= 0) { + return (DocuDirent) list[fc].get(i); + } + return null; + } + + /** + * Returns the digilib canonical name. + * + * @return + */ + public String getDirName() { + return dirName; + } + + /** + * The directory is valid (exists on disk). + * + * @return boolean + */ + public boolean isValid() { + return isValid; + } + + /** + * The directory has been read from disk. + * + * @return + */ + public boolean isRead() { + return (dirMTime != 0); + } + + /** + * @return long + */ + public long getAccessTime() { + return objectATime; + } + + /** + * @return Hashtable + */ + public Map getDirMeta() { + return dirMeta; + } + + /** + * Checks metadata + * + */ + public void checkMeta() { + if (metaChecked) { + return; + } else { + readMeta(); + } + } + + /** + * @return long + */ + public long getDirMTime() { + return dirMTime; + } + + /** + * Sets the dirMeta. + * + * @param dirMeta + * The dirMeta to set + */ + public void setDirMeta(Map dirMeta) { + this.dirMeta = dirMeta; + } + + public boolean hasUnresolvedFileMeta() { + return (this.unresolvedFileMeta != null); + } + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/io/ImageFile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/ImageFile.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,136 @@ +/* ImageFile.java -- digilib image file class. + + Digital Image Library servlet components + + Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + Please read license.txt for the full details. A copy of the GPL + may be found at http://www.gnu.org/copyleft/lgpl.html + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + * Created on 25.02.2003 + */ + +package digilib.io; + +import java.io.File; + +import digilib.image.ImageSize; + +/** + * @author casties + */ +public class ImageFile { + + // file name + private String filename = null; + // parent ImageFileset + private ImageFileset parent = null; + // parent directory + private Directory dir = null; + // mime file type + private String mimetype = null; + // image size in pixels + private ImageSize pixelSize = null; + + public ImageFile(String fn, ImageFileset parent, Directory dir) { + this.filename = fn; + this.parent = parent; + this.dir = dir; + } + + public ImageFile(String fn) { + File f = new File(fn); + this.dir = new Directory(f.getParentFile()); + this.filename = f.getName(); + } + + /** Returns the file name (without path). + * + * @return + */ + public String getName() { + return filename; + } + + + /** + * @return File + */ + public File getFile() { + if (dir == null) { + return null; + } + File f = new File(dir.getDir(), filename); + return f; + } + + /** + * @return ImageSize + */ + public ImageSize getSize() { + return pixelSize; + } + + /** + * @return String + */ + public String getMimetype() { + return mimetype; + } + + /** + * Sets the imageSize. + * @param imageSize The imageSize to set + */ + public void setSize(ImageSize imageSize) { + this.pixelSize = imageSize; + } + + /** + * Sets the mimetype. + * @param mimetype The mimetype to set + */ + public void setMimetype(String filetype) { + this.mimetype = filetype; + } + + /** + * @return ImageFileset + */ + public ImageFileset getParent() { + return parent; + } + + /** + * Sets the parent. + * @param parent The parent to set + */ + public void setParent(ImageFileset parent) { + this.parent = parent; + } + + /** + * @return boolean + */ + public boolean isChecked() { + return (pixelSize != null); + } + + /** Returns the aspect ratio of the image (width/height). + * + * @return + */ + public float getAspect() { + return (pixelSize != null) ? pixelSize.getAspect() : 0; + } + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/io/ImageFileset.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/io/ImageFileset.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,418 @@ +/* ImageFileset -- digilib image file info class. + * Digital Image Library servlet components + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can + * redistribute it and/or modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be + * found at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package digilib.io; + +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 org.apache.log4j.Logger; + +import digilib.image.ImageOps; +import digilib.image.ImageSize; + +/** + * @author casties + */ +public class ImageFileset extends DocuDirent { + + /** this is an image file */ + protected static int fileClass = FileOps.CLASS_IMAGE; + + /** logger for this class */ + private static Logger logger = Logger.getLogger(ImageFileset.class); + + /** list of files (ImageFile) */ + private ArrayList list = null; + + /** aspect ratio (width/height) */ + private float aspect = 0; + + /** resolution of the biggest image (DPI) */ + private float resX = 0; + + /** resolution of the biggest image (DPI) */ + private float resY = 0; + + /** + * Creator for empty fileset. + * + * + * @param initialCapacity + */ + public ImageFileset() { + list = new ArrayList(); + } + + /** + * Constructor with a file and hints. + * + * The hints are expected to contain 'basedirs' and 'scaledfilext' keys. + * + * @param file + * @param hints + */ + 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, hints); + } + + /** + * Adds an ImageFile to this Fileset. + * + * The files should be added in the order of higher to lower resolutions. + * The first file is considered the hires "original". + * + * + * @param f + * file to add + * @return true (always) + */ + public boolean add(ImageFile f) { + f.setParent(this); + return list.add(f); + } + + /** + * The number of image files in this Fileset. + * + * + * @return number of image files + */ + public int size() { + return (list != null) ? list.size() : 0; + } + + /** + * Gets the default File. + * + */ + public File getFile() { + return (list != null) ? ((ImageFile) list.get(0)).getFile() : null; + } + + /** + * Get the ImageFile at the index. + * + * + * @param index + * @return + */ + public ImageFile get(int index) { + return (ImageFile) list.get(index); + } + + /** + * Get the next smaller ImageFile than the given size. + * + * Returns the ImageFile from the set that has a width and height smaller or + * equal the given size. Returns null if there isn't any smaller image. + * Needs DocuInfo instance to checkFile(). + * + * + * @param size + * @param info + * @return + */ + public ImageFile getNextSmaller(ImageSize size) { + for (Iterator i = getHiresIterator(); i.hasNext();) { + ImageFile f = (ImageFile) i.next(); + try { + if (!f.isChecked()) { + ImageOps.checkFile(f); + } + if (f.getSize().isTotallySmallerThan(size)) { + return f; + } + } catch (IOException e) { + } + } + return null; + } + + /** + * Get the next bigger ImageFile than the given size. + * + * Returns the ImageFile from the set that has a width or height bigger or + * equal the given size. Returns null if there isn't any bigger image. Needs + * DocuInfo instance to checkFile(). + * + * + * @param size + * @param info + * @return + */ + public ImageFile getNextBigger(ImageSize size) { + for (ListIterator i = getLoresIterator(); i.hasPrevious();) { + ImageFile f = (ImageFile) i.previous(); + try { + if (!f.isChecked()) { + ImageOps.checkFile(f); + } + if (f.getSize().isBiggerThan(size)) { + return f; + } + } catch (IOException e) { + } + } + return null; + } + + /** + * Returns the biggest ImageFile in the set. + * + * + * @return + */ + public ImageFile getBiggest() { + return this.get(0); + } + + /** + * Returns the biggest ImageFile in the set. + * + * + * @return + */ + public ImageFile getSmallest() { + return this.get(this.size() - 1); + } + + /** + * Get an Iterator for this Fileset starting at the highest resolution + * images. + * + * + * @return + */ + public ListIterator getHiresIterator() { + return list.listIterator(); + } + + /** + * Get an Iterator for this Fileset starting at the lowest resolution + * images. + * + * The Iterator starts at the last element, so you have to use it backwards + * with hasPrevious() and previous(). + * + * + * @return + */ + public ListIterator getLoresIterator() { + return list.listIterator(list.size()); + } + + /** + * Fill the ImageFileset with files from different base directories. + * + * + * @param dirs + * list of base directories + * @param fl + * file (from first base dir) + * @param hints + * + */ + 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 baseFn = FileOps.basename(fn); + // add the first ImageFile to the ImageFileset + add(new ImageFile(fn, this, parent)); + // iterate the remaining base directories + for (int dirIdx = 1; dirIdx < nb; dirIdx++) { + if (dirs[dirIdx] == null) { + continue; + } + // read the directory + if (dirs[dirIdx].getFilenames() == null) { + dirs[dirIdx].readDir(); + } + String[] dirFiles = dirs[dirIdx].getFilenames(); + // try the same filename as the original + int fileIdx = Arrays.binarySearch(dirFiles, fn); + if (fileIdx < 0) { + // try closest matches without extension + fileIdx = -fileIdx - 1; + // try idx + if ((fileIdx < dirFiles.length) + && (FileOps.basename(dirFiles[fileIdx]).equals(baseFn))) { + // idx ok + } else if ((fileIdx > 0) + && (FileOps.basename(dirFiles[fileIdx - 1]) + .equals(baseFn))) { + // idx-1 ok + fileIdx = fileIdx - 1; + } else if ((fileIdx+1 < dirFiles.length) + && (FileOps.basename(dirFiles[fileIdx + 1]) + .equals(baseFn))) { + // idx+1 ok + fileIdx = fileIdx + 1; + } else { + // basename doesn't match + continue; + } + } + if (FileOps.classForFilename(dirFiles[fileIdx]) == FileOps.CLASS_IMAGE) { + /* logger.debug("adding file " + dirFiles[fileIdx] + + " to Fileset " + this.getName()); */ + add(new ImageFile(dirFiles[fileIdx], this, dirs[dirIdx])); + } + } + } + + /** + * Checks metadata and sets resolution in resX and resY. + * + */ + public void checkMeta() { + if (metaChecked) { + return; + } + if (fileMeta == null) { + // try to read metadata file + readMeta(); + if (fileMeta == null) { + // try directory metadata + ((DocuDirectory) parent).checkMeta(); + if (((DocuDirectory) parent).getDirMeta() != null) { + fileMeta = ((DocuDirectory) parent).getDirMeta(); + } else { + // try parent directory metadata + DocuDirectory gp = (DocuDirectory) parent.getParent(); + if (gp != null) { + gp.checkMeta(); + if (gp.getDirMeta() != null) { + fileMeta = gp.getDirMeta(); + } else { + // no metadata available + metaChecked = true; + return; + } + } + } + } + } + metaChecked = true; + String s; + float dpi = 0; + float dpix = 0; + float dpiy = 0; + float sizex = 0; + float sizey = 0; + float pixx = 0; + float pixy = 0; + // DPI is valid for X and Y + if (fileMeta.containsKey("original-dpi")) { + try { + dpi = Float.parseFloat((String) fileMeta.get("original-dpi")); + } catch (NumberFormatException e) { + } + if (dpi != 0) { + resX = dpi; + resY = dpi; + return; + } + } + // DPI-X and DPI-Y + if (fileMeta.containsKey("original-dpi-x") + && fileMeta.containsKey("original-dpi-y")) { + try { + dpix = Float.parseFloat((String) fileMeta + .get("original-dpi-x")); + dpiy = Float.parseFloat((String) fileMeta + .get("original-dpi-y")); + } catch (NumberFormatException e) { + } + if ((dpix != 0) && (dpiy != 0)) { + resX = dpix; + resY = dpiy; + return; + } + } + // SIZE-X and SIZE-Y and PIXEL-X and PIXEL-Y + if (fileMeta.containsKey("original-size-x") + && fileMeta.containsKey("original-size-y") + && fileMeta.containsKey("original-pixel-x") + && fileMeta.containsKey("original-pixel-y")) { + try { + sizex = Float.parseFloat((String) fileMeta + .get("original-size-x")); + sizey = Float.parseFloat((String) fileMeta + .get("original-size-y")); + pixx = Float.parseFloat((String) fileMeta + .get("original-pixel-x")); + pixy = Float.parseFloat((String) fileMeta + .get("original-pixel-y")); + } catch (NumberFormatException e) { + } + if ((sizex != 0) && (sizey != 0) && (pixx != 0) && (pixy != 0)) { + resX = pixx / (sizex * 100 / 2.54f); + resY = pixy / (sizey * 100 / 2.54f); + return; + } + } + } + + /** + * @return + */ + public float getResX() { + return resX; + } + + /** + * @return + */ + public float getResY() { + return resY; + } + + /** + * Sets the aspect ratio from an ImageSize. + * + * + * @param f + */ + public void setAspect(ImageSize s) { + aspect = s.getAspect(); + } + + /** + * Returns the aspect ratio. + * + * Aspect ratio is (width/height). So it's <1 for portrait and >1 for + * landscape. + * + * + * @return + */ + public float getAspect() { + return aspect; + } + +} \ No newline at end of file diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/DigilibConfiguration.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/DigilibConfiguration.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,267 @@ +/* + * DigilibConfiguration -- Holding all parameters for digilib servlet. + * + * Digital Image Library servlet components + * + * Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +package digilib.servlet; + +import java.io.File; +import java.util.Iterator; +import java.util.Map; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; + +import org.apache.log4j.BasicConfigurator; +import org.apache.log4j.Logger; + +import digilib.image.DocuImage; +import digilib.image.DocuImageImpl; +import digilib.io.FileOps; +import digilib.io.XMLListLoader; + +/** + * Class to hold the digilib servlet configuration parameters. The parameters + * can be read from the digilib-config file and be passed to other servlets or + * beans.
errorImgFileName: image file to send in case of error.
+ * denyImgFileName: image file to send if access is denied.
baseDirs: + * array of base directories in order of preference (prescaled versions first). + *
useAuth: use authentication information.
authConfPath: + * authentication configuration file.
...
+ * + * @author casties + * + */ +public class DigilibConfiguration extends ParameterMap { + + private static final long serialVersionUID = -6630487070791637120L; + + /** DocuImage class instance */ + private Class docuImageClass = null; + + /** Log4J logger */ + private Logger logger = Logger.getLogger("digilib.config"); + + /** + * Default constructor defines all parameters and their default values. + * + */ + public DigilibConfiguration() { + // create HashMap(20) + super(20); + // we start with a default logger config + BasicConfigurator.configure(); + + /* + * Definition of parameters and default values. System parameters that + * are not read from config file have a type 's'. + */ + + // digilib servlet version + newParameter( + "servlet.version", + digilib.servlet.Scaler.dlVersion, + null, + 's'); + // configuration file location + newParameter("servlet.config.file", null, null, 's'); + // DocuDirCache instance + newParameter("servlet.dir.cache", null, null, 's'); + // DocuImage class instance + newParameter( + "servlet.docuimage.class", + digilib.image.JAIDocuImage.class, + null, + 's'); + // AuthOps instance for authentication + newParameter("servlet.auth.op", null, null, 's'); + // worker queues + newParameter("servlet.fast.queue", null, null, 's'); + newParameter("servlet.slow.queue", null, null, 's'); + + /* + * parameters that can be read from config file have a type 'f' + */ + + // image file to send in case of error + newParameter( + "error-image", + new File("/docuserver/images/icons/scalerror.gif"), + null, + 'f'); + // image file to send if access is denied + newParameter( + "denied-image", + new File("/docuserver/images/icons/denied.gif"), + null, + 'f'); + // base directories in order of preference (prescaled versions last) + String[] bd = { "/docuserver/images", "/docuserver/scaled/small" }; + newParameter("basedir-list", bd, null, 'f'); + // use authentication information + newParameter("use-authorization", Boolean.FALSE, null, 'f'); + // authentication configuration file + 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 + newParameter("debug-level", new Integer(5), null, 'f'); + // Type of DocuImage instance + newParameter( + "docuimage-class", + "digilib.image.JAIDocuImage", + null, + 'f'); + // part of URL used to indicate authorized access + newParameter("auth-url-path", "authenticated/", null, 'f'); + // degree of subsampling on image load + newParameter("subsample-minimum", new Float(2f), null, 'f'); + // default scaling quality + newParameter("default-quality", new Integer(1), null, 'f'); + // use mapping file to translate paths + newParameter("use-mapping", Boolean.FALSE, null, 'f'); + // mapping file location + newParameter("mapping-file", new File("digilib-map.xml"), null, 'f'); + // log4j config file location + 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'); + // number of fast worker threads + newParameter("worker-fast-lanes", new Integer(2), null, 'f'); + // length of fast worker queue + newParameter("worker-fast-queue", new Integer(100), null, 'f'); + // number of slow worker threads + newParameter("worker-slow-lanes", new Integer(2), null, 'f'); + // length of slow worker queue + newParameter("worker-slow-queue", new Integer(100), null, 'f'); + + } + + /** + * Constructor taking a ServletConfig. Reads the config file location from + * an init parameter and loads the config file. Calls readConfig(). + * + * @see readConfig() + */ + public DigilibConfiguration(ServletConfig c) throws Exception { + this(); + readConfig(c); + } + + /** + * read parameter list from the XML file in init parameter "config-file" + */ + public void readConfig(ServletConfig c) throws Exception { + + /* + * Get config file name. The file name is first looked for as an init + * parameter, then in a fixed location in the webapp. + */ + if (c == null) { + // no config no file... + return; + } + String fn = c.getInitParameter("config-file"); + if (fn == null) { + fn = ServletOps.getConfigFile("digilib-config.xml", c); + if (fn == null) { + logger.fatal("readConfig: no param config-file"); + throw new ServletException("ERROR: no digilib config file!"); + } + } + File f = new File(fn); + // setup config file list reader + XMLListLoader lilo = + new XMLListLoader("digilib-config", "parameter", "name", "value"); + // read config file into HashMap + Map confTable = lilo.loadURL(f.toURL().toString()); + + // set config file path parameter + setValue("servlet.config.file", f.getCanonicalPath()); + + /* + * read parameters + */ + + for (Iterator i = confTable.keySet().iterator(); i.hasNext();) { + String key = (String) i.next(); + String val = (String) confTable.get(key); + Parameter p = get(key); + if (p != null) { + if (p.getType() == 's') { + // type 's' Parameters are not overwritten. + continue; + } + if (!p.setValueFromString(val)) { + /* + * automatic conversion failed -- try special cases + */ + + // basedir-list + if (key.equals("basedir-list")) { + // split list into directories + String[] sa = FileOps.pathToArray(val); + if (sa != null) { + p.setValue(sa); + } + } + + } + } else { + // parameter unknown -- just add + newParameter(key, null, val, 'f'); + } + } + + } + + /** + * Creates a new DocuImage instance. + * + * The type of DocuImage is specified by docuImageType. + * + * @return DocuImage + */ + public DocuImage getDocuImageInstance() { + DocuImageImpl di = null; + try { + if (docuImageClass == null) { + docuImageClass = Class.forName(getAsString("docuimage-class")); + } + di = (DocuImageImpl) docuImageClass.newInstance(); + } catch (Exception e) { + } + return di; + } + + /** + * @return Returns the docuImageClass. + */ + public Class getDocuImageClass() { + return docuImageClass; + } + /** + * @param docuImageClass The docuImageClass to set. + */ + public void setDocuImageClass(Class docuImageClass) { + this.docuImageClass = docuImageClass; + } +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/DigilibImageWorker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/DigilibImageWorker.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,263 @@ +/* DigilibImageWorker.java -- worker for image operations + * + * Digital Image Library servlet components + * + * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 19.10.2004 + */ + +package digilib.servlet; + +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import digilib.image.DocuImage; +import digilib.image.ImageOpException; +import digilib.io.FileOpException; +import digilib.io.ImageFile; + +/** + * worker for image operations. + * + * @author casties + * + */ +public class DigilibImageWorker extends DigilibWorker { + + private DigilibConfiguration dlConfig; + + HttpServletResponse response; + + long startTime; + + String mimeType; + + int scaleQual; + + DigilibRequest dlRequest; + + float paramROT; + + float paramCONT; + + float paramBRGT; + + float[] paramRGBM; + + float[] paramRGBA; + + ImageFile fileToLoad; + + float areaXoff; + + float areaYoff; + + float areaWidth; + + float areaHeight; + + float scaleXY; + + Rectangle2D outerUserImgArea; + + Rectangle2D innerUserImgArea; + + float minSubsample; + + boolean wholeRotArea; + + /** + * @param dlConfig + * @param response + * @param mimeType + * @param scaleQual + * @param dlRequest + * @param paramROT + * @param paramCONT + * @param paramBRGT + * @param paramRGBM + * @param paramRGBA + * @param fileToLoad + * @param areaXoff + * @param outerUserImgArea + * @param innerUserImgArea + * @param minSubsample + * @param wholeRotArea + */ + public DigilibImageWorker(DigilibConfiguration dlConfig, + HttpServletResponse response, String mimeType, int scaleQual, + DigilibRequest dlRequest, float paramROT, float paramCONT, + float paramBRGT, float[] paramRGBM, float[] paramRGBA, + ImageFile fileToLoad, float scaleXY, Rectangle2D outerUserImgArea, + Rectangle2D innerUserImgArea, float minSubsample, + boolean wholeRotArea) { + super(); + this.dlConfig = dlConfig; + this.response = response; + this.mimeType = mimeType; + this.scaleQual = scaleQual; + this.dlRequest = dlRequest; + this.paramROT = paramROT; + this.paramCONT = paramCONT; + this.paramBRGT = paramBRGT; + this.paramRGBM = paramRGBM; + this.paramRGBA = paramRGBA; + this.fileToLoad = fileToLoad; + this.scaleXY = scaleXY; + this.outerUserImgArea = outerUserImgArea; + this.innerUserImgArea = innerUserImgArea; + this.minSubsample = minSubsample; + this.wholeRotArea = wholeRotArea; + } + + /* + * do the work + */ + public void work() throws FileOpException, IOException, ImageOpException { + ; + logger.debug("image worker " + this.getName() + " working"); + startTime = System.currentTimeMillis(); + + /* crop and scale image */ + + // new DocuImage instance + DocuImage docuImage = dlConfig.getDocuImageInstance(); + if (docuImage == null) { + throw new ImageOpException("Unable to load DocuImage class!"); + } + + // set interpolation quality + docuImage.setQuality(scaleQual); + + // use subimage loading if possible + if (docuImage.isSubimageSupported()) { + logger.debug("Subimage: scale " + scaleXY + " = " + (1 / scaleXY)); + float subf = 1f; + float subsamp = 1f; + if (scaleXY < 1) { + subf = 1 / scaleXY; + // for higher quality reduce subsample factor by + // minSubsample + if (scaleQual > 0) { + subsamp = (float) Math.max(Math.floor(subf / minSubsample), + 1d); + } else { + subsamp = (float) Math.floor(subf); + } + scaleXY = subsamp / subf; + logger.debug("Using subsampling: " + subsamp + " rest " + + scaleXY); + } + + docuImage.loadSubimage(fileToLoad, outerUserImgArea.getBounds(), + (int) subsamp); + + logger.debug("SUBSAMP: " + subsamp + " -> " + docuImage.getWidth() + + "x" + docuImage.getHeight()); + + docuImage.scale(scaleXY, scaleXY); + + } else { + // else load and crop the whole file + docuImage.loadImage(fileToLoad); + docuImage.crop((int) areaXoff, (int) areaYoff, (int) areaWidth, + (int) areaHeight); + + docuImage.scale(scaleXY, scaleXY); + } + + // mirror image + // operation mode: "hmir": mirror horizontally, "vmir": mirror + // vertically + if (dlRequest.hasOption("mo", "hmir")) { + docuImage.mirror(0); + } + if (dlRequest.hasOption("mo", "vmir")) { + docuImage.mirror(90); + } + + // rotate image + if (paramROT != 0d) { + docuImage.rotate(paramROT); + if (wholeRotArea) { + // crop to the inner bounding box + float xcrop = (float) (docuImage.getWidth() - innerUserImgArea + .getWidth() + * scaleXY); + float ycrop = (float) (docuImage.getHeight() - innerUserImgArea + .getHeight() + * scaleXY); + if ((xcrop > 0) || (ycrop > 0)) { + // only crop smaller + xcrop = (xcrop > 0) ? xcrop : 0; + ycrop = (ycrop > 0) ? ycrop : 0; + // crop image + docuImage.crop((int) (xcrop / 2), (int) (ycrop / 2), + (int) (docuImage.getWidth() - xcrop), + (int) (docuImage.getHeight() - ycrop)); + } + } + + } + + // color modification + if ((paramRGBM != null) || (paramRGBA != null)) { + // make shure we actually have two arrays + if (paramRGBM == null) { + paramRGBM = new float[3]; + } + if (paramRGBA == null) { + paramRGBA = new float[3]; + } + // calculate "contrast" values (c=2^x) + float[] mult = new float[3]; + for (int i = 0; i < 3; i++) { + mult[i] = (float) Math.pow(2, (float) paramRGBM[i]); + } + docuImage.enhanceRGB(mult, paramRGBA); + } + + // contrast and brightness enhancement + if ((paramCONT != 0f) || (paramBRGT != 0f)) { + float mult = (float) Math.pow(2, paramCONT); + docuImage.enhance(mult, paramBRGT); + } + + logger.debug("time " + (System.currentTimeMillis() - startTime) + "ms"); + + /* write the resulting image */ + + // setup output -- if source is JPG then dest will be JPG else it's + // PNG + if (mimeType.equals("image/jpeg") || mimeType.equals("image/jp2")) { + mimeType = "image/jpeg"; + } else { + mimeType = "image/png"; + } + response.setContentType(mimeType); + + // write the image + docuImage.writeImage(mimeType, response.getOutputStream()); + response.flushBuffer(); + + logger.info("image worker " + this.getName() + " done in " + + (System.currentTimeMillis() - startTime)); + + docuImage.dispose(); + } +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/DigilibJob.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/DigilibJob.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,75 @@ +/* DigilibJob.java -- digilib job for worker + * + * Digital Image Library servlet components + * + * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 18.10.2004 + */ +package digilib.servlet; + +import digilib.image.DocuImage; + +/** digilib job for worker. + * + * @author casties + * + */ +public class DigilibJob { + + private DigilibConfiguration config; + + private DigilibRequest request; + + private DocuImage image; + + + /** + * @return Returns the config. + */ + public DigilibConfiguration getConfig() { + return config; + } + /** + * @param config The config to set. + */ + public void setConfig(DigilibConfiguration config) { + this.config = config; + } + /** + * @return Returns the image. + */ + public DocuImage getImage() { + return image; + } + /** + * @param image The image to set. + */ + public void setImage(DocuImage image) { + this.image = image; + } + /** + * @return Returns the request. + */ + public DigilibRequest getRequest() { + return request; + } + /** + * @param request The request to set. + */ + public void setRequest(DigilibRequest request) { + this.request = request; + } +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/DigilibManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/DigilibManager.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,62 @@ +/* DigilibManager.java -- work queue manager + * + * Digital Image Library servlet components + * + * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 18.10.2004 + */ + +package digilib.servlet; + +import EDU.oswego.cs.dl.util.concurrent.BoundedBuffer; +import EDU.oswego.cs.dl.util.concurrent.Executor; +import EDU.oswego.cs.dl.util.concurrent.PooledExecutor; + +/** work queue manager. + * + * @author casties + * + */ +public class DigilibManager implements Executor { + + private PooledExecutor workerQueue = null; + + private BoundedBuffer jobQueue = null; + + /** + * @param numFastLanes + * @param numSlowLanes + */ + public DigilibManager(int numLanes, int queueMax) { + super(); + + // create job queue + jobQueue = new BoundedBuffer(queueMax); + // create work queue + workerQueue = new PooledExecutor(jobQueue, numLanes); + workerQueue.abortWhenBlocked(); + + } + + + public void execute(Runnable worker) throws InterruptedException { + workerQueue.execute(worker); + } + + public int getQueueSize() { + return jobQueue.size(); + } +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/DigilibSender.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/DigilibSender.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,73 @@ +/* DigilibSender.java -- image file send worker + * + * Digital Image Library servlet components + * + * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 18.10.2004 + */ +package digilib.servlet; + +import java.io.File; + +import javax.servlet.http.HttpServletResponse; + +import digilib.io.FileOpException; + +/** + * image file send worker. + * + * @author casties + * + */ +public class DigilibSender extends DigilibWorker { + + private File file; + + private String mimetype; + + private HttpServletResponse response; + + /** + * @param file + * @param mimetype + * @param response + */ + public DigilibSender(File file, String mimetype, + HttpServletResponse response) { + super(); + this.file = file; + this.mimetype = mimetype; + this.response = response; + } + + /** + * Actually send the file. + * + */ + public void work() { + logger.debug("worker " + this.getName() + " sending file:" + + file.getName()); + try { + //sleep(2000); + ServletOps.sendFileImmediately(file, mimetype, response); + } catch (FileOpException e) { + logger.error("Unable to send file " + file.getPath() + " because " + + e); + } + logger.debug("worker "+this.getName()+" done"); + } + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/DigilibWorker.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/DigilibWorker.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,107 @@ +/* DigilibWorker.java -- image operation worker + * + * Digital Image Library servlet components + * + * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 18.10.2004 + */ +package digilib.servlet; + +import org.apache.log4j.Logger; + +import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore; +import EDU.oswego.cs.dl.util.concurrent.Semaphore; + + + +/** image operation worker. + * + * @author casties + * + */ +public abstract class DigilibWorker extends Thread { + + protected static Logger logger = Logger.getLogger(DigilibWorker.class); + + private static int runningThreads = 0; + + public static Semaphore lock = new FIFOSemaphore(4); + + protected boolean busy = false; + + protected Exception error; + + /** + * @param job + */ + public DigilibWorker() { + super(); + busy = true; + error = null; + } + + + public abstract void work() throws Exception; + + /** Actually do the work. + * + * @see java.lang.Runnable#run() + */ + public void run() { + try { + lock.acquire(); + logger.debug((++runningThreads)+" running threads"); + try { + work(); + } catch (Exception e) { + error = e; + logger.error(e); + } + synchronized (this) { + busy = false; + this.notify(); + } + runningThreads--; + lock.release(); + } catch (InterruptedException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + + /** + * @return Returns the busy. + */ + public boolean isBusy() { + return busy; + } + + /** returns if an error occurred. + * + * @return + */ + public boolean hasError() { + return (error != null); + } + + /** + * @return Returns the error. + */ + public Exception getError() { + return error; + } + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/DocumentBean.java --- a/servlet/src/digilib/servlet/DocumentBean.java Mon Oct 18 15:40:54 2004 +0200 +++ b/servlet/src/digilib/servlet/DocumentBean.java Thu Oct 21 20:53:37 2004 +0200 @@ -1,198 +1,295 @@ -/* DocumentBean -- Access control bean for JSP - - Digital Image Library servlet components - - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ +/* + * DocumentBean -- Access control bean for JSP + * + * Digital Image Library servlet components + * + * Copyright (C) 2001, 2002, 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + */ package digilib.servlet; +import java.util.List; -import java.util.*; -import javax.servlet.*; -import javax.servlet.http.*; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; -import digilib.*; -import digilib.io.*; -import digilib.auth.*; +import digilib.auth.AuthOpException; +import digilib.auth.AuthOps; +import digilib.io.DocuDirCache; +import digilib.io.DocuDirectory; +import digilib.io.FileOps; +import digilib.io.ImageFileset; -public class DocumentBean implements AuthOps { +public class DocumentBean { + + // general logger + private static Logger logger = Logger.getLogger("digilib.docubean"); + + // AuthOps object to check authorization + private AuthOps authOp; - // Utils object for logging - private Utils util = new Utils(5); - // AuthOps object to check authorization - private AuthOps authOp; - // FileOps object - private FileOps fileOp = new FileOps(util); + // use authorization database + private boolean useAuthentication = true; + + // path to add for authenticated access + private String authURLPath = ""; + + // DocuDirCache + private DocuDirCache dirCache = null; + + // DigilibConfiguration object + private DigilibConfiguration dlConfig; + + // DigilibRequest object + private DigilibRequest dlRequest = null; - // base directories in order of preference (prescaled versions first) - private String[] baseDirs = {"/docuserver/scaled/small", "/docuserver/images", "/docuserver/scans/quellen"}; - // part of URL path to prepend for authenticated access - private String authURLpath = "authenticated/"; + /** + * Constructor for DocumentBean. + */ + public DocumentBean() { + super(); + } - - public DocumentBean() { - } + public DocumentBean(ServletConfig conf) { + try { + setConfig(conf); + } catch (Exception e) { + logger.fatal("ERROR: Unable to read config: ", e); + } + } - public void setConfig(ServletConfig conf) throws ServletException { - util.dprintln(10, "setConfig"); - // servletOps takes a ServletConfig to get the config file name - ServletOps servletOp = new ServletOps(util, conf); - /** - * basedir-list : List of document directories - */ - String bl = servletOp.tryToGetInitParam("basedir-list", null); - if ((bl != null)&&(bl.length() > 0)) { - // split list into directories - StringTokenizer dirs = new StringTokenizer(bl, ":"); - int n = dirs.countTokens(); - if (n > 0) { - // add directories into array - baseDirs = new String[n]; - for (int i = 0; i < n; i++) { - baseDirs[i] = dirs.nextToken(); - } - } - util.dprintln(3, "basedir-list: "+bl); - } - /** - * auth-url-path : part of URL to indicate authenticated access - */ - String au = servletOp.tryToGetInitParam("auth-url-path", null); - if ((au != null)&&(au.length() > 0)) { - authURLpath = au; - util.dprintln(3, "auth-url-path: "+au); - } - /** - * authentication - */ - try { - // DB version - //private AuthOps authOp = new DBAuthOpsImpl(util); - // XML version - String cp = servletOp.tryToGetInitParam("auth-file", "/docuserver/www/digitallibrary/WEB-INF/digilib-auth.xml"); - util.dprintln(3, "auth-file: "+cp); - authOp = new XMLAuthOps(util, cp); - } catch (AuthOpException e) { - throw new ServletException(e); - } - } + public void setConfig(ServletConfig conf) throws ServletException { + logger.debug("setConfig"); + // get our ServletContext + ServletContext context = conf.getServletContext(); + // see if there is a Configuration instance + dlConfig = (DigilibConfiguration) context + .getAttribute("digilib.servlet.configuration"); + if (dlConfig == null) { + // create new Configuration + throw new ServletException("ERROR: No configuration!"); + } + + // get cache + dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); + + /* + * authentication + */ + useAuthentication = dlConfig.getAsBoolean("use-authorization"); + authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); + authURLPath = dlConfig.getAsString("auth-url-path"); + if (useAuthentication && (authOp == null)) { + throw new ServletException( + "ERROR: use-authorization configured but no AuthOp!"); + } + } + + /** + * check if the request must be authorized to access filepath + */ + public boolean isAuthRequired(DigilibRequest request) + throws AuthOpException { + logger.debug("isAuthRequired"); + return useAuthentication ? authOp.isAuthRequired(request) : false; + } + + /** + * check if the request is allowed to access filepath + */ + public boolean isAuthorized(DigilibRequest request) throws AuthOpException { + logger.debug("isAuthorized"); + return useAuthentication ? authOp.isAuthorized(request) : true; + } + + /** + * return a list of authorization roles needed for request to access the + * specified path + */ + public List rolesForPath(DigilibRequest request) throws AuthOpException { + logger.debug("rolesForPath"); + return useAuthentication ? authOp.rolesForPath(request) : null; + } + + /** + * check request authorization against a list of roles + */ + public boolean isRoleAuthorized(List roles, DigilibRequest request) { + logger.debug("isRoleAuthorized"); + return useAuthentication ? authOp.isRoleAuthorized(roles, request) + : true; + } + + /** + * check for authenticated access and redirect if necessary + */ + public boolean doAuthentication(HttpServletResponse response) + throws Exception { + return doAuthentication(dlRequest, response); + } - public String getDocuPath(HttpServletRequest request) { - util.dprintln(10, "getDocuPath"); - // fetch query string - String qs = request.getQueryString(); - String fn = ""; - if (qs != null && qs.length() > 0) { - // the file name is in the request before the first "+" - int endfn = qs.indexOf("+"); - if (endfn > 0) { - fn = qs.substring(0, endfn); - } else { - fn = qs; - } - } - util.dprintln(4, "docuPath: "+fn); - return fn; - } - - /** - * check if the request must be authorized to access filepath - */ - public boolean isAuthRequired(HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "isAuthRequired"); - return authOp.isAuthRequired(getDocuPath(request), request); - } + /** + * check for authenticated access and redirect if necessary + */ + public boolean doAuthentication(DigilibRequest request, + HttpServletResponse response) throws Exception { + logger.debug("doAuthentication"); + if (!useAuthentication) { + // shortcut if no authentication + return true; + } + // check if we are already authenticated + if (((HttpServletRequest) request.getServletRequest()).getRemoteUser() == null) { + logger.debug("unauthenticated so far"); + // if not maybe we must? + if (isAuthRequired(request)) { + logger.debug("auth required, redirect"); + // we are not yet authenticated -> redirect + response.sendRedirect(authURLPath + + ((HttpServletRequest) request.getServletRequest()) + .getServletPath() + + "?" + + ((HttpServletRequest) request.getServletRequest()) + .getQueryString()); + } + } + return true; + } - public boolean isAuthRequired(String filepath, HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "isAuthRequired"); - return authOp.isAuthRequired(filepath, request); - } - - /** - * check if the request is allowed to access filepath - */ - public boolean isAuthorized(HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "isAuthorized"); - return authOp.isAuthorized(getDocuPath(request), request); - } + /** + * Sets the current DigilibRequest. Also completes information in the request. + * + * @param dlRequest + * The dlRequest to set. + */ + public void setRequest(DigilibRequest dlRequest) throws Exception { + this.dlRequest = dlRequest; + if (dirCache == null) { + return; + } + String fn = dlRequest.getFilePath(); + // get information about the file + ImageFileset fileset = (ImageFileset) dirCache.getFile(fn, dlRequest + .getAsInt("pn"), FileOps.CLASS_IMAGE); + if (fileset == null) { + return; + } + // add file name + dlRequest.setValue("img.fn", fileset.getName()); + // add dpi + dlRequest.setValue("img.dpix", new Double(fileset.getResX())); + dlRequest.setValue("img.dpiy", new Double(fileset.getResY())); + // get number of pages in directory + DocuDirectory dd = dirCache.getDirectory(fn); + if (dd != null) { + dlRequest.setValue("pt", dd.size()); + } + } - public boolean isAuthorized(String filepath, HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "isAuthorized"); - return authOp.isAuthorized(filepath, request); - } + /** + * get the first page number in the directory (not yet functional) + */ + public int getFirstPage(DigilibRequest request) { + logger.debug("getFirstPage"); + return 1; + } - /** - * return a list of authorization roles needed for request - * to access the specified path - */ - public List rolesForPath(String filepath, HttpServletRequest request) throws AuthOpException { - util.dprintln(10, "rolesForPath"); - return authOp.rolesForPath(filepath, request); - } + /** + * get the number of pages/files in the directory + */ + public int getNumPages() throws Exception { + return getNumPages(dlRequest); + } - /** - * check request authorization against a list of roles - */ - public boolean isRoleAuthorized(List roles, HttpServletRequest request) { - util.dprintln(10, "isRoleAuthorized"); - return authOp.isRoleAuthorized(roles, request); - } + /** + * get the number of pages/files in the directory + */ + public int getNumPages(DigilibRequest request) throws Exception { + logger.debug("getNumPages"); + DocuDirectory dd = (dirCache != null) ? dirCache.getDirectory(request + .getFilePath()) : null; + if (dd != null) { + return dd.size(); + } + return 0; + } - /** - * check for authenticated access and redirect if necessary - */ - public boolean doAuthentication(HttpServletRequest request, HttpServletResponse response) throws Exception { - util.dprintln(10, "doAuthentication"); - // check if we are already authenticated - if (request.getRemoteUser() == null) { - util.dprintln(3, "unauthenticated so far"); - // if not maybe we must? - if (isAuthRequired(request)) { - util.dprintln(3, "auth required, redirect"); - // we are not yet authenticated -> redirect - response.sendRedirect(authURLpath+request.getServletPath()+"?"+request.getQueryString()); - } - } - return true; - } + /** + * Returns the dlConfig. + * + * @return DigilibConfiguration + */ + public DigilibConfiguration getDlConfig() { + return dlConfig; + } + + /** + * returns if the zoom area in the request can be moved + * + * @return + */ + public boolean canMoveRight() { + float ww = dlRequest.getAsFloat("ww"); + float wx = dlRequest.getAsFloat("wx"); + return (ww + wx < 1.0); + } - /** - * get the first page number in the directory - * (not yet functional) - */ - public int getFirstPage(HttpServletRequest request) { - return getFirstPage(getDocuPath(request), request); - } - - public int getFirstPage(String filepath, HttpServletRequest request) { - util.dprintln(10, "getFirstPage"); - return 1; - } + /** + * returns if the zoom area in the request can be moved + * + * @return + */ + public boolean canMoveLeft() { + float ww = dlRequest.getAsFloat("ww"); + float wx = dlRequest.getAsFloat("wx"); + return ((ww < 1.0) && (wx > 0)); + } - /** - * get the number of pages/files in the directory - */ - public int getNumPages(HttpServletRequest request) throws Exception { - return getNumPages(getDocuPath(request), request); - } + /** + * returns if the zoom area in the request can be moved + * + * @return + */ + public boolean canMoveUp() { + float wh = dlRequest.getAsFloat("wh"); + float wy = dlRequest.getAsFloat("wy"); + return ((wh < 1.0) && (wy > 0)); + } - public int getNumPages(String filepath, HttpServletRequest request) throws Exception { - util.dprintln(10, "getNumPages"); - return fileOp.getNumFilesVariant(baseDirs, "/"+filepath, true); - } + /** + * returns if the zoom area in the request can be moved + * + * @return + */ + public boolean canMoveDown() { + float wh = dlRequest.getAsFloat("wh"); + float wy = dlRequest.getAsFloat("wy"); + return (wh + wy < 1.0); + } -} + /** + * @return Returns the dlRequest. + */ + public DigilibRequest getRequest() { + return dlRequest; + } + +} \ No newline at end of file diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/Initialiser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/Initialiser.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,173 @@ +/* Initialiser.java -- initalisation servlet for setup tasks + * + * Digital Image Library servlet components + * + * Copyright (C) 2004 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 18.10.2004 + */ +package digilib.servlet; + +import java.io.File; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; + +import org.apache.log4j.Logger; +import org.apache.log4j.xml.DOMConfigurator; + +import digilib.auth.AuthOps; +import digilib.auth.XMLAuthOps; +import digilib.io.AliasingDocuDirCache; +import digilib.io.DocuDirCache; +import digilib.io.FileOps; + +/** + * Initalisation servlet for setup tasks. + * + * @author casties + * + */ +public class Initialiser extends HttpServlet { + + private static final long serialVersionUID = -5126621114382549343L; + + /** servlet version */ + public static final String iniVersion = "0.1a1"; + + /** gengeral logger for this class */ + private static Logger logger = Logger.getLogger("digilib.init"); + + /** AuthOps instance */ + AuthOps authOp; + + /** DocuDirCache instance */ + DocuDirCache dirCache; + + /** DigilibConfiguration instance */ + DigilibConfiguration dlConfig; + + /** use authorization database */ + boolean useAuthentication = false; + + private DigilibManager fastQueue; + + private DigilibManager slowQueue; + + /** + * Initialisation on first run. + * + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + */ + public void init(ServletConfig config) throws ServletException { + super.init(config); + + System.out + .println("***** Digital Image Library Initialisation Servlet (version " + + iniVersion + ") *****"); + + // get our ServletContext + ServletContext context = config.getServletContext(); + // see if there is a Configuration instance + dlConfig = (DigilibConfiguration) context + .getAttribute("digilib.servlet.configuration"); + if (dlConfig == null) { + // create new Configuration + try { + dlConfig = new DigilibConfiguration(config); + + /* + * further initialization + */ + + // set up the logger + File logConf = ServletOps.getConfigFile((File) dlConfig + .getValue("log-config-file"), config); + DOMConfigurator.configure(logConf.getAbsolutePath()); + dlConfig.setValue("log-config-file", logConf); + // say hello in the log file + logger + .info("***** Digital Image Library Initialisation Servlet (version " + + iniVersion + ") *****"); + // directory cache + String[] bd = (String[]) dlConfig.getValue("basedir-list"); + int[] fcs = { FileOps.CLASS_IMAGE, FileOps.CLASS_TEXT, + FileOps.CLASS_SVG }; + if (dlConfig.getAsBoolean("use-mapping")) { + // with mapping file + File mapConf = ServletOps.getConfigFile((File) dlConfig + .getValue("mapping-file"), config); + dirCache = new AliasingDocuDirCache(bd, fcs, mapConf, + dlConfig); + dlConfig.setValue("mapping-file", mapConf); + } else { + // without mapping + dirCache = new DocuDirCache(bd, fcs, dlConfig); + } + dlConfig.setValue("servlet.dir.cache", dirCache); + // useAuthentication + if (dlConfig.getAsBoolean("use-authorization")) { + // DB version + //authOp = new DBAuthOpsImpl(util); + // XML version + File authConf = ServletOps.getConfigFile((File) dlConfig + .getValue("auth-file"), config); + authOp = new XMLAuthOps(authConf); + dlConfig.setValue("servlet.auth.op", authOp); + dlConfig.setValue("auth-file", authConf); + } + // DocuImage class + Class cl = Class.forName(dlConfig + .getAsString("docuimage-class")); + dlConfig.setDocuImageClass(cl); + dlConfig.setValue("servlet.docuimage.class", cl.getName()); + // DigilibManager work queue + int fl = dlConfig.getAsInt("worker-fast-lanes"); + int fq = dlConfig.getAsInt("worker-fast-queue"); + fastQueue = new DigilibManager(fl, fq); + dlConfig.setValue("servlet.fast.queue", fastQueue); + int sl = dlConfig.getAsInt("worker-slow-lanes"); + int sq = dlConfig.getAsInt("worker-slow-queue"); + slowQueue = new DigilibManager(sl, sq); + dlConfig.setValue("servlet.slow.queue", slowQueue); + // set as the servlets main config + context.setAttribute("digilib.servlet.configuration", dlConfig); + + } catch (Exception e) { + throw new ServletException(e); + } + } else { + // say hello in the log file + logger + .info("***** Digital Image Library Initialisation Servlet (version " + + iniVersion + ") *****"); + logger.warn("Already initialised?"); + // set our AuthOps + useAuthentication = dlConfig.getAsBoolean("use-authorization"); + // AuthOps instance + authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); + // DocuDirCache instance + dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); + // work queues + fastQueue = (DigilibManager) dlConfig + .getValue("servlet.fast.queue"); + slowQueue = (DigilibManager) dlConfig + .getValue("servlet.slow.queue"); + } + } + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/Mapper.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/Mapper.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,399 @@ +/* + * Mapper -- Servlet for creating image-maps from SVG graphics + * + * Digital Image Library servlet components + * + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html You should have received a copy of + * the GNU General Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + * + * Created on 25.11.2003 by casties + */ + +package digilib.servlet; + +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.batik.dom.svg.SAXSVGDocumentFactory; +import org.apache.batik.transcoder.SVGAbstractTranscoder; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.apache.batik.util.XMLResourceDescriptor; +import org.apache.log4j.Logger; +import org.w3c.dom.svg.SVGDocument; +import org.w3c.dom.svg.SVGSVGElement; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.XMLFilterImpl; + +import digilib.auth.AuthOps; +import digilib.io.DocuDirCache; +import digilib.io.FileOpException; +import digilib.io.FileOps; +import digilib.io.SVGFile; + +/** + * Servlet for creating image-maps from SVG graphics + * + * @author casties + * + */ +public class Mapper extends HttpServlet { + + private static final long serialVersionUID = 6134014474526638223L; + + /** Servlet version */ + public static String servletVersion = "0.1a0"; + /** DigilibConfiguration instance */ + DigilibConfiguration dlConfig = null; + /** general logger */ + Logger logger = Logger.getLogger("digilib.mapper"); + /** AuthOps instance */ + AuthOps authOp; + /** DocuDirCache instance */ + DocuDirCache dirCache; + /** SVG document factory */ + SAXSVGDocumentFactory docFactory; + + /** use authentication */ + boolean useAuthentication = false; + + /* + * (non-Javadoc) + * + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + */ + public void init(ServletConfig config) throws ServletException { + super.init(config); + + System.out.println( + "***** Digital Image Library Image Map Servlet (version " + + servletVersion + + ") *****"); + + // get our ServletContext + ServletContext context = config.getServletContext(); + // see if there is a Configuration instance + dlConfig = + (DigilibConfiguration) context.getAttribute( + "digilib.servlet.configuration"); + if (dlConfig == null) { + // no Configuration + throw new ServletException("ERROR: No Configuration!"); + } + // say hello in the log file + logger.info( + "***** Digital Image Library Image Map Servlet (version " + + servletVersion + + ") *****"); + + // set our AuthOps + useAuthentication = dlConfig.getAsBoolean("use-authorization"); + authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); + // DocuDirCache instance + dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); + // parser name as a String (I hate you for not using JAXP, Batik!) + String parserName = null; + try { + // try the proper JAXP way + parserName = + SAXParserFactory + .newInstance() + .newSAXParser() + .getXMLReader() + .getClass() + .getName(); + } catch (Exception e) { + // fall back to Batik's hack + parserName = XMLResourceDescriptor.getXMLParserClassName(); + } + logger.debug("parser name: " + parserName); + docFactory = new SAXSVGDocumentFactory(parserName); + } + + /* + * (non-Javadoc) + * + * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + protected void doGet( + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + // create new request with defaults + DigilibRequest dlReq = new DigilibRequest(); + // set with request parameters + dlReq.setWithRequest(request); + // add DigilibRequest to ServletRequest + request.setAttribute("digilib.servlet.request", dlReq); + // do the processing + processRequest(request, response); + } + + /* + */ + protected void doPost( + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + // create new request with defaults + DigilibRequest dlReq = new DigilibRequest(); + // set with request parameters + dlReq.setWithRequest(request); + // add DigilibRequest to ServletRequest + request.setAttribute("digilib.servlet.request", dlReq); + // do the processing + processRequest(request, response); + } + + protected void processRequest( + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + + logger.debug("request: " + request.getQueryString()); + // time for benchmarking + long startTime = System.currentTimeMillis(); + + /* + * request parameters + */ + DigilibRequest dlRequest = + (DigilibRequest) request.getAttribute("digilib.servlet.request"); + + // destination image width + int paramDW = dlRequest.getAsInt("dw"); + // destination image height + int paramDH = dlRequest.getAsInt("dh"); + // relative area x_offset (0..1) + float paramWX = dlRequest.getAsFloat("wx"); + // relative area y_offset + float paramWY = dlRequest.getAsFloat("wy"); + // relative area width (0..1) + double paramWW = dlRequest.getAsFloat("ww"); + // relative area height + double paramWH = dlRequest.getAsFloat("wh"); + + try { + + /* + * find the file to load/send + */ + + // get PathInfo + String loadPathName = dlRequest.getFilePath(); + // find the file(set) + SVGFile fileToLoad = + (SVGFile) dirCache.getFile( + loadPathName, + dlRequest.getAsInt("pn"), + FileOps.CLASS_SVG); + if (fileToLoad == null) { + throw new FileOpException( + "File " + + loadPathName + + "(" + + dlRequest.getAsString("pn") + + ") not found."); + } + + /* + * read the SVG document + */ + + // read the document + SVGDocument doc = + docFactory.createSVGDocument( + fileToLoad.getFile().toURI().toString()); + // extract the SVG root + SVGSVGElement svgroot = doc.getRootElement(); + // get document width and height + float imgWidth = svgroot.getWidth().getBaseVal().getValue(); + float imgHeight = svgroot.getHeight().getBaseVal().getValue(); + + /* + * set up the transcoder + */ + + // create a PNG transcoder + MapTranscoder transcoder = new MapTranscoder(); + // create the transcoder input + TranscoderInput input = new TranscoderInput(doc); + logger.info("Loading: " + fileToLoad.getFile()); + // create the transcoder output + TranscoderOutput output = +// new TranscoderOutput(new FileWriter("/tmp/maptest.out")); + new TranscoderOutput(new MapFilter()); + // output is image/png + //response.setContentType("image/png"); + + // area of interest + Rectangle2D aoi = + new Rectangle2D.Double( + paramWX * imgWidth, + paramWY * imgHeight, + paramWW * imgWidth, + paramWH * imgHeight); + transcoder.addTranscodingHint(PNGTranscoder.KEY_AOI, aoi); + + // destination image dimensions + if (paramDW > 0) { + transcoder.addTranscodingHint( + SVGAbstractTranscoder.KEY_WIDTH, + new Float(paramDW)); + } + if (paramDH > 0) { + transcoder.addTranscodingHint( + SVGAbstractTranscoder.KEY_HEIGHT, + new Float(paramDH)); + } + + /* + * transcode + */ + + transcoder.transcode(input, output); + + logger.info( + "Done in " + (System.currentTimeMillis() - startTime) + "ms"); + + /* + * error handling + */ + + } catch (FileOpException e) { + logger.error("ERROR: File IO Error: ", e); + try { + ServletOps.htmlMessage("ERROR: File IO Error: " + e, response); + } catch (Exception ex) { + } // so we don't get a loop + } catch (TranscoderException e) { + logger.error("ERROR: SVG encoder error: ", e); + try { + ServletOps.htmlMessage( + "ERROR: SVG encoder error: " + e, + response); + } catch (Exception ex) { + } // so we don't get a loop + } + + } + + protected class MapFilter extends XMLFilterImpl { + + /* + * (non-Javadoc) + * + * @see org.xml.sax.ContentHandler#characters(char[], int, int) + */ + public void characters(char[] ch, int start, int length) + throws SAXException { + // TODO Auto-generated method stub + logger.debug( + "characters('" + + ch.toString() + + "'," + + start + + "," + + length + + ")"); + super.characters(ch, start, length); + } + + /* + * (non-Javadoc) + * + * @see org.xml.sax.ContentHandler#endDocument() + */ + public void endDocument() throws SAXException { + // TODO Auto-generated method stub + logger.debug("endDocument"); + super.endDocument(); + } + + /* + * (non-Javadoc) + * + * @see org.xml.sax.ContentHandler#endElement(java.lang.String, + * java.lang.String, java.lang.String) + */ + public void endElement(String uri, String localName, String qName) + throws SAXException { + // TODO Auto-generated method stub + logger.debug( + "endElement(" + uri + "," + localName + "," + qName + ")"); + super.endElement(uri, localName, qName); + } + + /* + * (non-Javadoc) + * + * @see org.xml.sax.ContentHandler#startDocument() + */ + public void startDocument() throws SAXException { + // TODO Auto-generated method stub + logger.debug("startDocument"); + super.startDocument(); + } + + /* + * (non-Javadoc) + * + * @see org.xml.sax.ContentHandler#startElement(java.lang.String, + * java.lang.String, java.lang.String, org.xml.sax.Attributes) + */ + public void startElement( + String uri, + String localName, + String qName, + Attributes atts) + throws SAXException { + // TODO Auto-generated method stub + logger.debug( + "startElement(" + + uri + + "," + + localName + + "," + + qName + + "," + + atts.toString() + + ")"); + super.startElement(uri, localName, qName, atts); + } + + } + + class MapTranscoder extends SVGAbstractTranscoder { + + /** + * + */ + public MapTranscoder() { + super(); + } + +} + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/Raster.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/Raster.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,293 @@ +/* + * Raster -- Servlet for displaying rasterized SVG graphics + * + * Digital Image Library servlet components + * + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html You should have received a copy of + * the GNU General Public License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + * + * Created on 25.11.2003 by casties + */ + +package digilib.servlet; + +import java.awt.geom.Rectangle2D; +import java.io.IOException; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.parsers.SAXParserFactory; + +import org.apache.batik.dom.svg.SAXSVGDocumentFactory; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.apache.batik.util.XMLResourceDescriptor; +import org.apache.log4j.Logger; +import org.w3c.dom.svg.SVGDocument; +import org.w3c.dom.svg.SVGSVGElement; + +import digilib.auth.AuthOps; +import digilib.io.DocuDirCache; +import digilib.io.FileOpException; +import digilib.io.FileOps; +import digilib.io.SVGFile; + +/** + * Servlet for displaying SVG graphics + * + * @author casties + * + */ +public class Raster extends HttpServlet { + + private static final long serialVersionUID = -7756999389932675241L; + + /** Servlet version */ + public static String servletVersion = "0.1b1"; + /** DigilibConfiguration instance */ + DigilibConfiguration dlConfig = null; + /** general logger */ + Logger logger = Logger.getLogger("digilib.raster"); + /** AuthOps instance */ + AuthOps authOp; + /** DocuDirCache instance */ + DocuDirCache dirCache; + /** SVG document factory */ + SAXSVGDocumentFactory docFactory; + + /** use authentication */ + boolean useAuthentication = false; + + /* + * (non-Javadoc) + * + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + */ + public void init(ServletConfig config) throws ServletException { + super.init(config); + + System.out.println( + "***** Digital Image Library SVG Render Servlet (version " + + servletVersion + + ") *****"); + + // get our ServletContext + ServletContext context = config.getServletContext(); + // see if there is a Configuration instance + dlConfig = + (DigilibConfiguration) context.getAttribute( + "digilib.servlet.configuration"); + if (dlConfig == null) { + // no config + throw new ServletException("ERROR: No Configuration!"); + } + // say hello in the log file + logger.info( + "***** Digital Image Library SVG Render Servlet (version " + + servletVersion + + ") *****"); + + // set our AuthOps + useAuthentication = dlConfig.getAsBoolean("use-authorization"); + authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); + // DocuDirCache instance + dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); + // parser name as a String (I hate you for not using JAXP, Batik!) + String parserName = null; + try { + // try the proper JAXP way + parserName = SAXParserFactory.newInstance().newSAXParser().getXMLReader().getClass().getName(); + } catch (Exception e) { + // fall back to Batik's hack + parserName = XMLResourceDescriptor.getXMLParserClassName(); + } + logger.debug("parser name: "+parserName); + docFactory = new SAXSVGDocumentFactory(parserName); + } + + /* + * (non-Javadoc) + * + * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + protected void doGet( + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + // create new request with defaults + DigilibRequest dlReq = new DigilibRequest(); + // set with request parameters + dlReq.setWithRequest(request); + // add DigilibRequest to ServletRequest + request.setAttribute("digilib.servlet.request", dlReq); + // do the processing + processRequest(request, response); + } + + /* + */ + protected void doPost( + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + // create new request with defaults + DigilibRequest dlReq = new DigilibRequest(); + // set with request parameters + dlReq.setWithRequest(request); + // add DigilibRequest to ServletRequest + request.setAttribute("digilib.servlet.request", dlReq); + // do the processing + processRequest(request, response); + } + + protected void processRequest( + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + + logger.debug("request: "+request.getQueryString()); + // time for benchmarking + long startTime = System.currentTimeMillis(); + + /* + * request parameters + */ + DigilibRequest dlRequest = + (DigilibRequest) request.getAttribute("digilib.servlet.request"); + + // destination image width + int paramDW = dlRequest.getAsInt("dw"); + // destination image height + int paramDH = dlRequest.getAsInt("dh"); + // relative area x_offset (0..1) + float paramWX = dlRequest.getAsFloat("wx"); + // relative area y_offset + float paramWY = dlRequest.getAsFloat("wy"); + // relative area width (0..1) + double paramWW = dlRequest.getAsFloat("ww"); + // relative area height + double paramWH = dlRequest.getAsFloat("wh"); + + try { + + /* + * find the file to load/send + */ + + // get PathInfo + String loadPathName = dlRequest.getFilePath(); + // find the file(set) + SVGFile fileToLoad = + (SVGFile) dirCache.getFile( + loadPathName, + dlRequest.getAsInt("pn"), + FileOps.CLASS_SVG); + if (fileToLoad == null) { + throw new FileOpException( + "File " + + loadPathName + + "(" + + dlRequest.getAsString("pn") + + ") not found."); + } + + /* + * read the SVG document + */ + + // read the document + SVGDocument doc = + docFactory.createSVGDocument( + fileToLoad.getFile().toURI().toString()); + // extract the SVG root + SVGSVGElement svgroot = doc.getRootElement(); + // get document width and height + float imgWidth = svgroot.getWidth().getBaseVal().getValue(); + float imgHeight = svgroot.getHeight().getBaseVal().getValue(); + + /* + * set up the transcoder + */ + + // create a PNG transcoder + PNGTranscoder transcoder = new PNGTranscoder(); + // create the transcoder input + //InputStream is = new FileInputStream(fileToLoad.getFile()); + TranscoderInput input = new TranscoderInput(doc); + logger.info("Loading: " + fileToLoad.getFile()); + // create the transcoder output + TranscoderOutput output = + new TranscoderOutput(response.getOutputStream()); + // output is image/png + response.setContentType("image/png"); + + // area of interest + Rectangle2D aoi = + new Rectangle2D.Double( + paramWX * imgWidth, + paramWY * imgHeight, + paramWW * imgWidth, + paramWH * imgHeight); + transcoder.addTranscodingHint(PNGTranscoder.KEY_AOI, aoi); + + // destination image dimensions + if (paramDW > 0) { + transcoder.addTranscodingHint( + PNGTranscoder.KEY_WIDTH, + new Float(paramDW)); + } + if (paramDH > 0) { + transcoder.addTranscodingHint( + PNGTranscoder.KEY_HEIGHT, + new Float(paramDH)); + } + + /* + * transcode + */ + + transcoder.transcode(input, output); + + logger.info( + "Done in " + (System.currentTimeMillis() - startTime) + "ms"); + + /* + * error handling + */ + + } catch (FileOpException e) { + logger.error("ERROR: File IO Error: ", e); + try { + ServletOps.htmlMessage("ERROR: File IO Error: " + e, response); + } catch (Exception ex) { + } // so we don't get a loop + } catch (TranscoderException e) { + logger.error("ERROR: SVG encoder error: ", e); + try { + ServletOps.htmlMessage( + "ERROR: SVG encoder error: " + e, + response); + } catch (Exception ex) { + } // so we don't get a loop + } + + } + +} diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/Scaler.java --- a/servlet/src/digilib/servlet/Scaler.java Mon Oct 18 15:40:54 2004 +0200 +++ b/servlet/src/digilib/servlet/Scaler.java Thu Oct 21 20:53:37 2004 +0200 @@ -1,377 +1,684 @@ -/* Scaler -- Scaler servlet main class - - Digital Image Library servlet components - - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ +/* + * Scaler -- Scaler servlet main class + * + * Digital Image Library servlet components + * + * Copyright (C) 2001, 2002, 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + */ package digilib.servlet; -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.util.*; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.IOException; +import java.util.List; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; -import digilib.*; -import digilib.io.*; -import digilib.image.*; -import digilib.auth.*; +import digilib.auth.AuthOpException; +import digilib.auth.AuthOps; +import digilib.image.ImageOpException; +import digilib.image.ImageOps; +import digilib.image.ImageSize; +import digilib.io.DocuDirCache; +import digilib.io.FileOpException; +import digilib.io.FileOps; +import digilib.io.ImageFile; +import digilib.io.ImageFileset; +//import tilecachetool.*; +/** + * @author casties + */ //public class Scaler extends HttpServlet implements SingleThreadModel { public class Scaler extends HttpServlet { - // Utils instance with debuglevel - Utils util; - // ServletOpss instance - ServletOps servletOp; - // FileOps instance - FileOps fileOp; - // AuthOps instance - AuthOps authOp; - // global DocuImage instance (don't reuse inside a request!) - DocuImage globalImage; + private static final long serialVersionUID = -325080527268912852L; + + /** digilib servlet version (for all components) */ + public static final String dlVersion = "2.0a6"; + + /** logger for accounting requests */ + private static Logger accountlog = Logger.getLogger("account.request"); + + /** gengeral logger for this class */ + private static Logger logger = Logger.getLogger("digilib.servlet"); + + /** logger for authentication related */ + private static Logger authlog = Logger.getLogger("digilib.auth"); + + /** general error code */ + public static final int ERROR_UNKNOWN = 0; + + /** error code for authentication error */ + public static final int ERROR_AUTH = 1; + + /** error code for file operation error */ + public static final int ERROR_FILE = 2; + + /** error code for image operation error */ + public static final int ERROR_IMAGE = 3; + + /** DocuDirCache instance */ + DocuDirCache dirCache; + + /** authentication error image file */ + File denyImgFile; + + /** image error image file */ + File errorImgFile; + + /** subsampling before scaling */ + float minSubsample = 2f; + + /** send files as is? */ + boolean sendFileAllowed = true; + + /** default scaling quality */ + int defaultQuality = 1; + + /** DigilibConfiguration instance */ + DigilibConfiguration dlConfig; + + /** fast worker queue */ + DigilibManager fastQueue; + + /** slow image worker queue */ + DigilibManager slowQueue; + + /** use authorization database */ + boolean useAuthorization = true; + + /** AuthOps instance */ + AuthOps authOp; + + // EXPRIMENTAL + /** try to enlarge cropping area for "oblique" angles */ + boolean wholeRotArea = false; + + /** + * Initialisation on first run. + * + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + */ + public void init(ServletConfig config) throws ServletException { + super.init(config); + + System.out + .println("***** Digital Image Library Image Scaler Servlet (version " + + dlVersion + ") *****"); + // say hello in the log file + logger + .info("***** Digital Image Library Image Scaler Servlet (version " + + dlVersion + ") *****"); + + // get our ServletContext + ServletContext context = config.getServletContext(); + // see if there is a Configuration instance + dlConfig = (DigilibConfiguration) context + .getAttribute("digilib.servlet.configuration"); + if (dlConfig == null) { + // no Configuration + throw new ServletException("No Configuration!"); + } + // set our AuthOps + useAuthorization = dlConfig.getAsBoolean("use-authorization"); + authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); + // work queues + fastQueue = (DigilibManager) dlConfig.getValue("servlet.fast.queue"); + slowQueue = (DigilibManager) dlConfig.getValue("servlet.slow.queue"); + + // DocuDirCache instance + dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); + denyImgFile = (File) dlConfig.getValue("denied-image"); + errorImgFile = (File) dlConfig.getValue("error-image"); + sendFileAllowed = dlConfig.getAsBoolean("sendfile-allowed"); + minSubsample = dlConfig.getAsFloat("subsample-minimum"); + defaultQuality = dlConfig.getAsInt("default-quality"); + } + + /** Process the HTTP Get request */ + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + accountlog.info("GET from " + request.getRemoteAddr()); + // create new request with defaults + DigilibRequest dlReq = new DigilibRequest(); + // set with request parameters + dlReq.setWithRequest(request); + // add DigilibRequest to ServletRequest + request.setAttribute("digilib.servlet.request", dlReq); + // do the processing + processRequest(request, response); + } + + /** Process the HTTP Post request */ + public void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + accountlog.info("POST from " + request.getRemoteAddr()); + // create new request with defaults + DigilibRequest dlReq = new DigilibRequest(); + // set with request parameters + dlReq.setWithRequest(request); + // add DigilibRequest to ServletRequest + request.setAttribute("digilib.servlet.request", dlReq); + // do the processing + processRequest(request, response); + } + + /** main request handler. */ +void processRequest(HttpServletRequest request, HttpServletResponse response) + throws ServletException { + + if (dlConfig == null) { + throw new ServletException("ERROR: No Configuration!"); + } + + accountlog.debug("request: " + request.getQueryString()); + logger.debug("request: " + request.getQueryString()); + + // time for benchmarking + long startTime = System.currentTimeMillis(); + // output mime/type + String mimeType = "image/png"; + + /* parameters for a session */ + + // scale the image file to fit window size i.e. respect dw,dh + boolean scaleToFit = true; + // scale the image by a fixed factor only + boolean absoluteScale = false; + // only crop the image to fit + boolean cropToFit = false; + // send the file as is + boolean sendFile = false; + // use low resolution images only + boolean loresOnly = false; + // use hires images only + boolean hiresOnly = false; + // interpolation to use for scaling + int scaleQual = defaultQuality; + // send html error message (or image file) + boolean errorMsgHtml = false; + // mirror the image + boolean doMirror = false; + // angle of mirror axis + float mirrorAngle = 0; + // original (hires) image resolution + float origResX = 0; + float origResY = 0; + + /* request parameters */ + + DigilibRequest dlRequest = (DigilibRequest) request + .getAttribute("digilib.servlet.request"); + + // destination image width + int paramDW = dlRequest.getAsInt("dw"); + // destination image height + int paramDH = dlRequest.getAsInt("dh"); + // relative area x_offset (0..1) + float paramWX = dlRequest.getAsFloat("wx"); + // relative area y_offset + float paramWY = dlRequest.getAsFloat("wy"); + // relative area width (0..1) + float paramWW = dlRequest.getAsFloat("ww"); + // relative area height + float paramWH = dlRequest.getAsFloat("wh"); + // scale factor (additional to dw/width, dh/height) + float paramWS = dlRequest.getAsFloat("ws"); + // rotation angle + float paramROT = dlRequest.getAsFloat("rot"); + // contrast enhancement + float paramCONT = dlRequest.getAsFloat("cont"); + // brightness enhancement + float paramBRGT = dlRequest.getAsFloat("brgt"); + // color modification + float[] paramRGBM = null; + Parameter p = dlRequest.get("rgbm"); + if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) { + paramRGBM = p.parseAsFloatArray("/"); + } + float[] paramRGBA = null; + p = dlRequest.get("rgba"); + if (p.hasValue() && (!p.getAsString().equals("0/0/0"))) { + paramRGBA = p.parseAsFloatArray("/"); + } + // destination resolution (DPI) + float paramDDPIX = dlRequest.getAsFloat("ddpix"); + float paramDDPIY = dlRequest.getAsFloat("ddpiy"); + if ((paramDDPIX == 0) || (paramDDPIY == 0)) { + // if X or Y resolution isn't set, use DDPI + paramDDPIX = dlRequest.getAsFloat("ddpi"); + paramDDPIY = paramDDPIX; + } + + /* + * operation mode: "fit": always fit to page, "clip": send original + * resolution cropped, "file": send whole file (if allowed) + */ + if (dlRequest.hasOption("mo", "clip")) { + scaleToFit = false; + absoluteScale = false; + cropToFit = true; + sendFile = false; + hiresOnly = true; + } else if (dlRequest.hasOption("mo", "fit")) { + scaleToFit = true; + absoluteScale = false; + cropToFit = false; + sendFile = false; + hiresOnly = false; + } else if (dlRequest.hasOption("mo", "osize")) { + scaleToFit = false; + absoluteScale = true; + cropToFit = false; + sendFile = false; + hiresOnly = true; + } + // operation mode: "lores": try to use scaled image, "hires": use + // unscaled image + // "autores": try best fitting resolution + if (dlRequest.hasOption("mo", "lores")) { + loresOnly = true; + hiresOnly = false; + } else if (dlRequest.hasOption("mo", "hires")) { + loresOnly = false; + hiresOnly = true; + } else if (dlRequest.hasOption("mo", "autores")) { + loresOnly = false; + hiresOnly = false; + } + // operation mode: "errtxt": error message in html, "errimg": error + // image + if (dlRequest.hasOption("mo", "errtxt")) { + errorMsgHtml = true; + } else if (dlRequest.hasOption("mo", "errimg")) { + errorMsgHtml = false; + } + // operation mode: "q0" - "q2": interpolation quality + if (dlRequest.hasOption("mo", "q0")) { + scaleQual = 0; + } else if (dlRequest.hasOption("mo", "q1")) { + scaleQual = 1; + } else if (dlRequest.hasOption("mo", "q2")) { + scaleQual = 2; + } + + // check with the maximum allowed size (if set) + int maxImgSize = dlConfig.getAsInt("max-image-size"); + if (maxImgSize > 0) { + paramDW = (paramDW * paramWS > maxImgSize) ? (int) (maxImgSize / paramWS) + : paramDW; + paramDH = (paramDH * paramWS > maxImgSize) ? (int) (maxImgSize / paramWS) + : paramDH; + } - // use authorization database - boolean useAuthentication = true; - // image file to send in case of error - File errorImgFile = new File("/docuserver/images/icons/scalerror.gif"); - // image file to send if access is denied - File denyImgFile = new File("/docuserver/images/icons/denied.gif"); - // base directories in order of preference (prescaled versions first) - String[] baseDirs = {"/docuserver/scaled/small", "/docuserver/images", "/docuserver/scans/quellen"}; + //"big" try for all file/image actions + try { + + // ImageFileset of the image to load + ImageFileset fileset = null; + + /* find the file to load/send */ + + // get PathInfo + String loadPathName = dlRequest.getFilePath(); + + /* check permissions */ + if (useAuthorization) { + // get a list of required roles (empty if no restrictions) + List rolesRequired = authOp.rolesForPath(loadPathName, request); + if (rolesRequired != null) { + authlog.debug("Role required: " + rolesRequired); + authlog.debug("User: " + request.getRemoteUser()); + // is the current request/user authorized? + if (!authOp.isRoleAuthorized(rolesRequired, request)) { + // send deny answer and abort + throw new AuthOpException(); + } + } + } + + // find the file(set) + ImageFile fileToLoad; + fileset = (ImageFileset) dirCache.getFile(loadPathName, dlRequest + .getAsInt("pn"), FileOps.CLASS_IMAGE); + if (fileset == null) { + throw new FileOpException("File " + loadPathName + "(" + + dlRequest.getAsInt("pn") + ") not found."); + } + + /* calculate expected source image size */ + ImageSize expectedSourceSize = new ImageSize(); + if (scaleToFit) { + float scale = (1 / Math.min(paramWW, paramWH)) * paramWS; + expectedSourceSize.setSize((int) (paramDW * scale), + (int) (paramDH * scale)); + } else { + expectedSourceSize.setSize((int) (paramDW * paramWS), + (int) (paramDH * paramWS)); + } + + /* select a resolution */ + if (hiresOnly) { + // get first element (= highest resolution) + fileToLoad = fileset.getBiggest(); + } else if (loresOnly) { + // enforced lores uses next smaller resolution + fileToLoad = fileset.getNextSmaller(expectedSourceSize); + if (fileToLoad == null) { + // this is the smallest we have + fileToLoad = fileset.getSmallest(); + } + } else { + // autores: use next higher resolution + fileToLoad = fileset.getNextBigger(expectedSourceSize); + if (fileToLoad == null) { + // this is the highest we have + fileToLoad = fileset.getBiggest(); + } + } + logger.info("Loading: " + fileToLoad.getFile()); + + /* + * send the image if its mo=(raw)file + */ + if (dlRequest.hasOption("mo", "file") + || dlRequest.hasOption("mo", "rawfile")) { + if (sendFileAllowed) { + String mt = null; + if (dlRequest.hasOption("mo", "rawfile")) { + mt = "application/octet-stream"; + } + logger.debug("Sending RAW File as is."); + ServletOps.sendFile(fileToLoad.getFile(), mt, response, + fastQueue); + logger.info("Done in " + + (System.currentTimeMillis() - startTime) + "ms"); + return; + } + } + + /* + * prepare resolution for original size + */ + if (absoluteScale) { + // get original resolution from metadata + fileset.checkMeta(); + origResX = fileset.getResX(); + origResY = fileset.getResY(); + if ((origResX == 0) || (origResY == 0)) { + throw new ImageOpException("Missing image DPI information!"); + } + + if ((paramDDPIX == 0) || (paramDDPIY == 0)) { + throw new ImageOpException( + "Missing display DPI information!"); + } + } + + // check the source image + if (!fileToLoad.isChecked()) { + ImageOps.checkFile(fileToLoad); + } + // get the source image type + mimeType = fileToLoad.getMimetype(); + // get the source image size + ImageSize imgSize = fileToLoad.getSize(); + + // decide if the image can be sent as is + boolean mimetypeSendable = mimeType.equals("image/jpeg") + || mimeType.equals("image/png") + || mimeType.equals("image/gif"); + boolean imagoOptions = dlRequest.hasOption("mo", "hmir") + || dlRequest.hasOption("mo", "vmir") || (paramROT != 0) + || (paramRGBM != null) || (paramRGBA != null) + || (paramCONT != 0) || (paramBRGT != 0); + boolean imageSendable = mimetypeSendable && !imagoOptions; + + /* + * if not autoRes and image smaller than requested size then send as + * is. if autoRes and image has requested size then send as is. if + * not autoScale and not scaleToFit nor cropToFit then send as is + * (mo=file) + */ + if (imageSendable + && ((loresOnly && fileToLoad.getSize().isSmallerThan( + expectedSourceSize)) || (!(loresOnly || hiresOnly) && fileToLoad + .getSize().fitsIn(expectedSourceSize)))) { + + logger.debug("Sending File as is."); + + ServletOps.sendFile(fileToLoad.getFile(), null, response, + fastQueue); + + logger.info("Done in " + + (System.currentTimeMillis() - startTime) + "ms"); + return; + } + + // set missing dw or dh from aspect ratio + float imgAspect = fileToLoad.getAspect(); + if (paramDW == 0) { + paramDW = (int) Math.round(paramDH * imgAspect); + } else if (paramDH == 0) { + paramDH = (int) Math.round(paramDW / imgAspect); + } + + /* crop and scale the image */ + + logger.debug("IMG: " + imgSize.getWidth() + "x" + + imgSize.getHeight()); + logger.debug("time " + (System.currentTimeMillis() - startTime) + + "ms"); + + // coordinates and scaling + float areaXoff; + float areaYoff; + float areaWidth; + float areaHeight; + float scaleX; + float scaleY; + float scaleXY; + + // coordinates using Java2D + // image size in pixels + Rectangle2D imgBounds = new Rectangle2D.Float(0, 0, imgSize + .getWidth(), imgSize.getHeight()); + // user window area in [0,1] coordinates + Rectangle2D relUserArea = new Rectangle2D.Float(paramWX, paramWY, + paramWW, paramWH); + // transform from relative [0,1] to image coordinates. + AffineTransform imgTrafo = AffineTransform.getScaleInstance(imgSize + .getWidth(), imgSize.getHeight()); + // transform user coordinate area to image coordinate area + Rectangle2D userImgArea = imgTrafo.createTransformedShape( + relUserArea).getBounds2D(); + + // calculate scaling factors based on inner user area + if (scaleToFit) { + areaWidth = (float) userImgArea.getWidth(); + areaHeight = (float) userImgArea.getHeight(); + scaleX = paramDW / areaWidth * paramWS; + scaleY = paramDH / areaHeight * paramWS; + scaleXY = (scaleX > scaleY) ? scaleY : scaleX; + } else if (absoluteScale) { + // absolute scale + scaleX = paramDDPIX / origResX; + scaleY = paramDDPIY / origResY; + // currently only same scale :-( + scaleXY = scaleX; + areaWidth = paramDW / scaleXY * paramWS; + areaHeight = paramDH / scaleXY * paramWS; + // reset user area size + userImgArea.setRect(userImgArea.getX(), userImgArea.getY(), + areaWidth, areaHeight); + } else { + // crop to fit + areaWidth = paramDW * paramWS; + areaHeight = paramDH * paramWS; + // reset user area size + userImgArea.setRect(userImgArea.getX(), userImgArea.getY(), + areaWidth, areaHeight); + scaleX = 1f; + scaleY = 1f; + scaleXY = 1f; + } + + // enlarge image area for rotations to cover additional pixels + Rectangle2D outerUserImgArea = userImgArea; + Rectangle2D innerUserImgArea = userImgArea; + if (wholeRotArea) { + if (paramROT != 0) { + try { + // rotate user area coordinates around center of user + // area + AffineTransform rotTrafo = AffineTransform + .getRotateInstance(Math.toRadians(paramROT), + userImgArea.getCenterX(), userImgArea + .getCenterY()); + // get bounds from rotated end position + innerUserImgArea = rotTrafo.createTransformedShape( + userImgArea).getBounds2D(); + // get bounds from back-rotated bounds + outerUserImgArea = rotTrafo.createInverse() + .createTransformedShape(innerUserImgArea) + .getBounds2D(); + } catch (NoninvertibleTransformException e1) { + // this shouldn't happen anyway + logger.error(e1); + } + } + } + + logger.debug("Scale " + scaleXY + "(" + scaleX + "," + scaleY + + ") on " + outerUserImgArea); + + // clip area at the image border + outerUserImgArea = outerUserImgArea.createIntersection(imgBounds); + + // check image parameters sanity + if ((outerUserImgArea.getWidth() < 1) || (outerUserImgArea.getHeight() < 1) + || (scaleXY * outerUserImgArea.getWidth() < 2) || (scaleXY * outerUserImgArea.getHeight() < 2)) { + logger.error("ERROR: invalid scale parameter set!"); + throw new ImageOpException("Invalid scale parameter set!"); + } + + /* + * submit the image worker job + */ + + DigilibWorker job = new DigilibImageWorker(dlConfig, response, + mimeType, scaleQual, dlRequest, paramROT, paramCONT, + paramBRGT, paramRGBM, paramRGBA, fileToLoad, scaleXY, outerUserImgArea, + innerUserImgArea, minSubsample, wholeRotArea); + + try { + // we're cheating + job.run(); + } catch (Exception e1) { + throw new ImageOpException(e1.toString()); + } + + /* + try { + slowQueue.execute(job); + logger.debug("servlet job submitted by " + + Thread.currentThread().getName() + " (" + + slowQueue.getQueueSize() + " in queue)"); + + synchronized (job) { + while (job.isBusy()) { + Thread.yield(); + job.wait(); + } + } + } catch (InterruptedException e1) { + throw new ImageOpException("INTERRUPTED: " + e1.getMessage()); + } + */ - /********************************************************* - * Initialize global variables - *********************************************************/ - public void init(ServletConfig config) throws ServletException { - super.init(config); - - // first we need an Utils to setup ServletOps UGLY!! - util = new Utils(5); - // servletOps takes a ServletConfig to get the config file name - servletOp = new ServletOps(util, config); - // then we can start reading parameters UGLY!! - - // Utils instance with debuglevel - int debugLevel = servletOp.tryToGetInitParam("debug-level", 10); - util = new Utils(debugLevel); - // reset Util for ServletOps instance - servletOp.setUtils(util); - // image file to send in case of error - String errorImgFileName = servletOp.tryToGetInitParam("error-image", "/docuserver/images/icons/scalerror.gif"); - errorImgFile = new File(errorImgFileName); - // image file to send if access is denied - String denyImgFileName = servletOp.tryToGetInitParam("denied-image", "/docuserver/images/icons/denied.gif"); - denyImgFile = new File(denyImgFileName); - // base directories in order of preference (prescaled versions first) - String baseDirList = servletOp.tryToGetInitParam("basedir-list", "/docuserver/scaled/small:/docuserver/images:/docuserver/scans/quellen"); - // split list into directories - StringTokenizer dirs = new StringTokenizer(baseDirList, ":"); - int n = dirs.countTokens(); - // add directories into array - baseDirs = new String[n]; - for (int i = 0; i < n; i++) { - baseDirs[i] = dirs.nextToken(); - } - // use authentication information - String useAuth = servletOp.tryToGetInitParam("use-authorization", "true"); - if ((useAuth.indexOf("false") > 0)||(useAuth.indexOf("FALSE") > 0)) { - useAuthentication = false; - } else { - useAuthentication = true; - try { - // DB version - //authOp = new DBAuthOpsImpl(util); - // XML version - String cnfPath = servletOp.tryToGetInitParam("auth-file", "/docuserver/www/digitallibrary/WEB-INF/digilib-auth.xml"); - authOp = new XMLAuthOps(util, cnfPath); - } catch (AuthOpException e) { - throw new ServletException(e); - } - } - // FileOps instance - fileOp = new FileOps(util); - // global DocuImage instance (don't reuse inside a request!) - globalImage = new JAIDocuImage(util); -// globalImage = new JIMIDocuImage(util); - //globalImage = new ImageLoaderDocuImage(util); - - } - - /**Process the HTTP Get request*/ - public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - util.dprintln(1, "The servlet has received a GET!"); - processRequest(request, response); - } - - /**Process the HTTP Post request*/ - public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - util.dprintln(1, "The servlet has received a POST!"); - processRequest(request, response); - } - - /**Clean up resources*/ - public void destroy() { - } - -/********************************************************************** - * main request handler - **********************************************************************/ + logger.debug("servlet done in " + + (System.currentTimeMillis() - startTime)); - void processRequest(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { - - // time for benchmarking - long startTime = System.currentTimeMillis(); - // output mime/type - String mimeType = "image/png"; - - /** - * parameters for a session - */ - - // scale the image file to fit window size - boolean scaleToFit = true; - // use heuristics (GIF?) to scale or not - boolean forcedScale = false; - // try prescaled images first - boolean preScaledFirst = true; - // interpolation to use for scaling - int scaleQual = 0; - // send html error message (or image file) - boolean errorMsgHtml = false; - - /** - * request parameter - */ - - // file/dir to load - String param_fn = servletOp.tryToGetParam("fn", "", request); - // page number - int param_pn = servletOp.tryToGetParam("pn", 1, request); - // destination image width - int param_dw = servletOp.tryToGetParam("dw", 300, request); - // destination image height - int param_dh = servletOp.tryToGetParam("dh", 400, request); - // relative area x_offset (0..1) - float param_wx = servletOp.tryToGetParam("wx", 0f, request); - // relative area y_offset - float param_wy = servletOp.tryToGetParam("wy", 0f, request); - // relative area width (0..1) - float param_ww = servletOp.tryToGetParam("ww", 1f, request); - // relative area height - float param_wh = servletOp.tryToGetParam("wh", 1f, request); - // scale factor (additional to dw/width, dh/height) - float param_ws = servletOp.tryToGetParam("ws", 1f, request); - // operation mode: flags separated by "+" - String param_mo = servletOp.tryToGetParam("mo", "", request); - // operation mode: "fit": always fit to page, "file": send as-is - if (param_mo.indexOf("fit") >= 0) { - scaleToFit = true; - forcedScale = true; - } else if (param_mo.indexOf("file") >= 0) { - scaleToFit = false; - forcedScale = true; - } - // operation mode: "errtxt": error message in html, "errimg": error image - if (param_mo.indexOf("errtxt") >= 0) { - errorMsgHtml = true; - } else if (param_mo.indexOf("errimg") >= 0) { - errorMsgHtml = false; - } - // operation mode: "q0" - "q2": interpolation quality - if (param_mo.indexOf("q0") >= 0) { - scaleQual = 0; - } else if (param_mo.indexOf("q1") >= 0) { - scaleQual = 1; - } else if (param_mo.indexOf("q2") >= 0) { - scaleQual = 2; - } - // operation mode: "lores": try to use scaled image, "hires": unscaled image - if (param_mo.indexOf("lores") >= 0) { - preScaledFirst = true; - } else if (param_mo.indexOf("hires") >= 0) { - preScaledFirst = false; - } - - Utils.dprintln(1, "Parameter values: fn:"+param_fn+" pn:"+param_pn+" dw:"+param_dw+" dh:"+param_dh+" wx:"+param_wx+" wy:"+param_wy+" ww:"+param_ww+" wh:"+param_wh+" ws:"+param_ws+" mo:"+param_mo); - - //"big" try for all file/image actions - try { + /* error handling */ - // DocuImage instance - DocuImage docuImage = new JAIDocuImage(util); -// DocuImage docuImage = new JIMIDocuImage(util); - //DocuImage docuImage = new ImageLoaderDocuImage(util); - - - /** - * find the file to load/send - */ - - String loadPathName = ""; - // if there's PathInfo, append - if (request.getPathInfo() != null) { - loadPathName += request.getPathInfo(); - } - // append fn parameter - loadPathName += param_fn; - // if it's zoomed, try hires version (to be optimized...) - if ((param_ww < 1f) || (param_wh < 1f)) { - preScaledFirst = false; - } - - if (useAuthentication) { - // check permissions - List rolesRequired = authOp.rolesForPath(loadPathName, request); - if (rolesRequired != null) { - Utils.dprintln(1, "Role required: "+rolesRequired); - Utils.dprintln(2, "User: "+request.getRemoteUser()); - if (! authOp.isRoleAuthorized(rolesRequired, request)) { - Utils.dprintln(1, "ERROR: access denied!"); - if (errorMsgHtml) { - servletOp.htmlMessage("ERROR: Unauthorized access!", response); - } else { - docuImage.sendFile(denyImgFile, response); - } - return; - } - } - } - - // find the file - File fileToLoad = fileOp.getFileVariant(baseDirs, loadPathName, param_pn, preScaledFirst); - - Utils.dprintln(1, "Loading: "+fileToLoad); - - // get the source image type (if it's known) - mimeType = fileOp.mimeForFile(fileToLoad); - - // if not forced and source is GIF/PNG then send-as-is if not zoomed - if((!forcedScale && (mimeType == "image/gif" || mimeType == "image/png") - && (param_ww == 1f) && (param_wh == 1f)) || (forcedScale && !scaleToFit)) { - - Utils.dprintln(1, "Sending File as is."); - - docuImage.sendFile(fileToLoad, response); - - Utils.dprintln(1, "Done in "+(System.currentTimeMillis()-startTime)+"ms"); - return; - } - - // load file - docuImage.loadImage(fileToLoad); - - /** - * crop and scale the image - */ - - // get size - int imgWidth = docuImage.getWidth(); - int imgHeight = docuImage.getHeight(); - - util.dprintln(2, "IMG: "+imgWidth+"x"+imgHeight); - util.dprintln(2, "time "+(System.currentTimeMillis()-startTime)+"ms"); + } // end of "big" try + catch (IOException e) { + logger.error("ERROR: File IO Error: " + e); + digilibError(errorMsgHtml, ERROR_FILE, + "ERROR: File IO Error: " + e, response); + } catch (AuthOpException e) { + logger.error("ERROR: Authorization error: " + e); + digilibError(errorMsgHtml, ERROR_AUTH, + "ERROR: Authorization error: " + e, response); + } catch (ImageOpException e) { + logger.error("ERROR: Image Error: " + e); + digilibError(errorMsgHtml, ERROR_IMAGE, + "ERROR: Image Operation Error: " + e, response); + } catch (RuntimeException e) { + // JAI likes to throw RuntimeExceptions ;-( + logger.error("ERROR: Other Image Error: " + e); + digilibError(errorMsgHtml, ERROR_IMAGE, + "ERROR: Other Image Operation Error: " + e, response); + } + } + /** + * Sends an error to the client as text or image. + * + * @param asHTML + * @param type + * @param msg + * @param response + */ + public void digilibError(boolean asHTML, int type, String msg, + HttpServletResponse response) { + try { + File img = null; + if (type == ERROR_AUTH) { + if (msg == null) { + msg = "ERROR: Unauthorized access!"; + } + img = denyImgFile; + } else { + if (msg == null) { + msg = "ERROR: Other image error!"; + } + img = this.errorImgFile; + } + if (asHTML && (img != null)) { + ServletOps.htmlMessage(msg, response); + } else { + ServletOps.sendFile(img, null, response, fastQueue); + } + } catch (IOException e) { + logger.error("Error sending error!", e); + } - // calculate absolute from relative coordinates - float areaXoff = param_wx * imgWidth; - float areaYoff = param_wy * imgHeight; - float areaWidth = param_ww * imgWidth; - float areaHeight = param_wh * imgHeight; - // calculate scaling factors - float scaleX = param_dw / areaWidth * param_ws; - float scaleY = param_dh / areaHeight * param_ws; - float scaleXY = (scaleX > scaleY) ? scaleY : scaleX; - - util.dprintln(1, "Scale "+scaleXY+"("+scaleX+","+scaleY+") on "+areaXoff+","+areaYoff+" "+areaWidth+"x"+areaHeight); - - // fit area to image - areaWidth = (areaXoff + areaWidth > imgWidth) ? imgWidth - areaXoff : areaWidth; - areaHeight = (areaYoff + areaHeight > imgHeight) ? imgHeight - areaYoff : areaHeight; - - util.dprintln(2, "cropped: "+areaXoff+","+areaYoff+" "+areaWidth+"x"+areaHeight); - - // check image parameters - if ((areaWidth < 1)||(areaHeight < 1) - ||(scaleXY * areaWidth < 2)||(scaleXY * areaHeight < 2)) { - Utils.dprintln(1, "ERROR: invalid scale parameter set!"); - throw new ImageOpException("Invalid scale parameter set!"); - } - - // crop and scale image - docuImage.cropAndScale((int)areaXoff, (int)areaYoff, (int)areaWidth, (int)areaHeight, - scaleXY, scaleQual); - - util.dprintln(2, "time "+(System.currentTimeMillis()-startTime)+"ms"); - - /** - * write the resulting image - */ - - // setup output -- if source is JPG then dest will be JPG else it's PNG - if (mimeType != "image/jpeg") { - mimeType="image/png"; - } - - // write the image - docuImage.writeImage(mimeType, response); + } - util.dprintln(1, "Done in "+(System.currentTimeMillis()-startTime)+"ms"); - - /** - * error handling - */ - - }//"big" try - catch (FileOpException e) { - util.dprintln(1, "ERROR: File IO Error: "+e); - try { - if (errorMsgHtml) { - servletOp.htmlMessage("ERROR: File IO Error: "+e, response); - } else { - globalImage.sendFile(errorImgFile, response); - } - } catch (FileOpException ex) {} // so we don't get a loop - return; - } - catch (AuthOpException e) { - Utils.dprintln(1, "ERROR: Authorization error: "+e); - try { - if (errorMsgHtml) { - servletOp.htmlMessage("ERROR: Authorization error: "+e, response); - } else { - globalImage.sendFile(errorImgFile, response); - } - } catch (FileOpException ex) {} // so we don't get a loop - return; - } - catch (ImageOpException e) { - Utils.dprintln(1, "ERROR: Image Error: "+e); - try { - if (errorMsgHtml) { - servletOp.htmlMessage("ERROR: Image Operation Error: "+e, response); - } else { - globalImage.sendFile(errorImgFile, response); - } - } catch (FileOpException ex) {} // so we don't get a loop - return; - } - - } - -}//Scaler class +} //Scaler class diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/ServletOps.java --- a/servlet/src/digilib/servlet/ServletOps.java Mon Oct 18 15:40:54 2004 +0200 +++ b/servlet/src/digilib/servlet/ServletOps.java Thu Oct 21 20:53:37 2004 +0200 @@ -1,156 +1,217 @@ -/* ServletOps -- Servlet utility class - - Digital Image Library servlet components - - Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2 of the License, or (at your - option) any later version. - - Please read license.txt for the full details. A copy of the GPL - may be found at http://www.gnu.org/copyleft/lgpl.html - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ +/* + * ServletOps -- Servlet utility class + * + * Digital Image Library servlet components + * + * Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Please read license.txt for the full details. A copy of the GPL may be found + * at http://www.gnu.org/copyleft/lgpl.html + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + */ package digilib.servlet; -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.*; -import java.util.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; -import digilib.*; -import digilib.io.*; +import javax.servlet.ServletConfig; +import javax.servlet.http.HttpServletResponse; +import org.apache.log4j.Logger; + +import EDU.oswego.cs.dl.util.concurrent.Executor; +import digilib.io.FileOpException; +import digilib.io.FileOps; public class ServletOps { - private Utils util = null; - private Hashtable confTable = null; - - public ServletOps() { - util = new Utils(); - } + private static Logger logger = Logger.getLogger("servlet.op"); - public ServletOps(Utils u) { - util = u; - } + /** + * convert a string with a list of pathnames into an array of strings using + * the system's path seperator string + */ + public static String[] getPathArray(String paths) { + // split list into directories + StringTokenizer dirs = new StringTokenizer(paths, + java.io.File.pathSeparator); + int n = dirs.countTokens(); + if (n < 1) { + return null; + } + // add directories into array + String[] pathArray = new String[n]; + for (int i = 0; i < n; i++) { + pathArray[i] = dirs.nextToken(); + } + return pathArray; + } - public ServletOps(Utils u, ServletConfig sc) throws ServletException { - util = u; - setConfig(sc); - } - - public void setUtils(Utils u) { - util = u; - } + /** + * get a real File for a config File. + * + * If the File is not absolute the path is appended to the WEB-INF directory + * of the web-app. + * + * @param file + * @param sc + * @return + */ + public static File getConfigFile(File f, ServletConfig sc) { + // is the filename absolute? + if (!f.isAbsolute()) { + // relative path -> use getRealPath to resolve in WEB-INF + String fn = sc.getServletContext().getRealPath( + "WEB-INF/" + f.getPath()); + f = new File(fn); + } + return f; + } - /** - * read parameter list from the XML file in init parameter "config-file" - */ - public void setConfig(ServletConfig c) throws ServletException { - // reset parameter table - confTable = null; - if (c == null) { - return; - } - // get config file name - String fn = c.getInitParameter("config-file"); - if (fn == null) { - util.dprintln(4, "setConfig: no param config-file"); - return; - } - File f = new File(fn); - // setup config file list reader - XMLListLoader lilo = new XMLListLoader("digilib-config", "parameter", "name", "value"); - try { - confTable = lilo.loadURL(f.toURL().toString()); - } catch (Exception e) { - util.dprintln(4, "setConfig: unable to read file "+fn); - throw new ServletException(e); - } - } + /** + * get a real file name for a config file pathname. + * + * If filename starts with "/" its treated as absolute else the path is + * appended to the WEB-INF directory of the web-app. + * + * @param filename + * @param sc + * @return + */ + public static String getConfigFile(String filename, ServletConfig sc) { + File f = new File(filename); + // is the filename absolute? + if (!f.isAbsolute()) { + // relative path -> use getRealPath to resolve in WEB-INF + filename = sc.getServletContext() + .getRealPath("WEB-INF/" + filename); + } + return filename; + } - /** - * print a servlet response and exit - */ - public static void htmlMessage(String s, HttpServletResponse response) throws IOException { - response.setContentType("text/html; charset=iso-8859-1"); - PrintWriter out = response.getWriter(); - out.println(""); - out.println("Scaler"); - out.println(""); - out.println("

"+s+"

"); - out.println(""); - } + /** + * print a servlet response and exit + */ + public static void htmlMessage(String msg, HttpServletResponse response) + throws IOException { + htmlMessage("Scaler", msg, response); + } + + /** + * print a servlet response and exit + */ + public static void htmlMessage(String title, String msg, + HttpServletResponse response) throws IOException { + response.setContentType("text/html; charset=iso-8859-1"); + PrintWriter out = response.getWriter(); + out.println(""); + out.println("" + title + ""); + out.println(""); + out.println("

" + msg + "

"); + out.println(""); + } - /** - * get a parameter from request and return it if set, otherwise return default - */ - public int tryToGetParam(String s, int i, HttpServletRequest r) { - try { - i = Integer.parseInt(r.getParameter(s)); - } catch(Exception e) { - util.dprintln(4, "trytoGetParam(int) failed on param "+s); - //e.printStackTrace(); - } - return i; - } - public float tryToGetParam(String s, float f, HttpServletRequest r) { - try { - f = Float.parseFloat(r.getParameter(s)); - } catch(Exception e) { - util.dprintln(4, "trytoGetParam(float) failed on param "+s); - //e.printStackTrace(); - } - return f; - } - public String tryToGetParam(String s, String x, HttpServletRequest r) { - if (r.getParameter(s) != null) { - x = r.getParameter(s); - } else { - util.dprintln(4, "trytoGetParam(string) failed on param "+s); - } - return x; - } - + /** + * Transfers an image file as-is with the mime type mt. + * + * The local file is copied to the OutputStream of the + * ServletResponse. If mt is null then the mime-type is + * auto-detected with mimeForFile. + * + * @param mt + * mime-type of the file. + * @param f + * Image file to be sent. + * @param res + * ServletResponse where the image file will be sent. + * @throws FileOpException + * Exception is thrown for a IOException. + */ + public static void sendFileImmediately(File f, String mt, + HttpServletResponse response) throws FileOpException { + logger.debug("sendRawFile(" + mt + ", " + f + ")"); + if (mt == null) { + // auto-detect mime-type + mt = FileOps.mimeForFile(f); + if (mt == null) { + throw new FileOpException("Unknown file type."); + } + } + response.setContentType(mt); + // open file + try { + if (mt.equals("application/octet-stream")) { + response.addHeader("Content-Disposition", + "attachment; filename=\"" + f.getName() + "\""); + } + FileInputStream inFile = new FileInputStream(f); + OutputStream outStream = response.getOutputStream(); + byte dataBuffer[] = new byte[4096]; + int len; + while ((len = inFile.read(dataBuffer)) != -1) { + // copy out file + outStream.write(dataBuffer, 0, len); + } + inFile.close(); + response.flushBuffer(); + } catch (IOException e) { + throw new FileOpException("Unable to send file."); + } + } - /** - * get an init parameter from config and return it if set, otherwise return default - */ - public int tryToGetInitParam(String s, int i) { - //System.out.println("trytogetInitParam("+s+", "+i+")"); - try { - //System.out.println("trytogetInitParam: "+(String)confTable.get(s)); - i = Integer.parseInt((String)confTable.get(s)); - } catch(Exception e) { - util.dprintln(4, "trytogetInitParam(int) failed on param "+s); - //e.printStackTrace(); - } - return i; - } - public float tryToGetInitParam(String s, float f) { - try { - f = Float.parseFloat((String)confTable.get(s)); - } catch(Exception e) { - util.dprintln(4, "trytoGetInitParam(float) failed on param "+s); - //e.printStackTrace(); - } - return f; - } - public String tryToGetInitParam(String s, String x) { - if ((confTable != null)&&((String)confTable.get(s) != null)) { - x = (String)confTable.get(s); - } else { - util.dprintln(4, "trytoGetInitParam(string) failed on param "+s); - } - return x; - } + /** + * Transfers an image file as-is with the mime type mt using a work queue. + * + * The local file is copied to the OutputStream of the + * ServletResponse. If mt is null then the mime-type is + * auto-detected with mimeForFile. + * + * @param mt + * mime-type of the file. + * @param f + * Image file to be sent. + * @param res + * ServletResponse where the image file will be sent. + * @throws FileOpException + * Exception is thrown for a IOException. + */ + public static void sendFile(File f, String mimetype, + HttpServletResponse response, Executor workQueue) + throws FileOpException { + // we're cheating + sendFileImmediately(f, mimetype, response); + /* + // create worker + DigilibSender job = new DigilibSender(f, null, response); + try { + logger.debug("queue size: " + + ((DigilibManager) workQueue).getQueueSize()); + workQueue.execute(job); + logger.debug("job sent!"); + synchronized (job) { + while (job.isBusy()) { + job.wait(); + } + } + } catch (InterruptedException e) { + throw new FileOpException("INTERRUPTED: Unable to send file. " + e); + } + */ -} + } + +} \ No newline at end of file diff -r 9f7b864f955f -r 5d0c0da080ec servlet/src/digilib/servlet/Texter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/servlet/src/digilib/servlet/Texter.java Thu Oct 21 20:53:37 2004 +0200 @@ -0,0 +1,189 @@ +/* Texter.java -- Servlet for displaying text + * Digital Image Library servlet components + * Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. Please read license.txt for the full details. A copy of + * the GPL may be found at http://www.gnu.org/copyleft/lgpl.html + * You should have received a copy of the GNU General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA + * + * Created on 15.09.2003 by casties + */ + +package digilib.servlet; + +import java.io.IOException; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; + +import digilib.auth.AuthOps; +import digilib.io.DocuDirCache; +import digilib.io.FileOpException; +import digilib.io.FileOps; +import digilib.io.TextFile; + +/** + * Servlet for displaying text + * + * + * @author casties + * + */ +public class Texter extends HttpServlet { + + private static final long serialVersionUID = -8539178734033662322L; + + /** Servlet version */ + public static String tlVersion = "0.1b1"; + + /** DigilibConfiguration instance */ + DigilibConfiguration dlConfig = null; + + /** general logger */ + Logger logger = Logger.getLogger("digilib.texter"); + + /** FileOps instance */ + FileOps fileOp; + + /** AuthOps instance */ + AuthOps authOp; + + /** ServletOps instance */ + ServletOps servletOp; + + /** DocuDirCache instance */ + DocuDirCache dirCache; + + /** use authentication */ + boolean useAuthentication = false; + + /* + * (non-Javadoc) + * + * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) + */ + public void init(ServletConfig config) throws ServletException { + super.init(config); + + System.out.println("***** Digital Image Library Text Servlet (version " + + tlVersion + ") *****"); + + // get our ServletContext + ServletContext context = config.getServletContext(); + // see if there is a Configuration instance + dlConfig = (DigilibConfiguration) context + .getAttribute("digilib.servlet.configuration"); + if (dlConfig == null) { + // no Configuration + throw new ServletException("No Configuration!"); + } + // say hello in the log file + logger.info("***** Digital Image Library Text Servlet (version " + + tlVersion + ") *****"); + + // set our AuthOps + useAuthentication = dlConfig.getAsBoolean("use-authorization"); + authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); + // DocuDirCache instance + dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); + } + + /* + * (non-Javadoc) + * + * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + protected void doGet(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException { + // create new request with defaults + DigilibRequest dlReq = new DigilibRequest(); + // set with request parameters + dlReq.setWithRequest(request); + // add DigilibRequest to ServletRequest + request.setAttribute("digilib.servlet.request", dlReq); + // do the processing + processRequest(request, response); + } + + /* + * (non-Javadoc) + * + * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, + * javax.servlet.http.HttpServletResponse) + */ + protected void doPost(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException { + // create new request with defaults + DigilibRequest dlReq = new DigilibRequest(); + // set with request parameters + dlReq.setWithRequest(request); + // add DigilibRequest to ServletRequest + request.setAttribute("digilib.servlet.request", dlReq); + // do the processing + processRequest(request, response); + } + +protected void processRequest(HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException { + + /* + * request parameters + */ + DigilibRequest dlRequest = (DigilibRequest) request.getAttribute("digilib.servlet.request"); + try { + + /* + * find the file to load/send + */ + TextFile f = getTextFile(dlRequest, "/txt"); + if (f != null) { + ServletOps.sendFileImmediately(f.getFile(), null, response); + } else { + f = getTextFile(dlRequest, ""); + if (f != null) { + ServletOps.sendFileImmediately(f.getFile(), null, response); + } else { + ServletOps.htmlMessage("No Text-File!", response); + } + } + + } catch (FileOpException e) { + logger.error("ERROR: File IO Error: ", e); + try { + ServletOps.htmlMessage("ERROR: File IO Error: " + e, response); + } catch (FileOpException ex) { + } // so we don't get a loop + } + } + + + /** + * Looks for a file in the given subDirectory. + * + * @param dlRequest + * The received request which has the file path. + * @param subDirectory + * The subDirectory of the file path where the file should + * be found. + * @return The wanted Textfile or null if there wasn't a file. + */ + + private TextFile getTextFile(DigilibRequest dlRequest, String subDirectory) { + String loadPathName = dlRequest.getFilePath() + subDirectory; + // find the file(set) + return (TextFile) dirCache.getFile(loadPathName, dlRequest + .getAsInt("pn"), FileOps.CLASS_TEXT); + } +} \ No newline at end of file