Mercurial > hg > digilib-old
changeset 925:66f1ba72d07b
added timeout-parameter and timeout-handler to AsyncServletWorker.
added stopNow abort method to ImageWorker.
author | robcast |
---|---|
date | Mon, 19 Dec 2011 21:39:17 +0100 |
parents | 6853c02b238b |
children | 2c70e595e4d7 |
files | .hgignore common/src/main/java/digilib/image/ImageWorker.java servlet3/src/main/java/digilib/servlet/AsyncServletWorker.java servlet3/src/main/java/digilib/servlet/DigilibServletConfiguration.java servlet3/src/main/java/digilib/servlet/Initialiser.java servlet3/src/main/java/digilib/servlet/Scaler.java webapp/src/main/webapp/WEB-INF/digilib-config.xml |
diffstat | 7 files changed, 180 insertions(+), 44 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Fri Dec 16 14:34:04 2011 +0100 +++ b/.hgignore Mon Dec 19 21:39:17 2011 +0100 @@ -61,4 +61,8 @@ syntax: regexp ^digilib-log\.txt$ syntax: regexp -^dl-access-log\.txt$ \ No newline at end of file +^dl-access-log\.txt$ +syntax: regexp +^webapp/digilib-log\.txt$ +syntax: regexp +^webapp/dl-access-log\.txt$ \ No newline at end of file
--- a/common/src/main/java/digilib/image/ImageWorker.java Fri Dec 16 14:34:04 2011 +0100 +++ b/common/src/main/java/digilib/image/ImageWorker.java Mon Dec 19 21:39:17 2011 +0100 @@ -12,10 +12,11 @@ import digilib.io.FileOpException; import digilib.servlet.DigilibConfiguration; -/** Worker that renders an image. +/** + * Worker that renders an image. * * @author casties - * + * */ public class ImageWorker implements Callable<DocuImage> { @@ -23,7 +24,11 @@ private DigilibConfiguration dlConfig; private ImageJobDescription jobinfo; - public ImageWorker(DigilibConfiguration dlConfig, ImageJobDescription jobinfo) { + /** flag for stopping the thread */ + private boolean stopNow = false; + + public ImageWorker(DigilibConfiguration dlConfig, + ImageJobDescription jobinfo) { super(); this.dlConfig = dlConfig; this.jobinfo = jobinfo; @@ -32,10 +37,15 @@ /** * render and return the image */ - public DocuImage call() throws FileOpException, IOException, ImageOpException { - - logger.debug("image worker starting"); + public DocuImage call() throws FileOpException, IOException, + ImageOpException { + + logger.debug("ImageWorker starting"); long startTime = System.currentTimeMillis(); + if (stopNow) { + logger.debug("ImageWorker stopping (at the start)"); + return null; + } DocuImage docuImage = jobinfo.getDocuImage(); if (docuImage == null) { @@ -50,7 +60,11 @@ Rectangle loadRect = jobinfo.getOuterUserImgArea().getBounds(); float scaleXY = jobinfo.getScaleXY(); - + + if (stopNow) { + logger.debug("ImageWorker stopping (after setup)"); + return null; + } // use subimage loading if possible if (docuImage.isSubimageSupported()) { logger.debug("Subimage: scale " + scaleXY + " = " + (1 / scaleXY)); @@ -60,7 +74,10 @@ subf = 1 / scaleXY; // for higher quality reduce subsample factor by minSubsample if (jobinfo.getScaleQual() > 0) { - subsamp = (float) Math.max(Math.floor(subf / dlConfig.getAsFloat("subsample-minimum")), 1d); + subsamp = (float) Math + .max(Math.floor(subf + / dlConfig.getAsFloat("subsample-minimum")), + 1d); } else { subsamp = (float) Math.floor(subf); } @@ -70,15 +87,31 @@ } docuImage.loadSubimage(jobinfo.getInput(), loadRect, (int) subsamp); logger.debug("SUBSAMP: " + subsamp + " -> " + docuImage.getSize()); + if (stopNow) { + logger.debug("ImageWorker stopping (after loading and cropping)"); + return null; + } docuImage.scale(scaleXY, scaleXY); } else { // else load and crop the whole file docuImage.loadImage(jobinfo.getInput()); + if (stopNow) { + logger.debug("ImageWorker stopping (after loading)"); + return null; + } docuImage.crop((int) loadRect.getX(), (int) loadRect.getY(), (int) loadRect.getWidth(), (int) loadRect.getHeight()); + if (stopNow) { + logger.debug("ImageWorker stopping (after cropping)"); + return null; + } docuImage.scale(scaleXY, scaleXY); } + if (stopNow) { + logger.debug("ImageWorker stopping (after scaling)"); + return null; + } // mirror image // operation mode: "hmir": mirror horizontally, "vmir": mirror // vertically @@ -89,28 +122,32 @@ docuImage.mirror(90); } + if (stopNow) { + logger.debug("ImageWorker stopping (after mirroring)"); + return null; + } // rotate image if (jobinfo.getAsFloat("rot") != 0d) { docuImage.rotate(jobinfo.getAsFloat("rot")); - /* if (jobinfo.get_wholeRotArea()) { - // crop to the inner bounding box - float xcrop = (float) (docuImage.getWidth() - jobinfo.get_innerUserImgArea().getWidth() - * scaleXY); - float ycrop = (float) (docuImage.getHeight() - jobinfo.get_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)); - } - } */ + /* + * if (jobinfo.get_wholeRotArea()) { // crop to the inner bounding + * box float xcrop = (float) (docuImage.getWidth() - + * jobinfo.get_innerUserImgArea().getWidth() scaleXY); float ycrop = + * (float) (docuImage.getHeight() - + * jobinfo.get_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)); } } + */ } + if (stopNow) { + logger.debug("ImageWorker stopping (after rotating)"); + return null; + } // color modification float[] paramRGBM = jobinfo.getRGBM(); float[] paramRGBA = jobinfo.getRGBA(); @@ -130,6 +167,10 @@ docuImage.enhanceRGB(mult, paramRGBA); } + if (stopNow) { + logger.debug("ImageWorker stopping (after enhanceRGB)"); + return null; + } // contrast and brightness enhancement float paramCONT = jobinfo.getAsFloat("cont"); float paramBRGT = jobinfo.getAsFloat("brgt"); @@ -138,15 +179,27 @@ docuImage.enhance(mult, paramBRGT); } + if (stopNow) { + logger.debug("ImageWorker stopping (after enhance)"); + return null; + } // color operation DocuImage.ColorOp colop = jobinfo.getColOp(); if (colop != null) { - docuImage.colorOp(colop); + docuImage.colorOp(colop); } - - logger.debug("rendered in " + (System.currentTimeMillis() - startTime) + "ms"); + + logger.debug("rendered in " + (System.currentTimeMillis() - startTime) + + "ms"); return docuImage; } + /** + * Set the stopNow flag. Thread stops at the next occasion. + */ + public void stopNow() { + this.stopNow = true; + } + }
--- a/servlet3/src/main/java/digilib/servlet/AsyncServletWorker.java Fri Dec 16 14:34:04 2011 +0100 +++ b/servlet3/src/main/java/digilib/servlet/AsyncServletWorker.java Mon Dec 19 21:39:17 2011 +0100 @@ -6,6 +6,8 @@ import java.io.IOException; import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +import javax.servlet.AsyncListener; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; @@ -22,7 +24,7 @@ * @author casties * */ -public class AsyncServletWorker implements Runnable { +public class AsyncServletWorker implements Runnable, AsyncListener { /** the AsyncServlet context */ private AsyncContext asyncContext; @@ -33,7 +35,11 @@ protected static Logger logger = Logger.getLogger(AsyncServletWorker.class); private long startTime; private ErrMsg errMsgType = ErrMsg.IMAGE; - private ImageJobDescription jobinfo; + private ImageJobDescription jobinfo; + /** flag to indicate that the response is completed (on abort)*/ + private boolean completed = false; + /** AsyncRequest timeout */ + protected static long timeout = 60000l; /** * @param dlConfig @@ -46,6 +52,8 @@ imageWorker = new ImageWorker(dlConfig, jobinfo); // save AsyncContext this.asyncContext = asyncContext; + asyncContext.setTimeout(AsyncServletWorker.timeout); + logger.debug("timeout for worker: " + asyncContext.getTimeout() + "ms"); this.startTime = startTime; this.errMsgType = errMsgType; this.jobinfo = jobinfo; @@ -56,39 +64,98 @@ */ public void run() { // get fresh response - HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse(); - logger.debug("working on response: (" + ServletOps.headersToString(response) + ")"); + HttpServletResponse response = (HttpServletResponse) asyncContext + .getResponse(); + logger.debug("working on response: (" + + ServletOps.headersToString(response) + ")"); try { // render the image DocuImage img = imageWorker.call(); + if (completed) { + logger.debug("AsyncServletWorker already completed (after scaling)!"); + return; + } // forced destination image type String mt = null; if (jobinfo.hasOption("jpg")) { - mt = "image/jpeg"; + mt = "image/jpeg"; } else if (jobinfo.hasOption("png")) { - mt = "image/png"; + mt = "image/png"; } // send image - ServletOps.sendImage(img, mt, response, logger); + ServletOps.sendImage(img, mt, + (HttpServletResponse) asyncContext.getResponse(), logger); logger.debug("Job done in: " + (System.currentTimeMillis() - startTime) + "ms"); } catch (ImageOpException e) { logger.error(e.getClass() + ": " + e.getMessage()); - Scaler.digilibError(errMsgType, Error.IMAGE, null, response); + Scaler.digilibError(errMsgType, Error.IMAGE, null, + (HttpServletResponse) asyncContext.getResponse()); } catch (IOException e) { logger.error(e.getClass() + ": " + e.getMessage()); - Scaler.digilibError(errMsgType, Error.FILE, null, response); + Scaler.digilibError(errMsgType, Error.FILE, null, + (HttpServletResponse) asyncContext.getResponse()); } catch (ServletException e) { logger.error("Servlet error: ", e); } catch (Exception e) { logger.error("Other error: ", e); } finally { - // submit response - logger.debug("context complete."); - logger.debug("response: (" + ServletOps.headersToString(response) + ")"); - asyncContext.complete(); + if (completed) { + logger.debug("AsyncServletWorker already completed (finally)!"); + } else { + // submit response + logger.debug("context complete."); + this.completed = true; + asyncContext.complete(); + } } } + @Override + public void onStartAsync(AsyncEvent event) throws IOException { + logger.debug("onStartAsync called (why?)"); + } + + @Override + public void onComplete(AsyncEvent event) throws IOException { + logger.debug("AsyncServletWorker onComplete"); + // make sure complete isn't called twice + this.completed = true; + } + + @Override + public void onError(AsyncEvent event) throws IOException { + logger.error("AsyncServletWorker onError: " + event.toString()); + if (completed) { + logger.debug("AsyncServletWorker already completed (TimeOut)!"); + return; + } + imageWorker.stopNow(); + this.completed = true; + Scaler.digilibError(errMsgType, Error.UNKNOWN, null, (HttpServletResponse) asyncContext.getResponse()); + asyncContext.complete(); + } + + @Override + public void onTimeout(AsyncEvent event) throws IOException { + logger.error("AsyncServletWorker TIMED OUT! (increase worker-timeout?)"+event); + if (completed) { + logger.debug("AsyncServletWorker already completed (TimeOut)!"); + return; + } + imageWorker.stopNow(); + this.completed = true; + Scaler.digilibError(errMsgType, Error.UNKNOWN, null, (HttpServletResponse) asyncContext.getResponse()); + asyncContext.complete(); + } + + public static long getTimeout() { + return timeout; + } + + public static void setTimeout(long timeout) { + AsyncServletWorker.timeout = timeout; + } + }
--- a/servlet3/src/main/java/digilib/servlet/DigilibServletConfiguration.java Fri Dec 16 14:34:04 2011 +0100 +++ b/servlet3/src/main/java/digilib/servlet/DigilibServletConfiguration.java Mon Dec 19 21:39:17 2011 +0100 @@ -120,6 +120,8 @@ newParameter("worker-threads", new Integer(1), null, 'f'); // max number of waiting threads newParameter("max-waiting-threads", new Integer(20), null, 'f'); + // timeout for worker threads (ms) + newParameter("worker-timeout", new Integer(60000), null, 'f'); // number of pdf-generation threads newParameter("pdf-worker-threads", new Integer(1), null, 'f'); // max number of waiting pdf-generation threads
--- a/servlet3/src/main/java/digilib/servlet/Initialiser.java Fri Dec 16 14:34:04 2011 +0100 +++ b/servlet3/src/main/java/digilib/servlet/Initialiser.java Mon Dec 19 21:39:17 2011 +0100 @@ -138,7 +138,10 @@ int nt = dlConfig.getAsInt("worker-threads"); int mt = dlConfig.getAsInt("max-waiting-threads"); imageEx = new DigilibJobCenter<DocuImage>(nt, mt, false, "servlet.worker.imageexecutor"); - dlConfig.setValue("servlet.worker.imageexecutor", imageEx); + dlConfig.setValue("servlet.worker.imageexecutor", imageEx); + // digilib worker timeout + long to = dlConfig.getAsInt("worker-timeout"); + AsyncServletWorker.setTimeout(to); // PDF worker threads int pnt = dlConfig.getAsInt("pdf-worker-threads"); int pmt = dlConfig.getAsInt("pdf-max-waiting-threads");
--- a/servlet3/src/main/java/digilib/servlet/Scaler.java Fri Dec 16 14:34:04 2011 +0100 +++ b/servlet3/src/main/java/digilib/servlet/Scaler.java Mon Dec 19 21:39:17 2011 +0100 @@ -5,6 +5,8 @@ import java.util.List; import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +import javax.servlet.AsyncListener; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; @@ -31,7 +33,7 @@ private static final long serialVersionUID = 5289386646192471549L; /** digilib servlet version (for all components) */ - public static final String version = "2.0b1 async"; + public static final String version = "2.0b2 async"; /** servlet error codes */ public static enum Error {UNKNOWN, AUTH, FILE, IMAGE}; @@ -265,9 +267,11 @@ } // worker job is done asynchronously - AsyncContext asyncCtx = request.startAsync(request, response); + AsyncContext asyncCtx = request.startAsync(request, response); // create job AsyncServletWorker job = new AsyncServletWorker(dlConfig, jobTicket, asyncCtx, errMsgType, startTime); + // AsyncServletWorker is its own AsyncListener + asyncCtx.addListener(job); // submit job imageJobCenter.submit(job); // we're done for now
--- a/webapp/src/main/webapp/WEB-INF/digilib-config.xml Fri Dec 16 14:34:04 2011 +0100 +++ b/webapp/src/main/webapp/WEB-INF/digilib-config.xml Mon Dec 19 21:39:17 2011 +0100 @@ -37,6 +37,9 @@ <!-- number of waiting requests in queue --> <parameter name="max-waiting-threads" value="20" /> + <!-- timeout for asynchronous servlet worker (ms) --> + <parameter name="worker-timeout" value="60000" /> + <!-- Restrict access to authorized users. User authentication and roles are provided by the servlet container (see tomcat-users.xml).