Package org.exoplatform.portal.application

Source Code of org.exoplatform.portal.application.ResourceRequestFilter

/**
* Copyright (C) 2009 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.exoplatform.portal.application;

import org.exoplatform.commons.utils.*;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.web.AbstractFilter;
import org.exoplatform.portal.resource.ResourceRenderer;
import org.exoplatform.portal.resource.SkinService;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;

import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

import javax.imageio.ImageIO;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ResourceRequestFilter extends AbstractFilter
{

   protected static Log log = ExoLogger.getLogger(ResourceRequestFilter.class);

   private static final Charset UTF_8 = Charset.forName("UTF-8");

   private FilterConfig cfg;

   private ImageType[] imageTypes = ImageType.values();

   private ConcurrentMap<String, FutureTask<Image>> mirroredImageCache = new ConcurrentHashMap<String, FutureTask<Image>>();

   public static final String IF_MODIFIED_SINCE     = "If-Modified-Since";
   public static final String LAST_MODIFIED     = "Last-Modified";   
  
   public void afterInit(FilterConfig filterConfig)
   {
      cfg = filterConfig;
      log.info("Cache eXo Resource at client: " + !PropertyManager.isDevelopping());
   }

   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
      ServletException
   {
      HttpServletRequest httpRequest = (HttpServletRequest)request;
      final String uri = URLDecoder.decode(httpRequest.getRequestURI(), "UTF-8");
      final HttpServletResponse httpResponse = (HttpServletResponse)response;
      ExoContainer portalContainer = getContainer();
      final SkinService skinService = (SkinService) portalContainer.getComponentInstanceOfType(SkinService.class);
      long ifModifiedSince = httpRequest.getDateHeader(IF_MODIFIED_SINCE);

      //
      if (uri.endsWith(".css"))
      {
//     Check if cached resource has not been modifed, return 304 code     
         long cssLastModified = skinService.getLastModified(uri);
         if (isNotModified(ifModifiedSince, cssLastModified)) {
            httpResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
         }
         response.setContentType("text/css; charset=UTF-8");
        
         final OutputStream out = response.getOutputStream();
         final BinaryOutput output = new BinaryOutput()
         {
            public Charset getCharset()
            {
               return UTF_8;
            }
            public void write(byte b) throws IOException
            {
               out.write(b);
            }
            public void write(byte[] bytes) throws IOException
            {
               out.write(bytes);
            }
            public void write(byte[] bytes, int off, int len) throws IOException
            {
               out.write(bytes, off, len);
            }
         };
         ResourceRenderer renderer = new ResourceRenderer()
         {
            public BinaryOutput getOutput() throws IOException
            {
               return output;
            }
            public void setExpiration(long seconds)
            {
               if (seconds > 0)
               {
                  httpResponse.addHeader("Cache-Control", "max-age=" + seconds + ",s-maxage=" + seconds);
               }
               else
               {
                  httpResponse.setHeader("Cache-Control", "no-cache");
               }

               long lastModified = skinService.getLastModified(uri);
               processIfModified(lastModified, httpResponse);
            }
         };

         //
         try
         {
            skinService.renderCSS(renderer, uri);
            if (log.isDebugEnabled())
            {
               log.debug("Use a merged CSS: " + uri);
            }
         }
         catch (Exception e)
         {
            if (e instanceof SocketException)
            {
               //Should we print something/somewhere exception message
            }
            else
            {
               log.error("Could not render css " + uri, e);
               httpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
            }
         }
      }
      else
      {

         // Fast matching
         final int len = uri.length();
         if (len >= 7 && uri.charAt(len - 7) == '-' && uri.charAt(len - 6) == 'r' && uri.charAt(len - 5) == 't')
         {
            for (final ImageType imageType : imageTypes)
            {
               if (imageType.matches(uri))
               {
                  final String resource =
                     uri.substring(httpRequest.getContextPath().length(), len - 7) + uri.substring(len - 4);
                  FutureTask<Image> futureImg = mirroredImageCache.get(resource);
                  if (futureImg == null)
                  {
                     FutureTask<Image> tmp = new FutureTask<Image>(new Callable<Image>()
                     {
                        public Image call() throws Exception
                        {
                           InputStream in = cfg.getServletContext().getResourceAsStream(resource);
                           if (in == null)
                           {
                              return null;
                           }

                           //
                           BufferedImage img = ImageIO.read(in);
                           log.debug("Read image " + uri + " (" + img.getWidth() + "," + img.getHeight() + ")");
                           AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
                           tx.translate(-img.getWidth(null), 0);
                           AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
                           img = op.filter(img, null);
                           log.debug("Mirrored image " + uri + " (" + img.getWidth() + "," + img.getHeight() + ")");
                           ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
                           ImageIO.write(img, imageType.getFormat(), baos);
                           baos.close();
                           return new Image(imageType, baos.toByteArray());
                        }
                     });

                     //
                     futureImg = mirroredImageCache.putIfAbsent(resource, tmp);
                     if (futureImg == null)
                     {
                        futureImg = tmp;
                        futureImg.run();
                     }
                  }

                  //
                  try
                  {
                     Image img = futureImg.get();
                     if (img != null)
                     {
                        //Check if cached resource has not been modifed, return 304 code     
                        long imgLastModified = img.getLastModified();
                        if (isNotModified(ifModifiedSince, imgLastModified)) {
                           httpResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                           return;
                        }
                        httpResponse.setContentType(img.type.getMimeType());
                        httpResponse.setContentLength(img.bytes.length);
                        httpResponse.setHeader("Cache-Control", "max-age=2592000,s-maxage=2592000");
                        processIfModified(imgLastModified, httpResponse);
                       
                        OutputStream out = httpResponse.getOutputStream();
                        out.write(img.bytes);
                        out.close();
                     }
                     else
                     {
                        mirroredImageCache.remove(resource);
                        httpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
                     }
                     return;
                  }
                  catch (InterruptedException e)
                  {
                     // Find out what is relevant to do
                     e.printStackTrace();
                  }
                  catch (ExecutionException e)
                  {
                     // Cleanup
                     e.printStackTrace();
                     mirroredImageCache.remove(resource);
                  }
               }
            }
         }

         //
         if (!PropertyManager.isDevelopping())
         {
            httpResponse.addHeader("Cache-Control", "max-age=2592000,s-maxage=2592000");
         }
         else
         {
            if (uri.endsWith(".jstmpl") || uri.endsWith(".js"))
            {
               httpResponse.setHeader("Cache-Control", "no-cache");
            }
            if (log.isDebugEnabled())
               log.debug(" Load Resource: " + uri);
         }
         chain.doFilter(request, response);
      }
   }

   /**
    * Add Last-Modified Http header to HttpServetResponse
    */
   public void processIfModified(long lastModified, HttpServletResponse httpResponse) {
      httpResponse.setDateHeader(ResourceRequestFilter.LAST_MODIFIED, lastModified);
   }

   /**
    * If cached resource has not changed since date in http header (If_Modified_Since), return true
    * Else return false;
    * @param ifModifedSince - String, and HttpHeader element
    * @param lastModified
    * @param httpResponse
    * @return
    */
   public boolean isNotModified(long ifModifedSince, long lastModified) {
      if (!PropertyManager.isDevelopping()) {
         if (ifModifedSince >= lastModified) {
            return true;
         }
      }
      return false;
   }
  
   public void destroy()
   {
   }
}
TOP

Related Classes of org.exoplatform.portal.application.ResourceRequestFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.