001 package org.apache.myfaces.tobago.servlet; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more 005 * contributor license agreements. See the NOTICE file distributed with 006 * this work for additional information regarding copyright ownership. 007 * The ASF licenses this file to You under the Apache License, Version 2.0 008 * (the "License"); you may not use this file except in compliance with 009 * the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 import org.apache.commons.io.IOUtils; 021 import org.apache.myfaces.tobago.application.ProjectStage; 022 import org.apache.myfaces.tobago.config.TobagoConfig; 023 import org.slf4j.Logger; 024 import org.slf4j.LoggerFactory; 025 import org.apache.myfaces.tobago.internal.util.MimeTypeUtils; 026 027 import javax.servlet.ServletConfig; 028 import javax.servlet.ServletException; 029 import javax.servlet.http.HttpServlet; 030 import javax.servlet.http.HttpServletRequest; 031 import javax.servlet.http.HttpServletResponse; 032 import java.io.IOException; 033 import java.io.InputStream; 034 import java.io.OutputStream; 035 036 /** 037 * <p><pre> 038 * <servlet> 039 * <servlet-name>ResourceServlet</servlet-name> 040 * <servlet-class>org.apache.myfaces.tobago.servlet.ResourceServlet</servlet-class> 041 * <init-param> 042 * <description>The value for the expires header in seconds. 043 * The default for ProjectStage.Production is 86400 sec (24 h) otherwise no expires header.</description> 044 * <param-name>expires</param-name> 045 * <param-value>14400</param-value> 046 * </init-param> 047 * <init-param> 048 * <description>The value for the copy buffer size. 049 * Default is 4096.</description> 050 * <param-name>bufferSize</param-name> 051 * <param-value>4096</param-value> 052 * </init-param> 053 * </servlet> 054 * <servlet-mapping> 055 * <servlet-name>ResourceServlet</servlet-name> 056 * <url-pattern>/org/apache/myfaces/tobago/renderkit/*</url-pattern> 057 * </servlet-mapping> 058 * </pre><p> 059 * 060 * @since 1.0.7 061 */ 062 public class ResourceServlet extends HttpServlet { 063 064 private static final long serialVersionUID = -4491419290205206466L; 065 066 private static final Logger LOG = LoggerFactory.getLogger(ResourceServlet.class); 067 068 private Long expires; 069 private int bufferSize; 070 071 @Override 072 public void init(ServletConfig servletConfig) throws ServletException { 073 super.init(servletConfig); 074 TobagoConfig tobagoConfig = TobagoConfig.getInstance(servletConfig.getServletContext()); 075 if (tobagoConfig != null && tobagoConfig.getProjectStage() == ProjectStage.Production) { 076 expires = 24 * 60 * 60 * 1000L; 077 } 078 String expiresString = servletConfig.getInitParameter("expires"); 079 if (expiresString != null) { 080 try { 081 expires = new Long(expiresString) * 1000; 082 } catch (NumberFormatException e) { 083 LOG.error("Caught: " + e.getMessage(), e); 084 } 085 } 086 String bufferSizeString = servletConfig.getInitParameter("bufferSize"); 087 bufferSize = 1024 * 4; 088 if (bufferSizeString != null) { 089 try { 090 bufferSize = Integer.parseInt(bufferSizeString); 091 } catch (NumberFormatException e) { 092 LOG.error("Caught: " + e.getMessage(), e); 093 } 094 } 095 } 096 097 @Override 098 protected void doGet( 099 HttpServletRequest request, HttpServletResponse response) 100 throws ServletException, IOException { 101 102 String requestURI = request.getRequestURI(); 103 String resource = requestURI.substring(request.getContextPath().length() + 1); 104 105 if (expires != null) { 106 response.setDateHeader("Last-Modified", 0); 107 response.setHeader("Cache-Control", "Public, max-age=" + expires); 108 response.setDateHeader("Expires", System.currentTimeMillis() + expires); 109 } 110 String contentType = MimeTypeUtils.getMimeTypeForFile(requestURI); 111 if (contentType != null) { 112 response.setContentType(contentType); 113 } else { 114 String message = "Unsupported mime type of resource='" + resource + "'"; 115 LOG.warn(message + " (because of security reasons)"); 116 response.sendError(HttpServletResponse.SC_FORBIDDEN, message); 117 return; 118 } 119 120 InputStream inputStream = null; 121 try { 122 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 123 124 // meta inf (like in servlet 3.0) 125 inputStream = classLoader.getResourceAsStream("META-INF/resources/" + resource); 126 127 // "normal" classpath 128 if (inputStream == null) { 129 inputStream = classLoader.getResourceAsStream(resource); 130 } 131 132 if (inputStream != null) { 133 copy(inputStream, response.getOutputStream()); 134 } else { 135 String message = "Resource '" + resource + "' not found!"; 136 LOG.warn(message); 137 response.sendError(HttpServletResponse.SC_NOT_FOUND, message); 138 } 139 } finally { 140 IOUtils.closeQuietly(inputStream); 141 } 142 } 143 144 @Override 145 protected long getLastModified(HttpServletRequest request) { 146 if (expires != null) { 147 return 0; 148 } else { 149 return super.getLastModified(request); 150 } 151 } 152 153 private void copy(InputStream inputStream, OutputStream outputStream) throws IOException { 154 byte[] buffer = new byte[bufferSize]; 155 int count; 156 while (-1 != (count = inputStream.read(buffer))) { 157 outputStream.write(buffer, 0, count); 158 } 159 } 160 }