Mercurial > hg > digilib-old
diff servlet/src/digilib/servlet/Scaler.java @ 290:5d0c0da080ec gen2 scaleable_1
digilib servlet version 2 ("scaleable digilib")
- first stab at using thread pools to limit resource use
- using Dug Leas util.concurrent
- doesn't mix with tomcat :-(
author | robcast |
---|---|
date | Thu, 21 Oct 2004 20:53:37 +0200 |
parents | 0ff3ede32060 |
children | 1cec876f2788 |
line wrap: on
line diff
--- 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