You are viewing a plain text version of this content. The canonical link for it is here.
Posted to portalapps-dev@portals.apache.org by wo...@apache.org on 2010/02/04 16:41:48 UTC
svn commit: r906529 - in
/portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet:
./ WebAppClasspathResourceServlet.java
Author: woonsan
Date: Thu Feb 4 15:41:48 2010
New Revision: 906529
URL: http://svn.apache.org/viewvc?rev=906529&view=rev
Log:
APA-29: Adding webapp classpath resource serving servlet.
TODO: Apply this to some portlet(s) such as googlemaps.
Added:
portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/
portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/WebAppClasspathResourceServlet.java (with props)
Added: portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/WebAppClasspathResourceServlet.java
URL: http://svn.apache.org/viewvc/portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/WebAppClasspathResourceServlet.java?rev=906529&view=auto
==============================================================================
--- portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/WebAppClasspathResourceServlet.java (added)
+++ portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/WebAppClasspathResourceServlet.java Thu Feb 4 15:41:48 2010
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.portals.gems.servlet;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Serves static resource files from the web application's classloader resources.
+ *
+ * <P>
+ * <EM>Note: This servlet could serve resources from the classpath insecurely.
+ * Please configure the init parameters for secure use as described below!</EM>
+ * </P>
+ *
+ * <P>This servlet has the ability to configure resource base path,
+ * allowed resource path prefix and postfix. This needs some configuration, which is described below. </P>
+ *
+ * <pre>
+ * <init-param>
+ * <param-name>resourceBasePath</param-name>
+ * <param-value>/META-INF/resources</param-value>
+ * </init-param>
+ * <init-param>
+ * <param-name>allowedResourcePathPrefixes</param-name>
+ * <param-value>/refdocs/</param-value>
+ * </init-param>
+ * <init-param>
+ * <param-name>allowedResourcePathPostfixes</param-name>
+ * <param-value>.gif, .jpg, .jpeg, .png, .ico, .js, .css</param-value>
+ * </init-param>
+ * </pre>
+ *
+ * <P> With the above configuration the following resource request path infos are valid:
+ * <UL>
+ * <LI>/icons/logo.gif --> classpath:/META-INF/resources/icons/logo.gif</LI>
+ * <LI>/javascript/init.js --> classpath:/META-INF/resources/javascript/init.js</LI>
+ * <LI>/refdocs/spec.txt --> classpath:/META-INF/resources/refdocs/spec.txt</LI>
+ * </UL>
+ * </P>
+ *
+ * <P> However, the following resource request path infos are forbidden:
+ * <UL>
+ * <LI>/icons/logo.bmp (because the path info doesn't start with <CODE>/refdocs/</CODE> and <CODE>.bmp</CODE> postfix is not allowed.)</LI>
+ * <LI>/designs/README (because the path info doesn't start with <CODE>/refdocs/</CODE> and no prefix does not match with this path info.</LI>
+ * </UL>
+ * </P>
+ *
+ * @version $Id$
+ */
+public class WebAppClasspathResourceServlet extends HttpServlet
+{
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String RESOURCE_BASE_PATH_PARAM = "resourceBasePath";
+
+ public static final String ALLOWED_RESOURCE_PATH_PREFIXES_PARAM = "allowedResourcePathPrefixes";
+
+ public static final String ALLOWED_RESOURCE_PATH_POSTFIXES_PARAM = "allowedResourcePathPostfixes";
+
+ private static final int BUF_SIZE = 4096;
+
+ private String resourceBasePath = "/META-INF/resources";
+
+ private String[] allowedResourcePathPrefixes = new String[0];
+
+ private String[] allowedResourcePathPostfixes = new String[] { ".gif", ".jpg", ".jpeg", ".png", ".ico", ".js", ".css" };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void init(ServletConfig config) throws ServletException
+ {
+ super.init(config);
+
+ String param = config.getInitParameter(RESOURCE_BASE_PATH_PARAM);
+ if (param != null)
+ {
+ resourceBasePath = param.trim();
+ }
+
+ param = config.getInitParameter(ALLOWED_RESOURCE_PATH_PREFIXES_PARAM);
+ if (param != null)
+ {
+ allowedResourcePathPrefixes = split(param, ", \t\r\n");
+ }
+
+ param = config.getInitParameter(ALLOWED_RESOURCE_PATH_POSTFIXES_PARAM);
+ if (param != null)
+ {
+ allowedResourcePathPostfixes = split(param, ", \t\r\n");
+ }
+ }
+
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ String path = getResourcePath(request);
+
+ if (path == null || "".equals(path.trim()))
+ {
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ if (!isDownloadableResource(path))
+ {
+ response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ String resourcePath = resourceBasePath + path;
+ URL resourceURL = null;
+
+ try
+ {
+ resourceURL = Thread.currentThread().getContextClassLoader().getResource(resourcePath);
+ }
+ catch (Throwable th)
+ {
+ }
+
+ if (resourceURL == null)
+ {
+ response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ InputStream is = null;
+ BufferedInputStream bis = null;
+ ServletOutputStream sos = null;
+ BufferedOutputStream bos = null;
+
+ try
+ {
+ long lastModified = 0L;
+ String protocol = resourceURL.getProtocol();
+
+ if ("file".equals(protocol))
+ {
+ File resourceFile = new File(resourceURL.toURI());
+ if (resourceFile.isFile())
+ {
+ lastModified = resourceFile.lastModified();
+ }
+ }
+
+ if ("jar".equals(protocol))
+ {
+ String jarFileOnlyURL = resourceURL.getFile();
+ int offset = jarFileOnlyURL.indexOf('!');
+ if (offset != -1) {
+ jarFileOnlyURL = jarFileOnlyURL.substring(0, offset);
+ }
+ File jarFile = new File(URI.create(jarFileOnlyURL));
+ if (jarFile.isFile())
+ {
+ lastModified = jarFile.lastModified();
+ }
+ }
+
+ if (lastModified > 0L)
+ {
+ response.setDateHeader("Last-Modified", lastModified);
+ long expires = (System.currentTimeMillis() - lastModified);
+ response.setDateHeader("Expires", expires + System.currentTimeMillis());
+ response.setHeader("Cache-Control", "max-age=" + (expires / 1000));
+ }
+
+ String mimeType = getServletContext().getMimeType(resourcePath);
+ if (mimeType == null)
+ {
+ mimeType = "application/octet-stream";
+ }
+
+ response.setContentType(mimeType);
+
+ is = resourceURL.openStream();
+ bis = new BufferedInputStream(is);
+ sos = response.getOutputStream();
+ bos = new BufferedOutputStream(sos);
+
+ byte[] buffer = new byte[BUF_SIZE];
+ int readLen = bis.read(buffer, 0, BUF_SIZE);
+ while (readLen != -1)
+ {
+ bos.write(buffer, 0, readLen);
+ readLen = bis.read(buffer, 0, BUF_SIZE);
+ }
+
+ bos.flush();
+ }
+ catch (Exception e)
+ {
+ throw new ServletException(e);
+ }
+ finally
+ {
+ if (bos != null)
+ {
+ try
+ {
+ bos.close();
+ }
+ catch (Exception ce)
+ {
+ }
+ }
+ if (sos != null)
+ {
+ try
+ {
+ sos.close();
+ }
+ catch (Exception ce)
+ {
+ }
+ }
+ if (bis != null)
+ {
+ try
+ {
+ bis.close();
+ }
+ catch (Exception ce)
+ {
+ }
+ }
+ if (is != null)
+ {
+ try
+ {
+ is.close();
+ }
+ catch (Exception ce)
+ {
+ }
+ }
+ }
+ }
+
+ private String getResourcePath(HttpServletRequest request) throws ServletException
+ {
+ String path = request.getPathInfo();
+
+ try
+ {
+ return URLDecoder.decode(path, "ISO-8859-1");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ throw new ServletException("Failed to decode the path: " + path);
+ }
+ }
+
+ private boolean isDownloadableResource(String path)
+ {
+ for (String prefix : allowedResourcePathPrefixes)
+ {
+ if (path.startsWith(prefix))
+ {
+ return true;
+ }
+ }
+ for (String postfix : allowedResourcePathPostfixes)
+ {
+ if (path.endsWith(postfix))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String [] split(String s, String delimiters)
+ {
+ List<String> tokens = new ArrayList<String>();
+ StringTokenizer st = new StringTokenizer(s, delimiters);
+
+ while (st.hasMoreTokens()) {
+ tokens.add(st.nextToken());
+ }
+
+ return tokens.toArray(new String[tokens.size()]);
+ }
+
+}
Propchange: portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/WebAppClasspathResourceServlet.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/WebAppClasspathResourceServlet.java
------------------------------------------------------------------------------
svn:keywords = Id
Propchange: portals/applications/gems/trunk/src/main/java/org/apache/portals/gems/servlet/WebAppClasspathResourceServlet.java
------------------------------------------------------------------------------
svn:mime-type = text/plain