Author: ssmiweve Date: 2007-06-17 13:27:33 +0200 (Sun, 17 Jun 2007) New Revision: 5342
Added: trunk/skinresourcefeed/ trunk/skinresourcefeed/LICENSE.txt trunk/skinresourcefeed/pom.xml trunk/skinresourcefeed/src/ trunk/skinresourcefeed/src/main/ trunk/skinresourcefeed/src/main/java/ trunk/skinresourcefeed/src/main/java/no/ trunk/skinresourcefeed/src/main/java/no/schibstedsok/ trunk/skinresourcefeed/src/main/java/no/schibstedsok/commons/ trunk/skinresourcefeed/src/main/java/no/schibstedsok/commons/resourcefeed/ trunk/skinresourcefeed/src/main/java/no/schibstedsok/commons/resourcefeed/ResourceServlet.java Log: commons-resourcefeed moved back into SESAT since it's only ever going to be used by sesat skins. for history beyond this point look in its previous repository at https://dev.schibstedsok.no/svn/schibstedsok-commons/trunk/commons-resourcefeed/ Added: trunk/skinresourcefeed/LICENSE.txt =================================================================== --- trunk/skinresourcefeed/LICENSE.txt (rev 0) +++ trunk/skinresourcefeed/LICENSE.txt 2007-06-17 11:27:33 UTC (rev 5342) @@ -0,0 +1 @@ +Copyright (2005-2006) Schibsted Søk AS \ No newline at end of file Added: trunk/skinresourcefeed/pom.xml =================================================================== --- trunk/skinresourcefeed/pom.xml (rev 0) +++ trunk/skinresourcefeed/pom.xml 2007-06-17 11:27:33 UTC (rev 5342) @@ -0,0 +1,43 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <parent> + <groupId>schibstedsok</groupId> + <artifactId>search-portal</artifactId> + <version>2.14-SNAPSHOT</version> + </parent> + + <modelVersion>4.0.0</modelVersion> + <name>Skin Resource Feed</name> + <artifactId>skinresourcefeed</artifactId> + <packaging>jar</packaging> + + <description>This is the project for SESAT's skins to serve resource documents.</description> + + <build> + <resources> + <resource> + <directory>src/main/conf</directory> + <filtering>true</filtering> + <includes> + <include>*.xml</include> + <include>*.dtd</include> + <include>**/*.xml</include> + <include>**/*.dtd</include> + </includes> + </resource> + </resources> + </build> + + + <dependencies> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + </dependency> + </dependencies> +</project> Property changes on: trunk/skinresourcefeed/pom.xml ___________________________________________________________________ Name: svn:executable + * Added: trunk/skinresourcefeed/src/main/java/no/schibstedsok/commons/resourcefeed/ResourceServlet.java =================================================================== --- trunk/skinresourcefeed/src/main/java/no/schibstedsok/commons/resourcefeed/ResourceServlet.java (rev 0) +++ trunk/skinresourcefeed/src/main/java/no/schibstedsok/commons/resourcefeed/ResourceServlet.java 2007-06-17 11:27:33 UTC (rev 5342) @@ -0,0 +1,310 @@ +/* Copyright (2006-2007) Schibsted Søk AS + * ResourceServlet.java + * + * Created on 19 January 2006, 13:51 + */ + +package no.schibstedsok.commons.resourcefeed; + + +import org.apache.log4j.Logger; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.HashMap; +import java.util.Set; +import java.util.Map; +import java.util.HashSet; +import java.util.Arrays; +import java.util.Iterator; + +/** Resource Provider. + * Serves configuration files (properties, xml), css, gifs, jpgs, javascript, + * classes, jar files and velocity templates for search-portal. + * Css, images, and javascript require direct access from client. + * + * @author <a href="mailto:[EMAIL PROTECTED]">Michael Semb Wever</a> + * @version $Id$ + */ +public final class ResourceServlet extends HttpServlet { + + private static final Logger LOG = Logger.getLogger(ResourceServlet.class); + + private static final String REMOTE_ADDRESS_KEY = "REMOTE_ADDR"; + private static final String ERR_RESTRICTED_AREA = "<strong>Restricted Area!</strong>"; + private static final String ERR_TRIED_TO_ACCESS = " tried to access Resource servlet!"; + private static final String ERR_NOT_FOUND = "Failed to find resource "; + private static final String ERR_TRIED_TO_CROSS_REFERENCE = " tried to cross-reference resource!"; + + private static final String DEBUG_DEFAULT_MODIFCATION_TIMESTAMP = "Default modified timestamp set to "; + private static final String DEBUG_CLIENT_IP = "Client ipaddress "; + + private static final Map<String,String> CONTENT_TYPES = new HashMap<String,String>(); + private static final Map<String,String> CONTENT_PATHS = new HashMap<String,String>(); + private static final Set<String> RESTRICTED = new HashSet<String>(); + + private long defaultLastModified = 0; + private String[] ipaddressesAllowed = new String[]{}; + + private ServletConfig servletConfig; + + static { + // The different extension to content type mappings + // XXX is there an opensource library to do this? + CONTENT_TYPES.put("properties", "text/plain"); + CONTENT_TYPES.put("xml", "text/xml"); + CONTENT_TYPES.put("css", "text/css"); + CONTENT_TYPES.put("js", "text/javascript"); + CONTENT_TYPES.put("jpg", "image/jpeg"); + CONTENT_TYPES.put("gif", "image/gif"); + CONTENT_TYPES.put("png", "image/png"); + CONTENT_TYPES.put("ico", "image/x-icon"); + CONTENT_TYPES.put("vm", "text/plain"); + CONTENT_TYPES.put("html", "text/plain"); + CONTENT_TYPES.put("class", "application/java"); + CONTENT_TYPES.put("jar", "application/java-archive"); + } + + /** + * [EMAIL PROTECTED] + */ + @Override + public String getServletInfo() { + + return "Servlet responsible for serving resources. Goes in hand with search-portal/site-spi"; + } + + /** + * [EMAIL PROTECTED] + */ + @Override + public void init(final ServletConfig config) { + + this.servletConfig = config; + + defaultLastModified = System.currentTimeMillis(); + LOG.info(DEBUG_DEFAULT_MODIFCATION_TIMESTAMP + defaultLastModified); + + final String allowed = config.getInitParameter("ipaddresses.allowed"); + LOG.info("allowing ipaddresses " + allowed); + if (null != allowed && allowed.length() >0) { + ipaddressesAllowed = allowed.split(","); + } + + final String restricted = config.getInitParameter("resources.restricted"); + LOG.info("restricted resources " + restricted); + if (null != restricted && restricted.length()>0) { + RESTRICTED.addAll(Arrays.asList(restricted.split(","))); + } + + final String paths = config.getInitParameter("content.paths"); + LOG.info("content path mappings " + paths); + if (null != paths && paths.length()>0) { + final String[] pathArr = paths.split(","); + for (String path : pathArr) { + final String[] pair = path.split("="); + CONTENT_PATHS.put(pair[0], pair[1]); + } + } + } + + /** + * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods. + * This servlet ignores URL parameters and POST content, as all the information is in the path, + * so it really doesn't matter if it is a GET or POST. + * + * Checks: + * - resource exists, + * - correct path is being used, + * - configuration/template resources are only accessed by schibsted machines, + * + * The resource is served to the ServletOutputStream byte by byte from + * getClass().getResourceAsStream(..) + * + * @param request servlet request + * @param response servlet response + * @throws javax.servlet.ServletException if ServletException occurs + * @throws java.io.IOException if IOException occurs + */ + protected void processRequest( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + request.setCharacterEncoding("UTF-8"); // correct encoding + + // Get resource name. Also strip the version number out of the resource + final String configName = request.getPathInfo().replaceAll("/(\\d)+/","/"); + + if (configName != null && configName.trim().length() > 0) { + + + final String extension = configName.substring(configName.lastIndexOf('.') + 1).toLowerCase(); + final String ipAddr = null != request.getAttribute(REMOTE_ADDRESS_KEY) + ? (String) request.getAttribute(REMOTE_ADDRESS_KEY) + : request.getRemoteAddr(); + + // Content-Type + response.setContentType(CONTENT_TYPES.get(extension) + ";charset=UTF-8"); + + // Path check. Resource can only be loaded through correct path. + final String directory = request.getServletPath(); + if (null != CONTENT_PATHS.get(extension) && directory.indexOf(CONTENT_PATHS.get(extension)) >= 0) { + + // ok, check configuration resources are private. + LOG.trace(DEBUG_CLIENT_IP + ipAddr); + + if (RESTRICTED.contains(extension) && !isIpAllowed(ipAddr)) { + + response.setContentType("text/html;charset=UTF-8"); + response.getOutputStream().print(ERR_RESTRICTED_AREA); + LOG.warn(ipAddr + ERR_TRIED_TO_ACCESS); + + } else { + serveResource(configName, request, response); + } + } else { + // not allowed to cross-reference resources. + response.sendError(HttpServletResponse.SC_NOT_FOUND); + LOG.warn(ipAddr + ERR_TRIED_TO_CROSS_REFERENCE); + } + } + } + + /** + * [EMAIL PROTECTED] + */ + @Override + protected void doGet( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + processRequest(request, response); + } + + /** + * [EMAIL PROTECTED] + */ + @Override + protected void doPost( + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + processRequest(request, response); + } + + /** Assigned to the time when the servlet is initialised via the init(ServletConfig) method. + * Any redeployment of the skin results in an update in the last-modified response header. + * Editing the files "in-place" on disk will not have any effect on the last-modified header. + * + * @param req incoming HttpServletRequest request + * @return last-modified header (in milliseconds) + **/ + @Override + protected long getLastModified(final HttpServletRequest req) { + return defaultLastModified; + } + + private void serveResource( + final String configName, + final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + + InputStream is = null; + + try { + is = configName.endsWith(".jar") + ? getJarStream(configName) : ResourceServlet.class.getResourceAsStream(configName); + + if (is != null) { + + // Write response headers before response data according to javadoc for HttpServlet.html#doGet(..) + + // Allow this URL to be cached indefinitely. + // Each jvm restart alters the number that appears in the URL being enough to ensure + // nothing is cached across deployment versions. + response.setHeader("Cache-Control", "Public"); + response.setDateHeader("Expires", Long.MAX_VALUE); + + // Avoid writing out the response body if it's a HEAD request or a GET that the browser has cache for + boolean writeBody = !"HEAD".equals(request.getMethod()); + writeBody &= request.getDateHeader("If-Modified-Since") <= defaultLastModified; + + if (writeBody) { + + // Output the resource byte for byte + final OutputStream os = response.getOutputStream(); + for (int b = is.read(); b >= 0; b = is.read()) { + os.write(b); + } + + // commit response now + os.flush(); + } + + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + LOG.info(ERR_NOT_FOUND + request.getPathInfo()); + } + + } finally { + + if (is != null) { + is.close(); + } + } + } + + private InputStream getJarStream(final String resource) { + final Set paths = servletConfig.getServletContext().getResourcePaths("/WEB-INF/lib"); + + final String baseName = resource.replace(".jar", "").replace("/", ""); + + try { + for (final Iterator iterator = paths.iterator(); iterator.hasNext();) { + + final String path = (String) iterator.next(); + + // Req. for jars can be done without the version suffix. A request for query-transform.jar might + // return the file query-transform-2.11-SNAPSHOT.jar. + if (path.contains(baseName)) { + final URL url = servletConfig.getServletContext().getResource(path); + return url.openConnection().getInputStream(); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return null; + } + + /** + * Returns wether we allow the ipaddress or not. + * @param ipAddr the ipaddress to check. + * + * @return returns true if the ip address is trusted. + */ + private boolean isIpAllowed(final String ipAddr) { + + boolean allowed = + ipAddr.startsWith("127.") || ipAddr.startsWith("10.") || ipAddr.startsWith("0:0:0:0:0:0:0:1%0"); + + for(String s : ipaddressesAllowed){ + allowed |= ipAddr.startsWith(s); + } + return allowed; + + } + +} Property changes on: trunk/skinresourcefeed/src/main/java/no/schibstedsok/commons/resourcefeed/ResourceServlet.java ___________________________________________________________________ Name: svn:keywords + Id _______________________________________________ Kernel-commits mailing list [email protected] http://sesat.no/mailman/listinfo/kernel-commits
