/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, availible at the root * application directory. */ package org.geoserver.ows; import java.io.File; import java.io.FileInputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.geoserver.ows.util.EncodingInfo; import org.geoserver.ows.util.XmlCharsetDetector; import org.geoserver.platform.GeoServerResourceLoader; import org.springframework.web.context.support.ServletContextResource; import org.springframework.web.context.support.ServletContextResourceLoader; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; /** * Controller which publishes files through a web interface. * <p> * To use this controller, it should be mapped to a particular url in the url * mapping of the spring dispatcher servlet. Example: * <pre> * <code> * <bean id="filePublisher" class="org.geoserver.ows.FilePublisher"/> * <bean id="dispatcherMappings" * <property name="alwaysUseFullPath" value="true"/> * class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> * <property name="mappings"> * <prop key="/schemas/** /*.xsd">filePublisher</prop> * <prop key="/schemas/** /*.dtd">filePublisher</prop> * <prop key="/styles/*">filePublisher</prop> * </property> * </bean> * </code> * </pre> * </p> * * @author Justin Deoliveira, The Open Planning Project * */ public class FilePublisher extends AbstractController { /** * Resource loader */ protected GeoServerResourceLoader loader; /** * Servlet resource loader */ protected ServletContextResourceLoader scloader; /** * Creates the new file publisher. * * @param loader The loader used to locate files. */ public FilePublisher(GeoServerResourceLoader loader) { this.loader = loader; } @Override protected void initServletContext(ServletContext servletContext) { this.scloader = new ServletContextResourceLoader(servletContext); } protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { String ctxPath = request.getContextPath(); String reqPath = request.getRequestURI(); reqPath = reqPath.substring(ctxPath.length()); if ((reqPath.length() > 1) && reqPath.startsWith("/")) { reqPath = reqPath.substring(1); } // sigh, in order to serve the file we have to open it 2 times // 1) to determine its mime type // 2) to determine its encoding and really serve it // we can't coalish 1) because we don't have a way to give jmimemagic the bytes at the // beginning of the file without disabling extension quick matching // load the file File file = loader.find(reqPath); if (file == null && scloader != null) { //try loading as a servlet resource ServletContextResource resource = (ServletContextResource) scloader.getResource(reqPath); if (resource != null && resource.exists()) { file = resource.getFile(); } } if (file == null) { //return a 404 response.sendError(HttpServletResponse.SC_NOT_FOUND); return null; } if (file.isDirectory()) { String uri = request.getRequestURI().toString(); uri += uri.endsWith("/") ? "index.html" : "/index.html"; response.addHeader("Location", uri); response.sendError(HttpServletResponse.SC_MOVED_TEMPORARILY); return null; } // set the mime if known by the servlet container, set nothing otherwise // (Tomcat behaves like this when it does not recognize the file format) String mime = getServletContext().getMimeType(file.getName()); if (mime != null) response.setContentType(mime); // set the content length long length = file.length(); if(length > 0 && length <= Integer.MAX_VALUE) response.setContentLength((int) length); // set the last modified header long lastModified = file.lastModified(); if(lastModified > 0) { SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss", Locale.ENGLISH); format.setTimeZone(TimeZone.getTimeZone("GMT")); String formatted = format.format(new Date(lastModified)) + " GMT"; response.setHeader("Last-Modified", formatted); } // Guessing the charset (and closing the stream) EncodingInfo encInfo = null; FileInputStream input = null; OutputStream output = null; final byte[] b4 = new byte[4]; int count = 0; try { // open the output input = new FileInputStream(file); // Read the first four bytes, and determine charset encoding count = input.read(b4); encInfo = XmlCharsetDetector.getEncodingName(b4, count); response.setCharacterEncoding(encInfo.getEncoding() != null ? encInfo.getEncoding() : "UTF-8"); // send out the first four bytes read output = response.getOutputStream(); output.write(b4, 0, count); // copy the content to the output byte[] buffer = new byte[8192]; int n = -1; while ((n = input.read(buffer)) != -1) { output.write(buffer, 0, n); } } finally { if(output != null) output.flush(); if(input != null) input.close(); } return null; } }