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 }