001    /*
002    Copyright (c) 1996-2011, Damon Hart-Davis
003    All rights reserved.
004    
005    Redistribution and use in source and binary forms, with or without
006    modification, are permitted provided that the following conditions are
007    met:
008    
009      * Redistributions of source code must retain the above copyright
010        notice, this list of conditions and the following disclaimer.
011    
012      * Redistributions in binary form must reproduce the above copyright
013        notice, this list of conditions and the following disclaimer in the
014        documentation and/or other materials provided with the
015        distribution.
016    
017    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
018    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
019    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
020    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
021    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
022    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
024    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
025    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
027    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028    */
029    
030    package org.hd.d.pg2k.webSvr.virtualHosts.AlohaEarth;
031    
032    import java.io.IOException;
033    import java.io.OutputStream;
034    
035    import javax.servlet.ServletContext;
036    import javax.servlet.ServletException;
037    import javax.servlet.http.HttpServlet;
038    import javax.servlet.http.HttpServletRequest;
039    import javax.servlet.http.HttpServletResponse;
040    
041    import org.hd.d.pg2k.svrCore.CoreConsts;
042    import org.hd.d.pg2k.svrCore.ROByteArray;
043    import org.hd.d.pg2k.webSvr.exhibit.DataSourceBean;
044    
045    /**Servlet to serve dynamic Aloha Earth map image.
046     * This expects to be able to collect parameters with a AEParams object.
047     * <p>
048     * If the parameters are missing or out of bounds then a default image
049     * is produced.
050     * <p>
051     * The image produced is with the type indicated by DYN_MAP_IMG_TYPE,
052     * in an efficient (small), indexed format.
053     * <p>
054     * Images may be cached, possibly with SoftReference,
055     * though any such cache may need to be cleared when the
056     * exhibit set or meta data changes.
057     */
058    public final class AlohaEarthMapServlet extends HttpServlet
059        {
060        /**Respond to a GET request for the content served by this servlet.
061         *
062         * @param request The servlet request we are processing
063         * @param response The servlet response we are producing
064         *
065         * @exception IOException if an input/output error occurs
066         * @exception ServletException if a servlet error occurs
067         */
068        @Override
069        public void doGet(final HttpServletRequest request,
070                          final HttpServletResponse response)
071            throws IOException, ServletException
072            {
073            doAction(request, response);
074            }
075    
076        /**Respond to a HEAD request for the content served by this servlet.
077         *
078         * @param request The servlet request we are processing
079         * @param response The servlet response we are producing
080         *
081         * @exception IOException if an input/output error occurs
082         * @exception ServletException if a servlet error occurs
083         */
084        @Override
085        public void doHead(final HttpServletRequest request,
086                           final HttpServletResponse response)
087            throws IOException, ServletException
088            {
089            doAction(request, response);
090            }
091    
092        /**Respond to a GET/HEAD request for the content served by this servlet.
093         * Returns the exhibit data, with a correct MIME type.
094         *
095         * @param request The servlet request we are processing
096         * @param response The servlet response we are producing
097         *
098         * @exception IOException if an input/output error occurs
099         * @exception ServletException if a servlet error occurs
100         */
101        public void doAction(final HttpServletRequest request,
102                             final HttpServletResponse response)
103            throws IOException, ServletException
104            {
105            try
106                {
107                // Get/create encoded image ready to send to the browser.
108                final AEParams aeps = (new AEParams()).setRequest(request);
109                final ServletContext ctxt = getServletContext();
110                final DataSourceBean dsb = DataSourceBean.getApplicationInstance(ctxt);
111                final ROByteArray encodedImage = AEUtils.getAemfb(dsb).
112                    selectMapEncodedImage(aeps, ctxt);
113    
114                // Set the correct MIME type and result length.
115                response.setContentType(AEUtils.ETP.mimeType);
116                response.setContentLength(encodedImage.length());
117    
118                // Set some cacheing headers too.
119                // We let the image stay in cache for our standard `slackness'.
120                // These images may change with the exhibit set.
121                final int cacheLifetime = CoreConsts.DEFAULT_TEMPORAL_SLACKNESS_S * 1000;
122                response.setHeader("Cache-Control", "max-age="+(cacheLifetime/1000));
123                response.setDateHeader("Expires", System.currentTimeMillis() + cacheLifetime);
124    
125                // If this is a HEAD, then return without providing the body.
126                if("HEAD".equalsIgnoreCase(request.getMethod()))
127                    { return; }
128    
129                // Write entire map-fragment image in one go for efficiency.
130                final OutputStream os = response.getOutputStream();
131                os.write(encodedImage.toByteArray());
132                os.flush();
133                }
134            catch(final Exception e)
135                {
136                // Log the problem!
137                getServletContext().log("failed to generate Aloha Earth map image", e);
138                // Redirect back to original raw image in case of error.
139                // (This may force the browser to rescale the image)
140                // If our result is already committed this may be messy...
141                response.reset();
142                response.resetBuffer();
143                request.getRequestDispatcher(AEUtils.BASE_2D_EARTH_MAP_RRURL).
144                    forward(request, response);
145                return;
146                }
147            }
148    
149        /**Have the last-modified date be the AEP unchanged-since timestamp. */
150        @Override protected long getLastModified(final HttpServletRequest request)
151            {
152            try
153                {
154                final ServletContext ctxt = getServletContext();
155                final DataSourceBean dsb = DataSourceBean.getApplicationInstance(ctxt);
156                return(dsb.getAllExhibitProperties(-1).hashNotChangedSince);
157                }
158            catch(final IOException e)
159                { return(-1); /* "Not known/available" value. */ }
160            }
161    
162        /**Unique Serialisation class ID generated by http://random&#46;hd&#46;org/. */
163        private static final long serialVersionUID = -6552601635962150780L;
164        }