changeset 810:5de6c7a73855 stream

Alternative versions of Scaler without async and without threads. Should still work with Servlet API 2.3 (i.e. Tomcat < 7, Jetty < 8).
author robcast
date Sun, 20 Feb 2011 19:34:05 +0100
parents d811204ce5a4
children e6d0cdaa7923
files client/digitallibrary/WEB-INF/web-2.3.xml client/digitallibrary/WEB-INF/web.xml servlet/DigilibServlet.jardesc servlet/src/digilib/servlet/ScalerNoAsync.java servlet/src/digilib/servlet/ScalerNoThread.java
diffstat 5 files changed, 818 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/digitallibrary/WEB-INF/web-2.3.xml	Sun Feb 20 19:34:05 2011 +0100
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
+ 
+<web-app>
+  <!-- General description of your web application -->
+  <display-name>
+        digilib
+  </display-name>
+  <description>
+        This is the web frontend of the Digital Document Library.
+  </description>
+  <!-- The Intialisation Listener -->
+  <listener>
+        <listener-class>
+            digilib.servlet.Initialiser
+        </listener-class>
+  </listener>
+  <!-- The Scaler servlet -->
+  <servlet>
+        <servlet-name>
+            Scaler
+        </servlet-name>
+        <servlet-class>
+            digilib.servlet.ScalerNoAsync
+        </servlet-class>
+        <!-- Load this servlet at server startup time -->
+        <load-on-startup>
+            5
+        </load-on-startup>
+  </servlet>
+  <!-- The Texter servlet -->
+  <servlet>
+        <servlet-name>
+            Texter
+        </servlet-name>
+        <servlet-class>
+            digilib.servlet.Texter
+        </servlet-class>
+  </servlet>
+  <!-- The PDFCache servlet -->
+  <servlet>
+        <servlet-name>
+            PDFCache
+        </servlet-name>
+        <servlet-class>
+            digilib.servlet.PDFCache
+        </servlet-class>
+  </servlet>
+  <!-- The mapping for the Scaler servlet -->
+  <servlet-mapping>
+        <servlet-name>
+            Scaler
+        </servlet-name>
+        <url-pattern>
+            /servlet/Scaler/*
+        </url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+        <servlet-name>
+            Scaler
+        </servlet-name>
+        <url-pattern>
+            /Scaler
+        </url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+        <servlet-name>
+            Scaler
+        </servlet-name>
+        <url-pattern>
+            /authenticated/servlet/Scaler/*
+        </url-pattern>
+  </servlet-mapping>
+  <!-- The mapping for the Texter servlet -->
+  <servlet-mapping>
+        <servlet-name>
+            Texter
+        </servlet-name>
+        <url-pattern>
+            /servlet/Texter/*
+        </url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+        <servlet-name>
+            Texter
+        </servlet-name>
+        <url-pattern>
+            /authenticated/servlet/Texter/*
+        </url-pattern>
+  </servlet-mapping>
+  <!-- The mapping for the Texter servlet -->
+  <servlet-mapping>
+        <servlet-name>
+            PDFCache
+        </servlet-name>
+        <url-pattern>
+            /servlet/PDFCache/*
+        </url-pattern>
+  </servlet-mapping>
+  <servlet-mapping>
+        <servlet-name>
+            PDFCache
+        </servlet-name>
+        <url-pattern>
+            /authenticated/servlet/PDFCache/*
+        </url-pattern>
+  </servlet-mapping>
+  <!-- region for authenticated access -->
+  <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>
+                Authenticated Digilib
+            </web-resource-name>
+            <url-pattern>
+                /authenticated/*
+            </url-pattern>
+        </web-resource-collection>
+        <!-- we need a default user -->
+<!--         <auth-constraint>
+            <role-name>
+                user
+            </role-name>
+        </auth-constraint>  -->
+  </security-constraint>
+  <login-config>
+  <!--
+        <auth-method>BASIC</auth-method>
+        <realm-name>digilib</realm-name> 
+   -->
+        <auth-method>
+            FORM
+        </auth-method>
+        <form-login-config>
+            <form-login-page>
+                /digilib-login.html
+            </form-login-page>
+            <form-error-page>
+                /digilib-fail.html
+            </form-error-page>
+        </form-login-config>
+  </login-config>
+</web-app>
--- a/client/digitallibrary/WEB-INF/web.xml	Sun Feb 20 00:02:51 2011 +0100
+++ b/client/digitallibrary/WEB-INF/web.xml	Sun Feb 20 19:34:05 2011 +0100
@@ -12,7 +12,13 @@
   <description>
         This is the web frontend of the Digital Document Library.
   </description>
-  <!-- The Scaler servlet -->
+  <!-- The Intialisation Listener (also configured by annotation) -->
+  <listener>
+        <listener-class>
+            digilib.servlet.Initialiser
+        </listener-class>
+  </listener>
+  <!-- The Scaler servlet (also configured by annotation) -->
   <servlet>
         <servlet-name>
             Scaler
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/DigilibServlet.jardesc	Sun Feb 20 19:34:05 2011 +0100
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<jardesc>
+    <jar path="digilib-servlet/DigilibServlet.jar"/>
+    <options buildIfNeeded="true" compress="true" descriptionLocation="/digilib-servlet/DigilibServlet.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+    <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+    <selectedProjects/>
+    <manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
+        <sealing sealJar="false">
+            <packagesToSeal/>
+            <packagesToUnSeal/>
+        </sealing>
+    </manifest>
+    <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
+        <file path="/digilib-servlet/license.txt"/>
+    </selectedElements>
+</jardesc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/ScalerNoAsync.java	Sun Feb 20 19:34:05 2011 +0100
@@ -0,0 +1,339 @@
+package digilib.servlet;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+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.AuthOpException;
+import digilib.auth.AuthOps;
+import digilib.image.DocuImage;
+import digilib.image.ImageJobDescription;
+import digilib.image.ImageOpException;
+import digilib.image.ImageWorker;
+import digilib.io.DocuDirCache;
+import digilib.io.DocuDirectory;
+import digilib.io.ImageInput;
+import digilib.util.DigilibJobCenter;
+
+/**
+ * Version of Scaler servlet that uses a thread pool but not Servlet 3.0 async API. 
+ */
+public class ScalerNoAsync extends HttpServlet {
+
+    private static final long serialVersionUID = -5439198888139362735L;
+
+    /** digilib servlet version (for all components) */
+    public static final String version = "1.9.0a3 noasync";
+
+    /** servlet error codes */
+    public static enum Error {UNKNOWN, AUTH, FILE, IMAGE};
+    
+    /** type of error message */
+    public static enum ErrMsg {IMAGE, TEXT, CODE};
+    
+    /** logger for accounting requests */
+    protected static Logger accountlog = Logger.getLogger("account.request");
+
+    /** gengeral logger for this class */
+    protected static Logger logger = Logger.getLogger("digilib.scaler");
+
+    /** logger for authentication related */
+    protected static Logger authlog = Logger.getLogger("digilib.auth");
+
+    /** DocuDirCache instance */
+    protected DocuDirCache dirCache;
+
+    /** Image executor */
+    DigilibJobCenter<DocuImage> imageJobCenter;
+
+    /** authentication error image file */
+    public static File denyImgFile;
+
+    /** image error image file */
+    public static File errorImgFile;
+
+    /** not found error image file */
+    public static File notfoundImgFile;
+
+    /** send files as is? */
+    protected boolean sendFileAllowed = true;
+
+    /** DigilibConfiguration instance */
+    protected DigilibConfiguration dlConfig;
+
+    /** use authorization database */
+    protected boolean useAuthorization = true;
+
+    /** AuthOps instance */
+    protected AuthOps authOp;
+
+    /**
+     * Initialisation on first run.
+     * 
+     * @throws ServletException
+     * 
+     * @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 "
+                        + version + ") *****");
+        // say hello in the log file
+        logger.info("***** Digital Image Library Image Scaler Servlet (version "
+                + version + ") *****");
+
+        // 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");
+
+        // DocuDirCache instance
+        dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
+
+        // Executor
+        imageJobCenter = (DigilibJobCenter<DocuImage>) dlConfig
+                .getValue("servlet.worker.imageexecutor");
+
+        denyImgFile = ServletOps.getFile(
+                (File) dlConfig.getValue("denied-image"), context);
+        errorImgFile = ServletOps.getFile(
+                (File) dlConfig.getValue("error-image"), context);
+        notfoundImgFile = ServletOps.getFile(
+                (File) dlConfig.getValue("notfound-image"), context);
+        sendFileAllowed = dlConfig.getAsBoolean("sendfile-allowed");
+    }
+
+    /** Returns modification time relevant to the request for caching.
+     * 
+     * @see javax.servlet.http.HttpServlet#getLastModified(javax.servlet.http.HttpServletRequest)
+     */
+    public long getLastModified(HttpServletRequest request) {
+        accountlog.debug("GetLastModified from " + request.getRemoteAddr()
+                + " for " + request.getQueryString());
+        long mtime = -1;
+        // create new request
+        DigilibRequest dlReq = new DigilibRequest(request);
+        DocuDirectory dd = dirCache.getDirectory(dlReq.getFilePath());
+        if (dd != null) {
+            mtime = dd.getDirMTime() / 1000 * 1000;
+        }
+        logger.debug("  returns "+mtime);
+        return mtime;
+    }
+
+    /* (non-Javadoc)
+     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
+        accountlog.info("GET from " + request.getRemoteAddr());
+        this.processRequest(request, response);
+    }
+
+
+    /* (non-Javadoc)
+     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
+        accountlog.info("POST from " + request.getRemoteAddr());
+        this.processRequest(request, response);
+    }
+    
+
+	protected void doHead(HttpServletRequest req, HttpServletResponse resp)
+			throws ServletException, IOException {
+		logger.debug("HEAD from "+req.getRemoteAddr());
+		super.doHead(req, resp);
+	}
+
+	protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
+			throws ServletException, IOException {
+		logger.debug("OPTIONS from "+req.getRemoteAddr());
+		super.doOptions(req, resp);
+	}
+
+	/** Service this request using the response.
+     * @param request
+     * @param response
+     * @throws ServletException 
+     */
+    public void processRequest(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException {
+
+        if (dlConfig == null) {
+            logger.error("ERROR: No Configuration!");
+            throw new ServletException("NO VALID digilib CONFIGURATION!");
+        }
+
+        accountlog.debug("request: " + request.getQueryString());
+        logger.debug("request: " + request.getQueryString());
+        long startTime = System.currentTimeMillis();
+
+        // parse request
+        DigilibRequest dlRequest = new DigilibRequest(request);
+        // extract the job information
+        ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig);
+
+        // type of error reporting
+        ErrMsg errMsgType = ErrMsg.IMAGE;
+        if (dlRequest.hasOption("errtxt")) {
+        	errMsgType = ErrMsg.TEXT;
+        } else if (dlRequest.hasOption("errcode")) {
+        	errMsgType = ErrMsg.CODE;
+        }
+        
+        try {
+        	/*
+        	 *  check if we can fast-track without scaling
+        	 */
+            ImageInput fileToLoad = (ImageInput) jobTicket.getInput();
+
+            // check permissions
+            if (useAuthorization) {
+                // get a list of required roles (empty if no restrictions)
+                List<String> rolesRequired = authOp.rolesForPath(
+                        jobTicket.getFilePath(), 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();
+                    }
+                }
+            }
+
+            // if requested, send image as a file
+            if (sendFileAllowed && jobTicket.getSendAsFile()) {
+                String mt = null;
+                if (jobTicket.hasOption("rawfile")) {
+                    mt = "application/octet-stream";
+                }
+                logger.debug("Sending RAW File as is.");
+                ServletOps.sendFile(fileToLoad.getFile(), mt, null, response, logger);
+                logger.info("Done in " + (System.currentTimeMillis() - startTime) + "ms");
+                return;
+            }
+
+            // if possible, send the image without actually having to transform it
+            if (! jobTicket.isTransformRequired()) {
+                logger.debug("Sending File as is.");
+                ServletOps.sendFile(fileToLoad.getFile(), null, null, response, logger);
+                logger.info("Done in " + (System.currentTimeMillis() - startTime) + "ms");
+                return;
+            }
+
+            // check load of workers
+            if (imageJobCenter.isBusy()) {
+                logger.error("Servlet overloaded!");
+                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
+                return;
+            }
+            // create job
+            ImageWorker job = new ImageWorker(dlConfig, jobTicket);
+            // submit job
+            Future<DocuImage> jobResult = imageJobCenter.submit(job);
+            // wait for result
+            DocuImage img = jobResult.get();
+            // send image
+            ServletOps.sendImage(img, null, response, logger);
+            logger.debug("Job Processing Time: "
+                    + (System.currentTimeMillis() - startTime) + "ms");
+
+        } catch (ImageOpException e) {
+            logger.error(e.getClass() + ": " + e.getMessage());
+            digilibError(errMsgType, Error.IMAGE, null, response);
+        } catch (IOException e) {
+            logger.error(e.getClass() + ": " + e.getMessage());
+            digilibError(errMsgType, Error.FILE, null, response);
+        } catch (AuthOpException e) {
+            logger.error(e.getClass() + ": " + e.getMessage());
+            digilibError(errMsgType, Error.AUTH, null, response);
+        } catch (InterruptedException e) {
+            logger.error(e.getClass() + ": " + e.getMessage());
+        } catch (ExecutionException e) {
+            logger.error(e.getClass() + ": " + e.getMessage());
+            String causeMsg = e.getCause().getMessage();
+            logger.error("caused by: " + causeMsg);
+            digilibError(errMsgType, Error.IMAGE, causeMsg, response);
+        }
+
+    }
+
+    /**
+     * Sends an error to the client as text or image.
+     * 
+     * @param type
+     * @param error
+     * @param msg
+     * @param response
+     */
+    public static void digilibError(ErrMsg type, Error error, String msg,
+            HttpServletResponse response) {
+        try {
+            File img = null;
+            int status = 0;
+            if (error == Error.AUTH) {
+                if (msg == null) {
+                    msg = "ERROR: Unauthorized access!";
+                }
+                img = denyImgFile;
+                status = HttpServletResponse.SC_FORBIDDEN;
+            } else if (error == Error.FILE) {
+                if (msg == null) {
+                    msg = "ERROR: Image file not found!";
+                }
+                img = notfoundImgFile;
+                status = HttpServletResponse.SC_NOT_FOUND;
+            } else {
+                if (msg == null) {
+                    msg = "ERROR: Other image error!";
+                }
+                img = errorImgFile;
+                status = HttpServletResponse.SC_BAD_REQUEST;
+            }
+            if (response.isCommitted()) {
+                // response already committed
+                logger.error("Unable to send error: " + msg);
+                return;
+            }
+            if (type == ErrMsg.TEXT) {
+                ServletOps.htmlMessage(msg, response);
+            } else if (type == ErrMsg.CODE) {
+                response.sendError(status, msg);
+            } else if (img != null) {
+                // default: image
+                ServletOps.sendFile(img, null, null, response, logger);
+            }
+        } catch (Exception e) {
+            logger.error("Error sending error!", e);
+        }
+
+    }
+
+    public static String getVersion() {
+        return version;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlet/src/digilib/servlet/ScalerNoThread.java	Sun Feb 20 19:34:05 2011 +0100
@@ -0,0 +1,314 @@
+package digilib.servlet;
+
+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.auth.AuthOpException;
+import digilib.auth.AuthOps;
+import digilib.image.DocuImage;
+import digilib.image.ImageJobDescription;
+import digilib.image.ImageOpException;
+import digilib.image.ImageWorker;
+import digilib.io.DocuDirCache;
+import digilib.io.DocuDirectory;
+import digilib.io.ImageInput;
+
+/**
+ * Version of Scaler servlet that doesn't use a thread pool.
+ */
+public class ScalerNoThread extends HttpServlet {
+
+    private static final long serialVersionUID = 1450947819851623306L;
+
+    /** digilib servlet version (for all components) */
+    public static final String version = "1.9.0a3 nothread";
+
+    /** servlet error codes */
+    public static enum Error {UNKNOWN, AUTH, FILE, IMAGE};
+    
+    /** type of error message */
+    public static enum ErrMsg {IMAGE, TEXT, CODE};
+    
+    /** logger for accounting requests */
+    protected static Logger accountlog = Logger.getLogger("account.request");
+
+    /** gengeral logger for this class */
+    protected static Logger logger = Logger.getLogger("digilib.scaler");
+
+    /** logger for authentication related */
+    protected static Logger authlog = Logger.getLogger("digilib.auth");
+
+    /** DocuDirCache instance */
+    protected DocuDirCache dirCache;
+
+    /** authentication error image file */
+    public static File denyImgFile;
+
+    /** image error image file */
+    public static File errorImgFile;
+
+    /** not found error image file */
+    public static File notfoundImgFile;
+
+    /** send files as is? */
+    protected boolean sendFileAllowed = true;
+
+    /** DigilibConfiguration instance */
+    protected DigilibConfiguration dlConfig;
+
+    /** use authorization database */
+    protected boolean useAuthorization = true;
+
+    /** AuthOps instance */
+    protected AuthOps authOp;
+
+    /**
+     * Initialisation on first run.
+     * 
+     * @throws ServletException
+     * 
+     * @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 "
+                        + version + ") *****");
+        // say hello in the log file
+        logger.info("***** Digital Image Library Image Scaler Servlet (version "
+                + version + ") *****");
+
+        // 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");
+
+        // DocuDirCache instance
+        dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache");
+
+        denyImgFile = ServletOps.getFile(
+                (File) dlConfig.getValue("denied-image"), context);
+        errorImgFile = ServletOps.getFile(
+                (File) dlConfig.getValue("error-image"), context);
+        notfoundImgFile = ServletOps.getFile(
+                (File) dlConfig.getValue("notfound-image"), context);
+        sendFileAllowed = dlConfig.getAsBoolean("sendfile-allowed");
+    }
+
+    /** Returns modification time relevant to the request for caching.
+     * 
+     * @see javax.servlet.http.HttpServlet#getLastModified(javax.servlet.http.HttpServletRequest)
+     */
+    public long getLastModified(HttpServletRequest request) {
+        accountlog.debug("GetLastModified from " + request.getRemoteAddr()
+                + " for " + request.getQueryString());
+        long mtime = -1;
+        // create new request
+        DigilibRequest dlReq = new DigilibRequest(request);
+        DocuDirectory dd = dirCache.getDirectory(dlReq.getFilePath());
+        if (dd != null) {
+            mtime = dd.getDirMTime() / 1000 * 1000;
+        }
+        logger.debug("  returns "+mtime);
+        return mtime;
+    }
+
+    /* (non-Javadoc)
+     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
+        accountlog.info("GET from " + request.getRemoteAddr());
+        this.processRequest(request, response);
+    }
+
+
+    /* (non-Javadoc)
+     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+     */
+    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
+        accountlog.info("POST from " + request.getRemoteAddr());
+        this.processRequest(request, response);
+    }
+    
+
+	protected void doHead(HttpServletRequest req, HttpServletResponse resp)
+			throws ServletException, IOException {
+		logger.debug("HEAD from "+req.getRemoteAddr());
+		super.doHead(req, resp);
+	}
+
+	protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
+			throws ServletException, IOException {
+		logger.debug("OPTIONS from "+req.getRemoteAddr());
+		super.doOptions(req, resp);
+	}
+
+	/** Service this request using the response.
+     * @param request
+     * @param response
+     * @throws ServletException 
+     */
+    public void processRequest(HttpServletRequest request,
+            HttpServletResponse response) throws ServletException {
+
+        if (dlConfig == null) {
+            logger.error("ERROR: No Configuration!");
+            throw new ServletException("NO VALID digilib CONFIGURATION!");
+        }
+
+        accountlog.debug("request: " + request.getQueryString());
+        logger.debug("request: " + request.getQueryString());
+        long startTime = System.currentTimeMillis();
+
+        // parse request
+        DigilibRequest dlRequest = new DigilibRequest(request);
+        // extract the job information
+        ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig);
+
+        // type of error reporting
+        ErrMsg errMsgType = ErrMsg.IMAGE;
+        if (dlRequest.hasOption("errtxt")) {
+        	errMsgType = ErrMsg.TEXT;
+        } else if (dlRequest.hasOption("errcode")) {
+        	errMsgType = ErrMsg.CODE;
+        }
+        
+        try {
+        	/*
+        	 *  check if we can fast-track without scaling
+        	 */
+            ImageInput fileToLoad = (ImageInput) jobTicket.getInput();
+
+            // check permissions
+            if (useAuthorization) {
+                // get a list of required roles (empty if no restrictions)
+                List<String> rolesRequired = authOp.rolesForPath(
+                        jobTicket.getFilePath(), 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();
+                    }
+                }
+            }
+
+            // if requested, send image as a file
+            if (sendFileAllowed && jobTicket.getSendAsFile()) {
+                String mt = null;
+                if (jobTicket.hasOption("rawfile")) {
+                    mt = "application/octet-stream";
+                }
+                logger.debug("Sending RAW File as is.");
+                ServletOps.sendFile(fileToLoad.getFile(), mt, null, response, logger);
+                logger.info("Done in " + (System.currentTimeMillis() - startTime) + "ms");
+                return;
+            }
+
+            // if possible, send the image without actually having to transform it
+            if (! jobTicket.isTransformRequired()) {
+                logger.debug("Sending File as is.");
+                ServletOps.sendFile(fileToLoad.getFile(), null, null, response, logger);
+                logger.info("Done in " + (System.currentTimeMillis() - startTime) + "ms");
+                return;
+            }
+
+            // create job
+            ImageWorker job = new ImageWorker(dlConfig, jobTicket);
+            // get result immediately
+            DocuImage img = job.call();
+            // send image
+            ServletOps.sendImage(img, null, response, logger);
+            logger.debug("Job Processing Time: "
+                    + (System.currentTimeMillis() - startTime) + "ms");
+
+        } catch (ImageOpException e) {
+            logger.error(e.getClass() + ": " + e.getMessage());
+            digilibError(errMsgType, Error.IMAGE, null, response);
+        } catch (IOException e) {
+            logger.error(e.getClass() + ": " + e.getMessage());
+            digilibError(errMsgType, Error.FILE, null, response);
+        } catch (AuthOpException e) {
+            logger.error(e.getClass() + ": " + e.getMessage());
+            digilibError(errMsgType, Error.AUTH, null, response);
+        }
+
+    }
+
+    /**
+     * Sends an error to the client as text or image.
+     * 
+     * @param type
+     * @param error
+     * @param msg
+     * @param response
+     */
+    public static void digilibError(ErrMsg type, Error error, String msg,
+            HttpServletResponse response) {
+        try {
+            File img = null;
+            int status = 0;
+            if (error == Error.AUTH) {
+                if (msg == null) {
+                    msg = "ERROR: Unauthorized access!";
+                }
+                img = denyImgFile;
+                status = HttpServletResponse.SC_FORBIDDEN;
+            } else if (error == Error.FILE) {
+                if (msg == null) {
+                    msg = "ERROR: Image file not found!";
+                }
+                img = notfoundImgFile;
+                status = HttpServletResponse.SC_NOT_FOUND;
+            } else {
+                if (msg == null) {
+                    msg = "ERROR: Other image error!";
+                }
+                img = errorImgFile;
+                status = HttpServletResponse.SC_BAD_REQUEST;
+            }
+            if (response.isCommitted()) {
+                // response already committed
+                logger.error("Unable to send error: " + msg);
+                return;
+            }
+            if (type == ErrMsg.TEXT) {
+                ServletOps.htmlMessage(msg, response);
+            } else if (type == ErrMsg.CODE) {
+                response.sendError(status, msg);
+            } else if (img != null) {
+                // default: image
+                ServletOps.sendFile(img, null, null, response, logger);
+            }
+        } catch (Exception e) {
+            logger.error("Error sending error!", e);
+        }
+
+    }
+
+    public static String getVersion() {
+        return version;
+    }
+
+}