001    /*                                                                                      */
002    /* Image Viewer - MCR-IView 1.0, 05-2006                */
003    /* +++++++++++++++++++++++++++++++++++++                */
004    /*                                                                                      */
005    /* Andreas Trappe       - concept, devel. in misc.  */
006    /* Britta Kapitzki      - Design                                        */
007    /* Thomas Scheffler - html prototype                */
008    /* Stephan Schmidt      - html prototype                        */
009    /*                                                                                      */
010    
011    package org.mycore.frontend.iview;
012    
013    import java.awt.Point;
014    import java.io.FileNotFoundException;
015    import java.io.IOException;
016    import java.io.UnsupportedEncodingException;
017    import java.text.DateFormat;
018    import java.text.SimpleDateFormat;
019    import java.util.Enumeration;
020    import java.util.GregorianCalendar;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Properties;
024    import java.util.StringTokenizer;
025    import java.util.Vector;
026    
027    import javax.servlet.ServletException;
028    import javax.servlet.ServletOutputStream;
029    import javax.servlet.http.HttpServletRequest;
030    import javax.servlet.http.HttpServletResponse;
031    
032    import org.apache.log4j.Logger;
033    import org.jdom.Document;
034    import org.jdom.Element;
035    import org.jdom.JDOMException;
036    import org.mycore.common.MCRCache;
037    import org.mycore.common.MCRConfiguration;
038    import org.mycore.common.MCRConfigurationException;
039    import org.mycore.common.MCRException;
040    import org.mycore.common.MCRSession;
041    import org.mycore.common.MCRSessionMgr;
042    import org.mycore.datamodel.ifs.MCRDirectory;
043    import org.mycore.datamodel.ifs.MCRFile;
044    import org.mycore.datamodel.ifs.MCRFilesystemNode;
045    import org.mycore.datamodel.metadata.MCRDerivate;
046    import org.mycore.frontend.servlets.MCRServlet;
047    import org.mycore.frontend.servlets.MCRServletJob;
048    import org.mycore.services.imaging.MCRImgCacheCommands;
049    import org.mycore.services.imaging.MCRImgService;
050    import org.mycore.services.imaging.Stopwatch;
051    
052    /**
053     * @author Andreas Trappe, Chi Vu Huu
054     * 
055     */
056    
057    public class MCRIViewServlet extends MCRServlet {
058    
059        private static final long serialVersionUID = 1L;
060    
061        private static Logger LOGGER = Logger.getLogger(MCRIViewServlet.class);
062    
063        private Stopwatch timer = new Stopwatch();
064    
065        private String dateFormat = "dd.MM.yyyy HH:mm:ss";
066    
067        private DateFormat dateFormatter = new SimpleDateFormat(dateFormat);
068    
069        public void init() throws MCRConfigurationException, ServletException {
070            super.init();
071        }
072    
073        public void doGetPost(MCRServletJob job) throws IOException, ServletException, JDOMException {
074    
075            HttpServletRequest request = job.getRequest();
076            HttpServletResponse response = job.getResponse();
077    
078            // get host config
079            String hostAlias = getProperty(request, "hosts");
080            if ((hostAlias == null) || (hostAlias.trim().length() == 0))
081                hostAlias = "local";
082    
083            // get PATH of MCRNode
084            String requestPath = request.getPathInfo();
085    
086            // printRequest(request);
087    
088            // generate error page if path is empty
089            if (requestPath == null)
090                prepareErrorPage(request, response, "Error: HTTP request path is null");
091            // generate error page if path is incorrect
092            StringTokenizer st = new StringTokenizer(requestPath, "/");
093            if (!st.hasMoreTokens())
094                prepareErrorPage(request, response, "Error: HTTP request path is null");
095    
096            /*
097             * ######################################################################
098             * ####################################### // get mode and process
099             */
100            timer.start();
101            if ((request.getParameter("mode") != null) && (!request.getParameter("mode").equals(""))) {
102                if (request.getParameter("mode").equals("generateLayout")) {
103                    generateLayout(request, response);
104                } else if (request.getParameter("mode").equals("getImage")) {
105                    getImage(request, response);
106                } else if (request.getParameter("mode").equals("getMetadata")) {
107                    getMetadata(request, response, st);
108                } else if (request.getParameter("mode").equals("setMetadata"))
109                    setMetadata(request, response);
110            }
111            timer.stop();
112            timer.reset();
113        }
114    
115        public void setMetadata(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
116    
117            LOGGER.debug("AJAX: received size of image area - " + "width=" + request.getParameter("XSL.browser.res.width.SESSION")
118                    + "px, height=" + request.getParameter("XSL.browser.res.height.SESSION") + "...");
119            Properties resolution = new Properties();
120            resolution.put("XSL.browser.res.width.SESSION", request.getParameter("XSL.browser.res.width.SESSION"));
121            resolution.put("XSL.browser.res.height.SESSION", request.getParameter("XSL.browser.res.height.SESSION"));
122            updateIViewConfig(new Properties(), request, resolution, "AJAX resol. data added...");
123    
124            forwardJDOM(request, response, new Element("setMetadata").setText("successfully"));
125    
126        }
127    
128        public void generateLayout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException,
129                JDOMException {
130    
131            MCRFilesystemNode nodeToBeDisplayed = getMCRNodeByRequest(request, response);
132            Properties iViewConfig = setIViewConfig(request);
133            response.setHeader("pragma", "no-cache");
134    
135            setZoom(request, iViewConfig, nodeToBeDisplayed);
136            // move ? -> recalculate POI
137            if (iViewConfig.getProperty("MCR.Module-iview.move") != null)
138                movePOI(request, iViewConfig, nodeToBeDisplayed);
139    
140            int origWidth = getWidthOfImage(nodeToBeDisplayed);
141            int origHeight = getHeightOfImage(nodeToBeDisplayed);
142    
143            // thumbnail highlighting
144            float orig2ThumbSF = MCRIViewTools.computeScaleFactor(getWidthOfImage(nodeToBeDisplayed), getHeightOfImage(nodeToBeDisplayed),
145                    getWidthOfThumbnail(iViewConfig), getHeightOfThumbnail(iViewConfig));
146            int thumbPOIX = Math.round(getPOI(iViewConfig).x * orig2ThumbSF);
147            int thumbPOIY = Math.round(getPOI(iViewConfig).y * orig2ThumbSF);
148            int thumbWidth = Math.round(origWidth * orig2ThumbSF);
149            int thumbHeight = Math.round(origHeight * orig2ThumbSF);
150    
151            // generate jdom ############################
152            // ############# header
153            Element jdom = new Element("iview");
154            jdom.addContent(new Element("header"));
155            // header - current zoom value
156            jdom.getChild("header").addContent(new Element("currentZoom").setText(getZoom(iViewConfig)));
157            // header - thumbnail highlighting data
158            jdom.getChild("header").addContent(new Element("thumbHighLighting-X").setText(Integer.toString(thumbPOIX)));
159            jdom.getChild("header").addContent(new Element("thumbHighLighting-Y").setText(Integer.toString(thumbPOIY)));
160            jdom.getChild("header").addContent(new Element("thumbHighLighting-SF").setText(Float.toString(orig2ThumbSF)));
161            jdom.getChild("header").addContent(new Element("thumbWidth").setText(Integer.toString(thumbWidth)));
162            jdom.getChild("header").addContent(new Element("thumbHeight").setText(Integer.toString(thumbHeight)));
163            jdom.getChild("header").addContent(new Element("origWidth").setText(Integer.toString(origWidth)));
164            jdom.getChild("header").addContent(new Element("origHeight").setText(Integer.toString(origHeight)));
165    
166            // content
167            jdom.addContent(new Element("content").addContent(new Element("ownerID").setText(nodeToBeDisplayed.getOwnerID())));
168            // header - path
169            if (nodeToBeDisplayed instanceof MCRFile)
170                jdom.getChild("content").addContent(new Element("path").setText(nodeToBeDisplayed.getParent().getPath().toString()));
171            else
172                jdom.getChild("content").addContent(new Element("path").setText(nodeToBeDisplayed.getPath().toString()));
173            // header - fileToBeDisplayed
174            if (nodeToBeDisplayed instanceof MCRFile)
175                jdom.getChild("content").addContent(new Element("fileToBeDisplayed").setText(nodeToBeDisplayed.getAbsolutePath().toString()));
176            // header - parent
177            if (nodeToBeDisplayed.hasParent()) {
178                if ((nodeToBeDisplayed instanceof MCRFile) && (nodeToBeDisplayed.getParent().hasParent()))
179                    jdom.getChild("content").addContent(new Element("parent").setText(nodeToBeDisplayed.getParent().getParent().getPath()));
180                else if (nodeToBeDisplayed instanceof MCRDirectory)
181                    jdom.getChild("content").addContent(new Element("parent").setText(nodeToBeDisplayed.getParent().getPath().toString()));
182            }
183    
184            // ############# node list
185            if (nodeToBeDisplayed != null && nodeToBeDisplayed instanceof MCRFile)
186                jdom.getChild("content").addContent(getNodeList(nodeToBeDisplayed.getParent(), iViewConfig, request, response).detach());
187            else if (nodeToBeDisplayed != null && nodeToBeDisplayed instanceof MCRDirectory)
188                jdom.getChild("content").addContent(getNodeList((MCRDirectory) nodeToBeDisplayed, iViewConfig, request, response).detach());
189    
190            // forward jdom ############################
191            forwardJDOM(request, response, jdom);
192        }
193    
194        /**
195         * Returns the width of an image as int, if possible. If not -1 will be
196         * returned.
197         * 
198         * @param MCRFilesystemNode
199         * @return width of image as int or -1, if no width can be found.
200         */
201        private int getWidthOfImage(MCRFilesystemNode fsn) {
202            try {
203                if (fsn instanceof MCRFile) {
204                    Element addData = fsn.getAdditionalData("ImageMetaData");
205                    // in some unknown circumstances this value is empty, so create
206                    // again
207                    if (addData == null) {
208                        MCRImgCacheCommands.cacheFile((MCRFile) fsn, false);
209                        addData = fsn.getAdditionalData("ImageMetaData");
210                    }
211                    String widthString = addData.getChild("imageSize").getChildText("width");
212                    int width = Integer.parseInt(widthString);
213                    return width;
214                } else
215                    return -1;
216            } catch (NumberFormatException nfe) {
217                LOGGER.warn("", nfe);
218                return -1;
219            } catch (IOException ioe) {
220                LOGGER.warn("", ioe);
221                return -1;
222            } catch (JDOMException jde) {
223                LOGGER.warn("", jde);
224                return -1;
225            }
226        }
227    
228        /**
229         * Returns the height of an image as int, if possible. If not -1 will be
230         * returned.
231         * 
232         * @param MCRFilesystemNode
233         * @return height of image as int or -1, if no height can be found.
234         */
235        private int getHeightOfImage(MCRFilesystemNode fsn) {
236            try {
237                if (fsn instanceof MCRFile) {
238                    Element addData = fsn.getAdditionalData("ImageMetaData");
239                    if (addData == null)
240                        return -1;
241                    String widthString = addData.getChild("imageSize").getChildText("height");
242                    int width = Integer.parseInt(widthString);
243                    return width;
244                } else
245                    return -1;
246            } catch (NumberFormatException nfe) {
247                return -1;
248            } catch (IOException ioe) {
249                return -1;
250            } catch (JDOMException jde) {
251                return -1;
252            }
253        }
254    
255        /**
256         * Use this to put the value of a zoom to iViewConfig, if nothing is known.
257         * E.g. zoom can be "thumbnail", "fitToScreen", "0.7", "+0.1" etc. Zoom in
258         * (+) and zoom out (-) are calculated. "fitToScreen", "thumbnail" and
259         * "fitToWidth" are not calculated!
260         * 
261         * @param request
262         *            HttpRequest
263         * @param Properties
264         *            iViewConfig
265         */
266        private void setZoom(HttpServletRequest request, Properties iViewConfig, MCRFilesystemNode fsn) {
267    
268            String zoom = getZoom(iViewConfig);
269            float newZoom;
270    
271            // zoom out
272            if (zoom.substring(0, 1).equals("-")) {
273                // calculate zoom
274                float lastZoom = getZoomValue(iViewConfig);
275                newZoom = getZoomOut(lastZoom);
276                // save zoom
277                Properties zoomProp = new Properties();
278                zoomProp.put("XSL.MCR.Module-iview.navi.zoom.SESSION", java.lang.Float.toString(newZoom));
279                updateIViewConfig(iViewConfig, request, zoomProp, "setZoomOut=" + java.lang.Float.toString(newZoom)
280                        + "........................");
281                // save zoomValue
282                setZoomValue(request, newZoom, iViewConfig);
283                // move poi
284                Point poi = calculatePOIAfterZooming(iViewConfig, getPOI(iViewConfig), lastZoom, newZoom, fsn, true);
285                setPOI(request, iViewConfig, poi);
286            }
287            // zoom in
288            else if (zoom.substring(0, 1).equals(" ")) {
289                // calculate zoom
290                float lastZoom = getZoomValue(iViewConfig);
291                newZoom = getZoomIn(lastZoom);
292                // save zoom
293                Properties zoomProp = new Properties();
294                zoomProp.put("XSL.MCR.Module-iview.navi.zoom.SESSION", java.lang.Float.toString(newZoom));
295                updateIViewConfig(iViewConfig, request, zoomProp, "setZoomIn=" + java.lang.Float.toString(newZoom) + "........................");
296                // save zoomValue
297                setZoomValue(request, newZoom, iViewConfig);
298                // move poi
299                Point poi = calculatePOIAfterZooming(iViewConfig, getPOI(iViewConfig), lastZoom, newZoom, fsn, true);
300                setPOI(request, iViewConfig, poi);
301            }
302            // real given zoom value (is not "thumbnail | fitToScreen | fitToWidth")
303            else if (!(zoom.equals("thumbnail") || zoom.equals("fitToScreen") || zoom.equals("fitToWidth"))) {
304                float oldZoomValue = getZoomValue(iViewConfig);
305                newZoom = java.lang.Float.parseFloat(zoom);
306                newZoom = validateZoom(newZoom);
307                // zoom changed ?
308                if (oldZoomValue != newZoom) {
309                    // move poi
310                    Point poi = calculatePOIAfterZooming(iViewConfig, getPOI(iViewConfig), getZoomValue(iViewConfig), newZoom, fsn, true);
311                    setPOI(request, iViewConfig, poi);
312                }
313                setZoomValue(request, newZoom, iViewConfig);
314            }
315        }
316    
317        private Point calculatePOIAfterZooming(Properties iViewConfig, Point oldPOI, float oldZoom, float newZoom, MCRFilesystemNode fsn,
318                boolean log) {
319    
320            Point midpoint = new Point();
321            midpoint.x = Math.round(oldPOI.x + (getWidthOfBrowser(iViewConfig) / oldZoom) / 2);
322            midpoint.y = Math.round(oldPOI.y + (getHeightOfBrowser(iViewConfig) / oldZoom) / 2);
323    
324            Point newPOI = new Point();
325            newPOI.x = midpoint.x - Math.round(getWidthOfBrowser(iViewConfig) / newZoom / 2);
326            newPOI.y = midpoint.y - Math.round(getHeightOfBrowser(iViewConfig) / newZoom / 2);
327    
328            if (log)
329                LOGGER.debug("calculatePOIAfterZooming: new POI=(" + newPOI.x + "," + newPOI.y + ")");
330    
331            newPOI = validatePOI(newPOI, iViewConfig, fsn, true);
332    
333            return newPOI;
334        }
335    
336        private void setZoomValue(HttpServletRequest request, float zoomValue, Properties iViewConfig) {
337            Properties zoomValueProp = new Properties();
338            zoomValueProp.put("XSL.MCR.Module-iview.navi.zoomValue.SESSION", java.lang.Float.toString(zoomValue));
339            updateIViewConfig(iViewConfig, request, zoomValueProp, "setZoomValue=" + java.lang.Float.toString(zoomValue)
340                    + "........................");
341        }
342    
343        private float getZoomValue(Properties iViewConfig) {
344            if (iViewConfig.containsKey("MCR.Module-iview.navi.zoomValue")) {
345                return java.lang.Float.parseFloat(iViewConfig.getProperty("MCR.Module-iview.navi.zoomValue"));
346            } else {
347                return 1.0F;
348            }
349        }
350    
351        private float getZoomIn(float currentZoom) {
352            // calculate
353            float newZoom;
354            float zoomDistance = getZoomDistance();
355            newZoom = currentZoom + zoomDistance;
356            // validate if out of border
357            newZoom = validateZoom(newZoom);
358            // round
359            newZoom = roundZoom(newZoom, true);
360            LOGGER.debug("zoom in value calculated = " + java.lang.Float.toString(newZoom) + " (old: " + java.lang.Float.toString(currentZoom)
361                    + ")");
362            return newZoom;
363        }
364    
365        private float roundZoom(float zoom, boolean log) {
366            float zoomRounded = (float) (Math.round(zoom * 10)) / 10;
367            if (log)
368                LOGGER.debug("zoom value rounded=" + zoom + " (old:" + zoomRounded);
369            return zoomRounded;
370        }
371    
372        private float getZoomOut(float currentZoom) {
373            // calculate
374            float newZoom;
375            float zoomDistance = getZoomDistance();
376            newZoom = currentZoom - zoomDistance;
377            // validate if out of border
378            newZoom = validateZoom(newZoom);
379            // round
380            newZoom = roundZoom(newZoom, true);
381            LOGGER.debug("zoom out value calculated = " + java.lang.Float.toString(newZoom) + " (old: " + java.lang.Float.toString(currentZoom)
382                    + ")");
383            return newZoom;
384        }
385    
386        private float getZoomDistance() {
387            float zoomDist = java.lang.Float.parseFloat((MCRConfiguration.instance().getString("MCR.Module-iview.zoomDistance")));
388            LOGGER.debug("getZoomDistance=" + java.lang.Float.toString(zoomDist));
389            return zoomDist;
390        }
391    
392        private float validateZoom(float zoom) {
393            float verZoom = zoom;
394            if (verZoom < 0.1F)
395                verZoom = 0.1F;
396            else if (verZoom > 1.0F)
397                verZoom = 1.0F;
398            return verZoom;
399        }
400    
401        public void getImage(HttpServletRequest request, HttpServletResponse response) throws ServletException, FileNotFoundException,
402                IOException {
403    
404            // get right file
405            MCRFilesystemNode nodeToBeDisplayed = getMCRNodeByRequest(request, response);
406            MCRFile image = null;
407            if (nodeToBeDisplayed instanceof MCRFile)
408                image = (MCRFile) nodeToBeDisplayed;
409            else {
410                prepareErrorPage(request, response, "mode=getImage only works with MCRFile, "
411                        + "requested MCRFilesystemNode is not of type MCRFile");
412            }
413    
414            // get viewer properties
415            Properties iViewConfig = setIViewConfig(request);
416            String zoom = "";
417            int availableWidth = 0;
418            int availableHeight = 0;
419            int xPOI = 0;
420            int yPOI = 0;
421            // // direct call of getImage to get thumbnail
422            if (request.getParameter("XSL.MCR.Module-iview.navi.zoom") != null
423                    && request.getParameter("XSL.MCR.Module-iview.navi.zoom").equals("thumbnail")) {
424                zoom = "thumbnail";
425            } // // call from IView
426            else {
427                zoom = getZoom(iViewConfig);
428                availableWidth = getAvailableWidth(zoom, nodeToBeDisplayed, iViewConfig);
429                availableHeight = getAvailableHeight(zoom, nodeToBeDisplayed, iViewConfig);
430                LOGGER.debug("requested zoomFactor=" + zoom);
431                // //// get ROI (Region of interest)
432                xPOI = getPOI(iViewConfig).x;
433                yPOI = getPOI(iViewConfig).y;
434            }
435    
436            // put image in requested size to output stream
437            ServletOutputStream out = response.getOutputStream();
438            response.setContentType("image/jpeg");
439            MCRImgService imgService = new MCRImgService();
440            Stopwatch timer = new Stopwatch();
441            timer.start();
442    
443            if (zoom.equals("thumbnail")) {
444                int thumbnailWidth = getWidthOfThumbnail(iViewConfig);
445                int thumbnailHeight = getHeightOfThumbnail(iViewConfig);
446                imgService.getImage(image, thumbnailWidth, thumbnailHeight, out, MCRImgService.ScaleMode.normal);
447                timer.stop();
448                LOGGER.debug("finished getting image with zoom=thumbnail from ");
449            } else if (zoom.equals("fitToWidth")) {
450    
451                if (scrollBar(iViewConfig)) {
452                    imgService.getImage(image, availableWidth, availableWidth, out, MCRImgService.ScaleMode.fitWidth);
453                } else
454                    imgService.getImage(image, xPOI, yPOI, availableWidth, availableHeight, out);
455                
456                float zoomValue = imgService.getScaleFactor();
457                setZoomValue(request, zoomValue, iViewConfig);
458                LOGGER.debug("finished getting image with zoom=fitToWidth(" + zoomValue + ") from ");
459            } else if (zoom.equals("fitToScreen")) {
460                imgService.getImage(image, availableWidth, availableHeight, out, MCRImgService.ScaleMode.normal);
461                float zoomValue = imgService.getScaleFactor();
462                setZoomValue(request, zoomValue, iViewConfig);
463                LOGGER.debug("finished getting image with zoom=fitToScreen(" + zoomValue + ") from ");
464            } // normal number (10 or 20...)
465            else {
466                float zoomValue = java.lang.Float.parseFloat(zoom);
467                LOGGER.debug("imgService.getImage(image, xPOI(" + xPOI + "), yPOI(" + yPOI + "), " + "availableWidth(" + availableWidth
468                        + "), availableHeight(" + availableHeight + "), zoomValue(" + zoomValue + "), out);");
469                imgService.getImage(image, xPOI, yPOI, availableWidth, availableHeight, zoomValue, out);
470                timer.stop();
471                LOGGER.debug("finished getting image with real given zoom=" + zoomValue + " from ");
472            }
473            out.flush();
474            out.close();
475    
476        }
477    
478        private int getAvailableWidth(String zoom, MCRFilesystemNode nodeToBeDisplayed, Properties iViewConfig) {
479            if (scrollBar(iViewConfig)) {
480                if (zoom.equals("fitToWidth") || zoom.equals("fitToScreen")) {
481                    return getWidthOfBrowser(iViewConfig);
482                } // normal number (10 or 20...)
483                else {
484                    return getWidthOfImage(nodeToBeDisplayed);
485                }
486            } else {
487                return getWidthOfBrowser(iViewConfig);
488            }
489        }
490    
491        private int getAvailableHeight(String zoom, MCRFilesystemNode nodeToBeDisplayed, Properties iViewConfig) {
492            if (scrollBar(iViewConfig)) {
493                if (zoom.equals("fitToWidth")) {
494                    return getHeightOfImage(nodeToBeDisplayed);
495                } else if (zoom.equals("fitToScreen")) {
496                    return getHeightOfBrowser(iViewConfig);
497                }
498                // normal number (10 or 20...)
499                else
500                    return getHeightOfImage(nodeToBeDisplayed);
501            } else {
502                return getHeightOfBrowser(iViewConfig);
503            }
504        }
505    
506        private boolean scrollBar(Properties iViewConfig) {
507            if (!iViewConfig.getProperty("MCR.Module-iview.scrollBars", "true").equals("true"))
508                return false;
509            else
510                return true;
511        }
512    
513        private int getHeightOfThumbnail(Properties iViewConfig) {
514            int thumbnailHeight = Integer.parseInt(iViewConfig.getProperty("MCR.Module-iview.thumbnail.size.height"));
515            return thumbnailHeight;
516        }
517    
518        private int getWidthOfThumbnail(Properties iViewConfig) {
519            int thumbnailWidth = Integer.parseInt(iViewConfig.getProperty("MCR.Module-iview.thumbnail.size.width"));
520            return thumbnailWidth;
521        }
522    
523        private String getZoom(Properties iViewConfig) {
524            return iViewConfig.getProperty("MCR.Module-iview.navi.zoom");
525        }
526    
527        private int getHeightOfBrowser(Properties iViewConfig) {
528            return Integer.parseInt(iViewConfig.getProperty("browser.res.height").toString());
529        }
530    
531        private int getWidthOfBrowser(Properties iViewConfig) {
532            return Integer.parseInt(iViewConfig.getProperty("browser.res.width").toString());
533        }
534    
535        public boolean stringNotEmpty(String string) {
536            if (string != null && !string.equals(""))
537                return true;
538            else
539                return false;
540        }
541    
542        /**
543         * generates an JDOM-Element containing a list of supported children of a
544         * given node
545         */
546        private Element getNodeList(MCRDirectory dir, Properties iViewConfig, HttpServletRequest request, HttpServletResponse response)
547                throws IOException, ServletException, JDOMException {
548            // get cached file node list from MCRSession OR init new one
549            MCRSession session = MCRSessionMgr.getCurrentSession();
550            String cacheObjKey = "IView.FileNodesList";
551            Object cacheObj = session.get(cacheObjKey);
552            MCRCache cachedFileNodeList;
553            // // init new one
554            if (cacheObj == null) {
555                cachedFileNodeList = new MCRCache(10, "IViewFileNodeList in MCRSession,session=" + session.getID());
556                session.put(cacheObjKey, cachedFileNodeList);
557            } // get cached one
558            else
559                cachedFileNodeList = (MCRCache) cacheObj;
560            String cachedFileNodeListID = dir.getID();
561    
562            // get node list from cache
563            if (cachedFileNodeList.get(cachedFileNodeListID) != null
564                    && !stringNotEmpty(request.getParameter("XSL.MCR.Module-iview.defaultSort.SESSION"))) {
565                Element nodeList = (Element) cachedFileNodeList.get(cachedFileNodeListID);
566                return nodeList;
567            }
568            // generate node list and put to cache
569            else {
570                String sort = iViewConfig.getProperty("MCR.Module-iview.defaultSort").toString();
571                String sortOrder = iViewConfig.getProperty("MCR.Module-iview.defaultSort.order").toString();
572                int sortValue = 0;
573                int orderValue = 0;
574                if (sort.equals("name"))
575                    sortValue = 1;
576                else if (sort.equals("size"))
577                    sortValue = 2;
578                else if (sort.equals("lastModified"))
579                    sortValue = 3;
580                if (sortOrder.equals("ascending"))
581                    orderValue = 4;
582                else
583                    orderValue = 5;
584                LOGGER.debug("sort nodes by " + sort + ", order=" + sortOrder);
585                MCRFileNodeComparator comp = new MCRFileNodeComparator(sortValue, orderValue);
586                // get nodes
587                Element root = new Element("nodes");
588                LOGGER.debug("start to get children list");
589                MCRFilesystemNode[] children = dir.getChildren(comp);
590                LOGGER.debug("finsihshed getting children list");
591    
592                LOGGER.debug("start to go throug children list");
593                for (int i = 0; i < children.length; i++) {
594                    if (getSupport(children[i])) {
595                        Element node = new Element("node");
596                        node.setAttribute("ID", children[i].getID());
597                        root.addContent(node);
598                        addChild(node, "name", children[i].getName());
599                        addChild(node, "size", String.valueOf(children[i].getSize()));
600                        addDate(node, "lastModified", children[i].getLastModified());
601                        if (children[i] instanceof MCRFile) {
602                            node.setAttribute("type", "file");
603                            MCRFile file = (MCRFile) (children[i]);
604                            addChild(node, "contentType", file.getContentTypeID());
605                            addChild(node, "md5", file.getMD5());
606                            addChild(node, "label", file.getLabel());
607                        } else
608                            node.setAttribute("type", "directory");
609                    }
610                }
611                LOGGER.debug("finished to go throug children list");
612                // cache
613                cachedFileNodeList.put(cachedFileNodeListID, root);
614                LOGGER.debug("finished to get node list for" + dir.getName());
615                return root;
616            }
617        }
618    
619        private void addChild(Element parent, String itemName, String content) {
620            if ((content == null) || (content.trim().length() == 0))
621                return;
622            parent.addContent(new Element(itemName).addContent(content.trim()));
623        }
624    
625        private void addDate(Element parent, String type, GregorianCalendar date) {
626            Element xDate = new Element("date");
627            parent.addContent(xDate);
628    
629            xDate.setAttribute("type", type);
630    
631            String time = dateFormatter.format(date.getTime());
632    
633            xDate.setAttribute("format", dateFormat);
634            xDate.addContent(time);
635        }
636    
637        public void prepareErrorPage(HttpServletRequest request, HttpServletResponse response, String errorMessage) throws IOException,
638                ServletException {
639            LOGGER.error(errorMessage);
640            generateErrorPage(request, response, HttpServletResponse.SC_BAD_REQUEST, errorMessage, new MCRException(errorMessage), false);
641            return;
642        }
643    
644        private Point getPOI(Properties iViewConfig) {
645            int x = 0;
646            int y = 0;
647    
648            if (!scrollBar(iViewConfig)) {
649                if (iViewConfig.getProperty("MCR.Module-iview.roi.xpos") != null
650                        || iViewConfig.getProperty("MCR.Module-iview.roi.ypos") != null) {
651                    x = Integer.parseInt(iViewConfig.getProperty("MCR.Module-iview.roi.xpos"));
652                    y = Integer.parseInt(iViewConfig.getProperty("MCR.Module-iview.roi.ypos"));
653                }
654            }
655    
656            return new Point(x, y);
657        }
658    
659        public void movePOI(HttpServletRequest request, Properties iViewConfig, MCRFilesystemNode fsn) {
660    
661            Point point = getPOI(iViewConfig);
662    
663            // position = 0,0
664            if (iViewConfig.getProperty("MCR.Module-iview.move").equals("reset")) {
665                LOGGER.debug("MCR.Module-iview.move=reset received -> xpos and ypos = 0");
666                point.x = 0;
667                point.y = 0;
668            } // new position to be calculated
669            else {
670                // get distance to be moved
671                int deltaX = getXDistance(iViewConfig, true);
672                int deltaY = getYDistance(iViewConfig, true);
673    
674                getZoomValue(iViewConfig);
675    
676                if (iViewConfig.getProperty("MCR.Module-iview.move").equals("up")) {
677                    point.y = point.y - deltaY;
678                    LOGGER.debug("move up, new ROI=" + Integer.toString(point.x) + "," + Integer.toString(point.y) + "...");
679                } else if (iViewConfig.getProperty("MCR.Module-iview.move").equals("right")) {
680                    point.x = point.x + deltaX;
681                    LOGGER.debug("move right, new ROI=" + Integer.toString(point.x) + "," + Integer.toString(point.y) + "...");
682                } else if (iViewConfig.getProperty("MCR.Module-iview.move").equals("down")) {
683                    point.y = point.y + deltaY;
684                    LOGGER.debug("move down, new ROI=" + Integer.toString(point.x) + "," + Integer.toString(point.y) + "...");
685                } else if (iViewConfig.getProperty("MCR.Module-iview.move").equals("left")) {
686                    point.x = point.x - deltaX;
687                    LOGGER.debug("move left, new ROI=" + Integer.toString(point.x) + "," + Integer.toString(point.y) + "...");
688                } else if (iViewConfig.getProperty("MCR.Module-iview.move").equals("draged")) {
689                    LOGGER.debug("move by draging, old ROI=" + Integer.toString(point.x) + "," + Integer.toString(point.y) + "...");
690                    point.x = point.x - deltaX;
691                    point.y = point.y - deltaY;
692                    LOGGER.debug("move by draging, new ROI=" + Integer.toString(point.x) + "," + Integer.toString(point.y) + "...");
693                }
694                // verify
695                point = validatePOI(point, iViewConfig, fsn, true);
696            }
697            setPOI(request, iViewConfig, point);
698        }
699    
700        private void setPOI(HttpServletRequest request, Properties iViewConfig, Point point) {
701            // save
702            Properties roi = new Properties();
703            roi.put("XSL.MCR.Module-iview.roi.xpos.SESSION", Integer.toString(point.x));
704            roi.put("XSL.MCR.Module-iview.roi.ypos.SESSION", Integer.toString(point.y));
705            updateIViewConfig(iViewConfig, request, roi, "save POI(" + Integer.toString(point.x) + "," + Integer.toString(point.y) + ")");
706        }
707    
708        private int getXDistance(Properties iViewConfig, boolean log) {
709            int deltaX = 0;
710            if (iViewConfig.getProperty("MCR.Module-iview.move") != null && iViewConfig.getProperty("MCR.Module-iview.move").equals("draged")) {
711                // x draged distance
712                if (iViewConfig.containsKey("MCR.Module-iview.move.distanceX"))
713                    deltaX = Integer.parseInt(iViewConfig.getProperty("MCR.Module-iview.move.distanceX").toString());
714            } else {
715                // browser area
716                deltaX = getWidthOfBrowser(iViewConfig);
717            }
718            // adaptated to current zoom
719            // float deltaXAdap = deltaX/getZoomValue(iViewConfig);
720            // round
721            // deltaX = Math.round(deltaXAdap);
722            if (log)
723                LOGGER.debug("x-Distance ==> orig " + deltaX);
724            deltaX = (int) (deltaX / getZoomValue(iViewConfig));
725            if (log)
726                LOGGER.debug("x-Distance ==> browser resol.(" + getWidthOfBrowser(iViewConfig) + ") / " + "currentZoomValue("
727                        + getZoomValue(iViewConfig) + ") = " + getWidthOfBrowser(iViewConfig) / getZoomValue(iViewConfig) + " -> rounded = "
728                        + deltaX);
729    
730            return deltaX;
731        }
732    
733        private int getYDistance(Properties iViewConfig, boolean log) {
734            int deltaY = 0;
735            if (iViewConfig.getProperty("MCR.Module-iview.move") != null && iViewConfig.getProperty("MCR.Module-iview.move").equals("draged")) {
736                // x draged distance
737                if (iViewConfig.containsKey("MCR.Module-iview.move.distanceY"))
738                    deltaY = Integer.parseInt(iViewConfig.getProperty("MCR.Module-iview.move.distanceY").toString());
739            } else {
740                // browser area
741                deltaY = getHeightOfBrowser(iViewConfig);
742            }
743            // adaptated to current zoom
744            // float deltaYAdap = deltaY/getZoomValue(iViewConfig);
745            // round
746            // deltaY = Math.round(deltaYAdap);
747            if (log)
748                LOGGER.debug("y-Distance ==> orig " + deltaY);
749            deltaY = (int) (deltaY / getZoomValue(iViewConfig));
750            if (log)
751                LOGGER.debug("y-Distance ==> browser resol.(" + getHeightOfBrowser(iViewConfig) + ") / " + "currentZoomValue("
752                        + getZoomValue(iViewConfig) + ") = " + getHeightOfBrowser(iViewConfig) / getZoomValue(iViewConfig) + " -> rounded = "
753                        + deltaY);
754            return deltaY;
755        }
756    
757        /**
758         * Verify if ROI is right and/or below out of image
759         * 
760         * @param point
761         */
762        public Point validatePOI(Point point, Properties iViewConfig, MCRFilesystemNode fsn, boolean log) {
763            Point valPoint = new Point(point.x, point.y);
764            int width = getWidthOfImage(fsn);
765            int height = getHeightOfImage(fsn);
766    
767            // out on right side
768            if (width > -1) {
769                int xDistance = getXDistance(iViewConfig, false);
770                int widthOfImage = getWidthOfImage(fsn);
771                int outOfBorder = (valPoint.x + xDistance) - widthOfImage;
772    
773                if (outOfBorder > 0) {
774                    if (log)
775                        LOGGER.debug("validatePOI: found x=" + valPoint.x + " which is right out --> reset to " + (valPoint.x - outOfBorder));
776                    valPoint.x = valPoint.x - outOfBorder;
777                }
778            }
779            // out on below side
780            if (height > -1) {
781                int outOfBorder = (valPoint.y + getYDistance(iViewConfig, false)) - getHeightOfImage(fsn);
782                if (outOfBorder > 0) {
783                    if (log)
784                        LOGGER.debug("validatePOI: found y=" + valPoint.y + " which is below out --> reset to " + (valPoint.y - outOfBorder));
785                    valPoint.y = valPoint.y - outOfBorder;
786                }
787            }
788            // out on left side
789            if (valPoint.x < 0) {
790                if (log)
791                    LOGGER.debug("validatePOI: found x=" + valPoint.x + " which is left out  --> reset to 0");
792                valPoint.x = 0;
793            }
794            // out on above side
795            if (valPoint.y < 0) {
796                if (log)
797                    LOGGER.debug("validatePOI: found y=" + valPoint.y + " which is above out  --> reset to 0");
798                valPoint.y = 0;
799            }
800    
801            return valPoint;
802        }
803    
804        public void forwardJDOM(HttpServletRequest request, HttpServletResponse response, Element elem) throws IOException {
805    
806            // name of target xsl
807            if (getProperty(request, "XSL.Style") == null)
808                request.setAttribute("XSL.Style", "iview");
809            // SAXBuilder builder = new SAXBuilder();
810            Element root = new Element("mcr-module");
811            root.addContent(elem);
812            Document jdom = new Document(root);
813            getLayoutService().doLayout(request, response, jdom);
814        }
815    
816        /**
817         * @param node
818         *            - The MCRFilesystemNode to be verified
819         * @return true if module imaging supports this MCRFile or at least one
820         *         MCRFile within MCRDirectory, false if no support
821         */
822        public boolean getSupport(MCRFilesystemNode node) {
823            String suppContTypes = new String(MCRConfiguration.instance().getString("MCR.Module-iview.SupportedContentTypes"));
824            if (node instanceof MCRDirectory) {
825                List<MCRFile> list = new Vector<MCRFile>();
826                getFirstSupportedFile(list, suppContTypes, (MCRDirectory) node);
827                if (list.size() == 1)
828                    return true;
829                else
830                    return false;
831            } else {
832                MCRFile file = (MCRFile) node;
833                if (getFileSupport(file))
834                    return true;
835                else
836                    return false;
837            }
838        }
839    
840        public boolean getFileSupport(MCRFile file) {
841            boolean support = false;
842            String suppContTypes = new String(MCRConfiguration.instance().getString("MCR.Module-iview.SupportedContentTypes"));
843            if (file instanceof MCRFile && suppContTypes.indexOf(file.getContentTypeID()) > -1)
844                support = true;
845            return support;
846        }
847    
848        public void getFirstSupportedFile(List<MCRFile> list, String contentType, MCRDirectory rootNode) {
849            MCRFilesystemNode[] nodes = rootNode.getChildren();
850            int i = 0;
851            while ((i < nodes.length) && list.size() != 1) {
852                if (nodes[i] instanceof MCRDirectory) {
853                    MCRDirectory dir = (MCRDirectory) (nodes[i]);
854                    getFirstSupportedFile(list, contentType, dir);
855                } else {
856                    MCRFile file = (MCRFile) (nodes[i]);
857                    if (contentType.indexOf(file.getContentTypeID()) > -1) {
858                        list.add(file);
859                    }
860                }
861                i++;
862            }
863        }
864    
865        public MCRFilesystemNode getMCRNodeByRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException,
866                IOException {
867            String reqPath = request.getPathInfo();
868            StringTokenizer st = new StringTokenizer(reqPath, "/");
869            String ownerID = st.nextToken(); // ownerID
870    
871            // get node
872            MCRFilesystemNode root = MCRFilesystemNode.getRootNode(ownerID);
873            MCRDirectory dirOwnerID = (MCRDirectory) root;
874            MCRFilesystemNode nodeToBeDisplayed = null;
875            if (reqPath.length() - 1 == ownerID.length()) { // root of derivate
876                nodeToBeDisplayed = dirOwnerID;
877            } // some subfolder or file
878            else {
879                int pos = ownerID.length() + 1;
880                String path = reqPath.substring(pos); // path of node without
881                // ownerID
882                nodeToBeDisplayed = dirOwnerID.getChildByPath(path);
883            }
884            if (nodeToBeDisplayed != null && !getSupport(nodeToBeDisplayed))
885                prepareErrorPage(request, response, "Error: MCRFilesystemNode=" + nodeToBeDisplayed.getID()
886                        + "is not supported by Module-IView");
887            return nodeToBeDisplayed;
888        }
889    
890        public void getMetadata(HttpServletRequest request, HttpServletResponse response, StringTokenizer st) throws ServletException,
891                IOException {
892    
893            // type == "support"
894            // verifies if a Derrivat's main file is supported
895            // and responses <support mainfile="...">true|false</support>
896            if (request.getParameter("type").equals("support")) {
897                // get name main file
898                String derivID = st.nextToken();
899                MCRDerivate deriv = new MCRDerivate();
900                deriv.receiveFromDatastore(derivID);
901                String nameOfMainFile = deriv.getDerivate().getInternals().getMainDoc();
902                // verify support
903                if (nameOfMainFile != null && !nameOfMainFile.equals("")) {
904                    MCRDirectory root = (MCRDirectory) MCRFilesystemNode.getRootNode(derivID);
905                    // get main file
906                    MCRFile mainFile = (MCRFile) root.getChildByPath(nameOfMainFile);
907                    if (getFileSupport(mainFile)) {
908                        forwardJDOM(request, response, new Element("support").setAttribute("mainFile", mainFile.getAbsolutePath()).setText(
909                                "true"));
910                    } else
911                        forwardJDOM(request, response, new Element("support").setText("false"));
912                } else
913                    forwardJDOM(request, response, new Element("support").setText("false"));
914            }
915        }
916    
917        public Properties getIViewConfig(HttpServletRequest request) {
918            LOGGER.debug("getting IViewConfig....................................");
919            try {
920                return setIViewConfig(request);
921            } catch (UnsupportedEncodingException e) {
922                LOGGER.error("Error while getting IViewConfig from request.", e);
923                return null;
924            }
925        }
926    
927        public void updateIViewConfig(Properties iViewConfig, HttpServletRequest request, Properties dateUp, String logMessage) {
928            LOGGER.debug("updating iViewConfig (" + logMessage + ").....................................");
929    
930            MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
931            Enumeration<?> e = dateUp.keys();
932            while (e.hasMoreElements()) {
933                String key = e.nextElement().toString();
934                String value = dateUp.get(key).toString();
935    
936                if (key.startsWith("XSL.")) {
937                    // store parameter in session 'cause it ends with *.SESSION
938                    if (key.endsWith(".SESSION")) {
939                        String key4IViewConfig = key.substring(4, key.length() - 8);
940                        String key4Session = key.substring(0, key.length() - 8);
941    
942                        iViewConfig.put(key4IViewConfig, value);
943                        mcrSession.put(key4Session, value);
944                        LOGGER.debug("update IViewConfig: found " + key + "=" + dateUp.getProperty(key)
945                                + " that should be saved in session, safed " + key4Session + "=" + value);
946                    } else {
947                        iViewConfig.put(key.substring(4), value);
948                    }
949                }
950    
951            }
952            // return iViewConfig;
953        }
954    
955        @SuppressWarnings("unchecked")
956        public Properties setIViewConfig(HttpServletRequest request) throws UnsupportedEncodingException {
957    
958            LOGGER.debug("setting IViewConfig....................................");
959    
960            // PROPERTIES: Read all properties from mycore.properties
961            Properties iViewConfig = (Properties) (MCRConfiguration.instance().getProperties().clone());
962    
963            // SESSION: Read all *.xsl attributes that are stored in the browser
964            MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
965            for (Map.Entry<Object, Object> entry : mcrSession.getMapEntries()) {
966                String key = entry.getKey().toString();
967                if (key.startsWith("XSL.")) {
968                    iViewConfig.put(key.substring(4), entry.getValue());
969                }
970            }
971    
972            // HTTP-REQUEST-PARAMETER: Read all *.xsl attributes from the client
973            for (Enumeration e = request.getParameterNames(); e.hasMoreElements();) {
974                String name = (String) (e.nextElement());
975    
976                if (name.startsWith("XSL.")) {
977                    if (!name.endsWith(".SESSION")) {
978                        iViewConfig.put(name.substring(4), request.getParameter(name));
979                    } // store parameter in session if ends with *.SESSION
980                    else {
981                        iViewConfig.put(name.substring(4, name.length() - 8), request.getParameter(name));
982                        if (mcrSession != null) {
983                            mcrSession.put(name.substring(0, name.length() - 8), request.getParameter(name));
984                            LOGGER.debug("found HTTP-Req.-Parameter " + name + "=" + request.getParameter(name)
985                                    + " that should be saved in session, safed " + name.substring(0, name.length() - 8) + "="
986                                    + request.getParameter(name));
987                        }
988    
989                    }
990                }
991            }
992    
993            // SERVLETS-REQUEST-ATTRIBUTES: Read all *.xsl attributes provided by
994            // the invoking servlet
995            for (Enumeration e = request.getAttributeNames(); e.hasMoreElements();) {
996                String name = (String) (e.nextElement());
997                if (name.startsWith("XSL.")) {
998                    if (!name.endsWith(".SESSION")) {
999                        iViewConfig.put(name.substring(4), request.getAttribute(name));
1000                    } // store parameter in session if ends with *.SESSION
1001                    else {
1002                        iViewConfig.put(name.substring(4, name.length() - 8), request.getAttribute(name));
1003                        if (mcrSession != null) {
1004                            mcrSession.put(name.substring(0, name.length() - 8), request.getAttribute(name));
1005                            LOGGER.debug("found Req.-Attribut " + name + "=" + request.getAttribute(name)
1006                                    + " that should be saved in session, safed " + name.substring(0, name.length() - 8) + "="
1007                                    + request.getAttribute(name));
1008                        }
1009    
1010                    }
1011                }
1012    
1013            }
1014            LOGGER.debug("+++++++++++++++++++++++++++++++++++++++++++++ ");
1015    
1016            // ensure iview session is not timed out
1017            verifyIViewConfig(iViewConfig, request);
1018    
1019            return iViewConfig;
1020        }
1021    
1022        /**
1023         * Ensure iview session is not timed out
1024         * 
1025         * @param iViewConfig
1026         * @param request
1027         *            TODO
1028         * @throws UnsupportedEncodingException
1029         */
1030        private void verifyIViewConfig(Properties iViewConfig, HttpServletRequest request) throws UnsupportedEncodingException {
1031            Properties dateUp = new Properties();
1032            if (!iViewConfig.containsKey("MCR.Module-iview.navi.zoom"))
1033                dateUp.put("XSL.MCR.Module-iview.navi.zoom.SESSION", "fitToScreen");
1034            if (!iViewConfig.containsKey("MCR.Module-iview.display"))
1035                dateUp.put("XSL.MCR.Module-iview.display.SESSION", "normal");
1036            if (!iViewConfig.containsKey("MCR.Module-iview.style"))
1037                dateUp.put("XSL.MCR.Module-iview.style.SESSION", "image");
1038            if (!iViewConfig.containsKey("MCR.Module-iview.embedded"))
1039                dateUp.put("XSL.MCR.Module-iview.embedded.SESSION", "false");
1040            if (!iViewConfig.containsKey("MCR.Module-iview.lastEmbeddedURL"))
1041                dateUp.put("XSL.MCR.Module-iview.lastEmbeddedURL.SESSION", MCRServlet.getBaseURL());
1042    
1043            updateIViewConfig(iViewConfig, request, dateUp, "session timed out, reset essential parameters");
1044        }
1045    
1046        @SuppressWarnings("unchecked")
1047        public void printRequest(HttpServletRequest request) {
1048            LOGGER.debug("############################################# ");
1049            for (Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements();) {
1050                String name = e.nextElement();
1051                LOGGER.debug("HEADER: " + name + "=" + request.getHeader(name));
1052            }
1053            LOGGER.debug("start print Request-Parameters ############## ");
1054            for (Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
1055                String name = e.nextElement();
1056                LOGGER.debug("" + name + "=" + request.getParameter(name));
1057            }
1058            LOGGER.debug("finished printing Request-Parameters ######### ");
1059            LOGGER.debug("                                               ");
1060    
1061            LOGGER.debug("start print Request-Attributes ################ ");
1062            for (Enumeration<String> e = request.getAttributeNames(); e.hasMoreElements();) {
1063                String name = e.nextElement();
1064                LOGGER.debug("" + name + "=" + request.getAttribute(name));
1065            }
1066            LOGGER.debug("finished printing Request-Attributes ########## ");
1067            LOGGER.debug("############################################### ");
1068        }
1069    
1070        public void printIViewConfig(Properties iViewConfig) {
1071            LOGGER.debug("############################################# ");
1072            LOGGER.debug("start printing IViewCOnfig-Parameters ####### ");
1073    
1074            for (Object key : iViewConfig.keySet()) {
1075                String name = key.toString();
1076                String value = iViewConfig.getProperty(name);
1077                if (name.startsWith("MCR.Module-iview") || name.startsWith("browser"))
1078                    LOGGER.debug(name + "=" + value);
1079            }
1080            LOGGER.debug("finished printing IViewCOnfig-Parameters ###### ");
1081            LOGGER.debug("############################################### ");
1082        }
1083    
1084    }