package com.apress.progwt.server.web.servlet; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import javax.activation.FileTypeMap; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; /** * * * Copyright 2008 Jeff Dwyer * * Licensed 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. * * @author Jeff Dwyer * * FileServlet is used as a surrogate for simple jetty/tomcat file serving capabilities. * The problem we're trying to solve is that the dispatcher-servlet maps all .html files * and /service/* This works ok, except that our dispattcher servlet then matches the GWT * HTML files as well. This servlet overrides that and just serves those files directly. * * Ideall this would be done within the dispatcher servlet or with a theoretical <servlet-mapping><excludes> element. * */ public class FileServlet extends HttpServlet { private static final Logger log = Logger.getLogger(FileServlet.class); private int bufferSize = 1024; private String rootPath; /** * Initialize the servlet, and determine the webapp root. */ public void init() throws ServletException { // determine the root of this webapp ServletContext context = getServletContext(); rootPath = context.getRealPath("/"); if (rootPath == null) { throw new ServletException("Cannot find the real path of this webapp, probably using a non-extracted WAR"); } if (rootPath.endsWith("/")) { rootPath = rootPath.substring(0, rootPath.length() - 1); } } /** * Transfer the file. */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { FileChannel in = null; try { // find the location of the file String contextPath = request.getContextPath(); String relativePath = request.getRequestURI().substring(contextPath.length()); log.debug("Real Path "+rootPath+"||"+relativePath); // check the file and open it File file = new File(rootPath,relativePath); log.debug("File "+file.getAbsolutePath()); if (!file.isFile()) { log.info("File not found: " + file.getAbsolutePath()); log("File not found: " + file.getAbsolutePath()); throw new FileNotFoundException(file.getAbsolutePath()); } // check for modifications long ifModifiedSince = request.getDateHeader("If-Modified-Since"); long lastModified = file.lastModified(); if (ifModifiedSince != -1 && lastModified < ifModifiedSince) { throw new NotModifiedException(); } // setup IO streams ByteBuffer buffer = ByteBuffer.allocate(bufferSize); in = new FileInputStream(file).getChannel(); // start returning the response //response.setContentType(getContentTypeFor(file.getName())); OutputStream out = response.getOutputStream(); // read the bytes, returning them in the response while (in.read(buffer) != -1) { out.write(buffer.array(), 0, buffer.position()); buffer.clear(); } out.close(); } catch (NotModifiedException e) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } catch (FileNotFoundException e) { response.sendError(HttpServletResponse.SC_NOT_FOUND); } catch (IOException e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } finally { try { if (in != null) in.close(); } catch (IOException ignore) { } } } /** * Static files treat GET and POST requests the same way. */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } /** * Return the content-type the would be returned for this file name. */ public String getContentTypeFor(String fileName) { // everything else FileTypeMap typeMap = FileTypeMap.getDefaultFileTypeMap(); return typeMap.getContentType(fileName); } /** * An exception when the source object has not been modified. * While this condition is not a failure, it is a break from the normal flow of execution. */ private static class NotModifiedException extends IOException { public NotModifiedException() { } } }