Mercurial > hg > digilib-old
annotate servlet2/src/main/java/digilib/servlet/Scaler.java @ 1127:a5699754288b
use css standard color names (from http://en.wikipedia.org/wiki/Web_colors).
new colors for area annotation.
| author | robcast |
|---|---|
| date | Fri, 09 Nov 2012 18:55:46 +0100 |
| parents | 4e368c85cce4 |
| children | 2ee261676828 |
| rev | line source |
|---|---|
| 903 | 1 package digilib.servlet; |
| 2 | |
| 3 import java.io.File; | |
| 4 import java.io.IOException; | |
| 5 import java.util.List; | |
| 6 import java.util.concurrent.ExecutionException; | |
| 7 import java.util.concurrent.Future; | |
| 8 | |
| 9 import javax.servlet.ServletConfig; | |
| 10 import javax.servlet.ServletContext; | |
| 11 import javax.servlet.ServletException; | |
| 12 import javax.servlet.http.HttpServlet; | |
| 13 import javax.servlet.http.HttpServletRequest; | |
| 14 import javax.servlet.http.HttpServletResponse; | |
| 15 | |
| 16 import org.apache.log4j.Logger; | |
| 17 | |
| 18 import digilib.auth.AuthOpException; | |
| 19 import digilib.auth.AuthOps; | |
| 20 import digilib.image.DocuImage; | |
| 21 import digilib.image.ImageJobDescription; | |
| 22 import digilib.image.ImageOpException; | |
| 23 import digilib.image.ImageWorker; | |
| 24 import digilib.io.DocuDirCache; | |
| 25 import digilib.io.DocuDirectory; | |
| 26 import digilib.io.ImageInput; | |
| 27 import digilib.util.DigilibJobCenter; | |
| 28 | |
| 29 /** | |
| 30 * Version of Scaler servlet that uses a thread pool but not Servlet 3.0 async API. | |
| 31 */ | |
| 32 public class Scaler extends HttpServlet { | |
| 33 | |
| 34 private static final long serialVersionUID = -5439198888139362735L; | |
| 35 | |
| 36 /** digilib servlet version (for all components) */ | |
|
1032
4e368c85cce4
CLOSED - # 22: wrong contrast setting. dito #23 (at least on OSX 10.7)
robcast
parents:
960
diff
changeset
|
37 public static final String version = "2.1b2 noasync"; |
| 903 | 38 |
| 39 /** servlet error codes */ | |
| 40 public static enum Error {UNKNOWN, AUTH, FILE, IMAGE}; | |
| 41 | |
| 42 /** type of error message */ | |
| 43 public static enum ErrMsg {IMAGE, TEXT, CODE}; | |
| 44 | |
| 45 /** logger for accounting requests */ | |
| 46 protected static Logger accountlog = Logger.getLogger("account.request"); | |
| 47 | |
| 48 /** gengeral logger for this class */ | |
| 49 protected static Logger logger = Logger.getLogger("digilib.scaler"); | |
| 50 | |
| 51 /** logger for authentication related */ | |
| 52 protected static Logger authlog = Logger.getLogger("digilib.auth"); | |
| 53 | |
| 54 /** DocuDirCache instance */ | |
| 55 protected DocuDirCache dirCache; | |
| 56 | |
| 57 /** Image executor */ | |
| 58 DigilibJobCenter<DocuImage> imageJobCenter; | |
| 59 | |
| 60 /** authentication error image file */ | |
| 61 public static File denyImgFile; | |
| 62 | |
| 63 /** image error image file */ | |
| 64 public static File errorImgFile; | |
| 65 | |
| 66 /** not found error image file */ | |
| 67 public static File notfoundImgFile; | |
| 68 | |
| 69 /** send files as is? */ | |
| 70 protected boolean sendFileAllowed = true; | |
| 71 | |
| 72 /** DigilibConfiguration instance */ | |
| 73 protected DigilibServletConfiguration dlConfig; | |
| 74 | |
| 75 /** use authorization database */ | |
| 76 protected boolean useAuthorization = true; | |
| 77 | |
| 78 /** AuthOps instance */ | |
| 79 protected AuthOps authOp; | |
| 80 | |
| 81 /** | |
| 82 * Initialisation on first run. | |
| 83 * | |
| 84 * @throws ServletException | |
| 85 * | |
| 86 * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) | |
| 87 */ | |
| 88 public void init(ServletConfig config) throws ServletException { | |
| 89 super.init(config); | |
| 90 | |
| 91 System.out | |
| 92 .println("***** Digital Image Library Image Scaler Servlet (version " | |
| 93 + version + ") *****"); | |
| 94 // say hello in the log file | |
| 95 logger.info("***** Digital Image Library Image Scaler Servlet (version " | |
| 96 + version + ") *****"); | |
| 97 | |
| 98 // get our ServletContext | |
| 99 ServletContext context = config.getServletContext(); | |
| 100 // see if there is a Configuration instance | |
| 101 dlConfig = (DigilibServletConfiguration) context | |
| 102 .getAttribute("digilib.servlet.configuration"); | |
| 103 if (dlConfig == null) { | |
| 104 // no Configuration | |
| 105 throw new ServletException("No Configuration!"); | |
| 106 } | |
|
1032
4e368c85cce4
CLOSED - # 22: wrong contrast setting. dito #23 (at least on OSX 10.7)
robcast
parents:
960
diff
changeset
|
107 // log DocuImage version |
|
4e368c85cce4
CLOSED - # 22: wrong contrast setting. dito #23 (at least on OSX 10.7)
robcast
parents:
960
diff
changeset
|
108 logger.info("Scaler uses " + dlConfig.getValue("servlet.docuimage.version")); |
| 903 | 109 // set our AuthOps |
| 110 useAuthorization = dlConfig.getAsBoolean("use-authorization"); | |
| 111 authOp = (AuthOps) dlConfig.getValue("servlet.auth.op"); | |
| 112 | |
| 113 // DocuDirCache instance | |
| 114 dirCache = (DocuDirCache) dlConfig.getValue("servlet.dir.cache"); | |
| 115 | |
| 116 // Executor | |
| 117 imageJobCenter = (DigilibJobCenter<DocuImage>) dlConfig | |
| 118 .getValue("servlet.worker.imageexecutor"); | |
| 119 | |
| 120 denyImgFile = ServletOps.getFile( | |
| 121 (File) dlConfig.getValue("denied-image"), context); | |
| 122 errorImgFile = ServletOps.getFile( | |
| 123 (File) dlConfig.getValue("error-image"), context); | |
| 124 notfoundImgFile = ServletOps.getFile( | |
| 125 (File) dlConfig.getValue("notfound-image"), context); | |
| 126 sendFileAllowed = dlConfig.getAsBoolean("sendfile-allowed"); | |
| 127 } | |
| 128 | |
| 129 /** Returns modification time relevant to the request for caching. | |
| 130 * | |
| 131 * @see javax.servlet.http.HttpServlet#getLastModified(javax.servlet.http.HttpServletRequest) | |
| 132 */ | |
| 133 public long getLastModified(HttpServletRequest request) { | |
| 134 accountlog.debug("GetLastModified from " + request.getRemoteAddr() | |
| 135 + " for " + request.getQueryString()); | |
| 136 long mtime = -1; | |
| 137 // create new request | |
| 138 DigilibServletRequest dlReq = new DigilibServletRequest(request); | |
| 139 DocuDirectory dd = dirCache.getDirectory(dlReq.getFilePath()); | |
| 140 if (dd != null) { | |
| 141 mtime = dd.getDirMTime() / 1000 * 1000; | |
| 142 } | |
| 143 logger.debug(" returns "+mtime); | |
| 144 return mtime; | |
| 145 } | |
| 146 | |
| 147 /* (non-Javadoc) | |
| 148 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) | |
| 149 */ | |
| 150 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException { | |
| 151 accountlog.info("GET from " + request.getRemoteAddr()); | |
| 152 this.processRequest(request, response); | |
| 153 } | |
| 154 | |
| 155 | |
| 156 /* (non-Javadoc) | |
| 157 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) | |
| 158 */ | |
| 159 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException { | |
| 160 accountlog.info("POST from " + request.getRemoteAddr()); | |
| 161 this.processRequest(request, response); | |
| 162 } | |
| 163 | |
| 164 | |
| 165 protected void doHead(HttpServletRequest req, HttpServletResponse resp) | |
| 166 throws ServletException, IOException { | |
| 167 logger.debug("HEAD from "+req.getRemoteAddr()); | |
| 168 super.doHead(req, resp); | |
| 169 } | |
| 170 | |
| 171 protected void doOptions(HttpServletRequest req, HttpServletResponse resp) | |
| 172 throws ServletException, IOException { | |
| 173 logger.debug("OPTIONS from "+req.getRemoteAddr()); | |
| 174 super.doOptions(req, resp); | |
| 175 } | |
| 176 | |
| 177 /** Service this request using the response. | |
| 178 * @param request | |
| 179 * @param response | |
| 180 * @throws ServletException | |
| 181 */ | |
| 182 public void processRequest(HttpServletRequest request, | |
| 183 HttpServletResponse response) throws ServletException { | |
| 184 | |
| 185 if (dlConfig == null) { | |
| 186 logger.error("ERROR: No Configuration!"); | |
| 187 throw new ServletException("NO VALID digilib CONFIGURATION!"); | |
| 188 } | |
| 189 | |
| 190 accountlog.debug("request: " + request.getQueryString()); | |
| 191 logger.debug("request: " + request.getQueryString()); | |
| 192 long startTime = System.currentTimeMillis(); | |
| 193 | |
| 194 // parse request | |
| 195 DigilibServletRequest dlRequest = new DigilibServletRequest(request); | |
| 196 // extract the job information | |
| 197 ImageJobDescription jobTicket = ImageJobDescription.getInstance(dlRequest, dlConfig); | |
| 198 | |
| 199 // type of error reporting | |
| 200 ErrMsg errMsgType = ErrMsg.IMAGE; | |
| 201 if (dlRequest.hasOption("errtxt")) { | |
| 202 errMsgType = ErrMsg.TEXT; | |
| 203 } else if (dlRequest.hasOption("errcode")) { | |
| 204 errMsgType = ErrMsg.CODE; | |
| 205 } | |
| 206 | |
| 207 try { | |
| 208 /* | |
| 209 * check if we can fast-track without scaling | |
| 210 */ | |
| 211 ImageInput fileToLoad = (ImageInput) jobTicket.getInput(); | |
| 212 | |
| 213 // check permissions | |
| 214 if (useAuthorization) { | |
| 215 // get a list of required roles (empty if no restrictions) | |
| 216 List<String> rolesRequired = authOp.rolesForPath( | |
| 217 jobTicket.getFilePath(), request); | |
| 218 if (rolesRequired != null) { | |
| 219 authlog.debug("Role required: " + rolesRequired); | |
| 220 authlog.debug("User: " + request.getRemoteUser()); | |
| 221 // is the current request/user authorized? | |
| 222 if (!authOp.isRoleAuthorized(rolesRequired, request)) { | |
| 223 // send deny answer and abort | |
| 224 throw new AuthOpException(); | |
| 225 } | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 // if requested, send image as a file | |
| 230 if (sendFileAllowed && jobTicket.getSendAsFile()) { | |
| 231 String mt = null; | |
| 232 if (jobTicket.hasOption("rawfile")) { | |
| 233 mt = "application/octet-stream"; | |
| 234 } | |
| 235 logger.debug("Sending RAW File as is."); | |
| 236 ServletOps.sendFile(fileToLoad.getFile(), mt, null, response, logger); | |
| 237 logger.info("Done in " + (System.currentTimeMillis() - startTime) + "ms"); | |
| 238 return; | |
| 239 } | |
| 240 | |
| 241 // if possible, send the image without actually having to transform it | |
| 242 if (! jobTicket.isTransformRequired()) { | |
| 243 logger.debug("Sending File as is."); | |
| 244 ServletOps.sendFile(fileToLoad.getFile(), null, null, response, logger); | |
| 245 logger.info("Done in " + (System.currentTimeMillis() - startTime) + "ms"); | |
| 246 return; | |
| 247 } | |
| 248 | |
| 249 // check load of workers | |
| 250 if (imageJobCenter.isBusy()) { | |
| 251 logger.error("Servlet overloaded!"); | |
| 252 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); | |
| 253 return; | |
| 254 } | |
| 255 // create job | |
| 256 ImageWorker job = new ImageWorker(dlConfig, jobTicket); | |
| 257 // submit job | |
| 258 Future<DocuImage> jobResult = imageJobCenter.submit(job); | |
| 259 // wait for result | |
| 260 DocuImage img = jobResult.get(); | |
| 261 // forced destination image type | |
| 262 String mt = null; | |
| 263 if (jobTicket.hasOption("jpg")) { | |
| 264 mt = "image/jpeg"; | |
| 265 } else if (jobTicket.hasOption("png")) { | |
| 266 mt = "image/png"; | |
| 267 } | |
| 268 // send image | |
| 269 ServletOps.sendImage(img, mt, response, logger); | |
| 270 logger.debug("Job Processing Time: " | |
| 271 + (System.currentTimeMillis() - startTime) + "ms"); | |
| 272 | |
| 273 } catch (ImageOpException e) { | |
| 274 logger.error(e.getClass() + ": " + e.getMessage()); | |
| 275 digilibError(errMsgType, Error.IMAGE, null, response); | |
| 276 } catch (IOException e) { | |
| 277 logger.error(e.getClass() + ": " + e.getMessage()); | |
| 278 digilibError(errMsgType, Error.FILE, null, response); | |
| 279 } catch (AuthOpException e) { | |
| 280 logger.error(e.getClass() + ": " + e.getMessage()); | |
| 281 digilibError(errMsgType, Error.AUTH, null, response); | |
| 282 } catch (InterruptedException e) { | |
| 283 logger.error(e.getClass() + ": " + e.getMessage()); | |
| 284 } catch (ExecutionException e) { | |
| 285 logger.error(e.getClass() + ": " + e.getMessage()); | |
| 286 String causeMsg = e.getCause().getMessage(); | |
| 287 logger.error("caused by: " + causeMsg); | |
| 288 digilibError(errMsgType, Error.IMAGE, causeMsg, response); | |
| 289 } | |
| 290 | |
| 291 } | |
| 292 | |
| 293 /** | |
| 294 * Sends an error to the client as text or image. | |
| 295 * | |
| 296 * @param type | |
| 297 * @param error | |
| 298 * @param msg | |
| 299 * @param response | |
| 300 */ | |
| 301 public static void digilibError(ErrMsg type, Error error, String msg, | |
| 302 HttpServletResponse response) { | |
| 303 try { | |
| 304 File img = null; | |
| 305 int status = 0; | |
| 306 if (error == Error.AUTH) { | |
| 307 if (msg == null) { | |
| 308 msg = "ERROR: Unauthorized access!"; | |
| 309 } | |
| 310 img = denyImgFile; | |
| 311 status = HttpServletResponse.SC_FORBIDDEN; | |
| 312 } else if (error == Error.FILE) { | |
| 313 if (msg == null) { | |
| 314 msg = "ERROR: Image file not found!"; | |
| 315 } | |
| 316 img = notfoundImgFile; | |
| 317 status = HttpServletResponse.SC_NOT_FOUND; | |
| 318 } else { | |
| 319 if (msg == null) { | |
| 320 msg = "ERROR: Other image error!"; | |
| 321 } | |
| 322 img = errorImgFile; | |
| 323 status = HttpServletResponse.SC_BAD_REQUEST; | |
| 324 } | |
| 325 if (response.isCommitted()) { | |
| 326 // response already committed | |
| 327 logger.error("Unable to send error: " + msg); | |
| 328 return; | |
| 329 } | |
| 330 if (type == ErrMsg.TEXT) { | |
| 331 ServletOps.htmlMessage(msg, response); | |
| 332 } else if (type == ErrMsg.CODE) { | |
| 333 response.sendError(status, msg); | |
| 334 } else if (img != null) { | |
| 335 // default: image | |
| 336 ServletOps.sendFile(img, null, null, response, logger); | |
| 337 } | |
| 338 } catch (Exception e) { | |
| 339 logger.error("Error sending error!", e); | |
| 340 } | |
| 341 | |
| 342 } | |
| 343 | |
| 344 public static String getVersion() { | |
| 345 return version; | |
| 346 } | |
| 347 | |
| 348 } |
