001
014
015 package com.liferay.portal.servlet.filters.dynamiccss;
016
017 import com.liferay.portal.kernel.cache.key.CacheKeyGenerator;
018 import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.servlet.BufferCacheServletResponse;
022 import com.liferay.portal.kernel.servlet.HttpHeaders;
023 import com.liferay.portal.kernel.servlet.ServletResponseUtil;
024 import com.liferay.portal.kernel.util.ContentTypes;
025 import com.liferay.portal.kernel.util.FileUtil;
026 import com.liferay.portal.kernel.util.GetterUtil;
027 import com.liferay.portal.kernel.util.HttpUtil;
028 import com.liferay.portal.kernel.util.JavaConstants;
029 import com.liferay.portal.kernel.util.StringPool;
030 import com.liferay.portal.kernel.util.StringUtil;
031 import com.liferay.portal.servlet.filters.IgnoreModuleRequestFilter;
032 import com.liferay.portal.util.PropsUtil;
033
034 import java.io.File;
035
036 import java.net.URL;
037 import java.net.URLConnection;
038
039 import javax.servlet.FilterChain;
040 import javax.servlet.FilterConfig;
041 import javax.servlet.ServletContext;
042 import javax.servlet.http.HttpServletRequest;
043 import javax.servlet.http.HttpServletResponse;
044
045
049 public class DynamicCSSFilter extends IgnoreModuleRequestFilter {
050
051 public static final boolean ENABLED = GetterUtil.getBoolean(
052 PropsUtil.get(DynamicCSSFilter.class.getName()));
053
054 @Override
055 public void init(FilterConfig filterConfig) {
056 super.init(filterConfig);
057
058 _servletContext = filterConfig.getServletContext();
059
060 File tempDir = (File)_servletContext.getAttribute(
061 JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR);
062
063 _tempDir = new File(tempDir, _TEMP_DIR);
064
065 _tempDir.mkdirs();
066
067 DynamicCSSUtil.init();
068 }
069
070 protected String getCacheFileName(HttpServletRequest request) {
071 CacheKeyGenerator cacheKeyGenerator =
072 CacheKeyGeneratorUtil.getCacheKeyGenerator(
073 DynamicCSSFilter.class.getName());
074
075 cacheKeyGenerator.append(HttpUtil.getProtocol(request.isSecure()));
076 cacheKeyGenerator.append(StringPool.UNDERLINE);
077 cacheKeyGenerator.append(request.getRequestURI());
078
079 String queryString = request.getQueryString();
080
081 if (queryString != null) {
082 cacheKeyGenerator.append(sterilizeQueryString(queryString));
083 }
084
085 return String.valueOf(cacheKeyGenerator.finish());
086 }
087
088 protected Object getDynamicContent(
089 HttpServletRequest request, HttpServletResponse response,
090 FilterChain filterChain)
091 throws Exception {
092
093 String requestURI = request.getRequestURI();
094
095 String requestPath = requestURI;
096
097 String contextPath = request.getContextPath();
098
099 if (!contextPath.equals(StringPool.SLASH)) {
100 requestPath = requestPath.substring(contextPath.length());
101 }
102
103 URL resourceURL = _servletContext.getResource(requestPath);
104
105 if (resourceURL == null) {
106 return null;
107 }
108
109 URLConnection urlConnection = resourceURL.openConnection();
110
111 String cacheCommonFileName = getCacheFileName(request);
112
113 File cacheContentTypeFile = new File(
114 _tempDir, cacheCommonFileName + "_E_CONTENT_TYPE");
115 File cacheDataFile = new File(
116 _tempDir, cacheCommonFileName + "_E_DATA");
117
118 if (cacheDataFile.exists() &&
119 (cacheDataFile.lastModified() >= urlConnection.getLastModified())) {
120
121 if (cacheContentTypeFile.exists()) {
122 String contentType = FileUtil.read(cacheContentTypeFile);
123
124 response.setContentType(contentType);
125 }
126
127 return cacheDataFile;
128 }
129
130 String dynamicContent = null;
131
132 String content = null;
133
134 try {
135 if (requestPath.endsWith(_CSS_EXTENSION)) {
136 if (_log.isInfoEnabled()) {
137 _log.info("Parsing SASS on CSS " + requestPath);
138 }
139
140 content = StringUtil.read(urlConnection.getInputStream());
141
142 dynamicContent = DynamicCSSUtil.parseSass(
143 _servletContext, request, requestPath, content);
144
145 response.setContentType(ContentTypes.TEXT_CSS);
146
147 FileUtil.write(cacheContentTypeFile, ContentTypes.TEXT_CSS);
148 }
149 else if (requestPath.endsWith(_JSP_EXTENSION)) {
150 if (_log.isInfoEnabled()) {
151 _log.info("Parsing SASS on JSP or servlet " + requestPath);
152 }
153
154 BufferCacheServletResponse bufferCacheServletResponse =
155 new BufferCacheServletResponse(response);
156
157 processFilter(
158 DynamicCSSFilter.class, request, bufferCacheServletResponse,
159 filterChain);
160
161 bufferCacheServletResponse.finishResponse();
162
163 content = bufferCacheServletResponse.getString();
164
165 dynamicContent = DynamicCSSUtil.parseSass(
166 _servletContext, request, requestPath, content);
167
168 FileUtil.write(
169 cacheContentTypeFile,
170 bufferCacheServletResponse.getContentType());
171 }
172 else {
173 return null;
174 }
175 }
176 catch (Exception e) {
177 _log.error("Unable to parse SASS on CSS " + requestPath, e);
178
179 if (_log.isDebugEnabled()) {
180 _log.debug(content);
181 }
182
183 response.setHeader(
184 HttpHeaders.CACHE_CONTROL,
185 HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
186 }
187
188 if (dynamicContent != null) {
189 FileUtil.write(cacheDataFile, dynamicContent);
190 }
191 else {
192 dynamicContent = content;
193 }
194
195 return dynamicContent;
196 }
197
198 @Override
199 protected void processFilter(
200 HttpServletRequest request, HttpServletResponse response,
201 FilterChain filterChain)
202 throws Exception {
203
204 Object parsedContent = getDynamicContent(
205 request, response, filterChain);
206
207 if (parsedContent == null) {
208 processFilter(
209 DynamicCSSFilter.class, request, response, filterChain);
210 }
211 else {
212 if (parsedContent instanceof File) {
213 ServletResponseUtil.write(response, (File)parsedContent);
214 }
215 else if (parsedContent instanceof String) {
216 ServletResponseUtil.write(response, (String)parsedContent);
217 }
218 }
219 }
220
221 protected String sterilizeQueryString(String queryString) {
222 return StringUtil.replace(
223 queryString, new String[] {StringPool.SLASH, StringPool.BACK_SLASH},
224 new String[] {StringPool.UNDERLINE, StringPool.UNDERLINE});
225 }
226
227 private static final String _CSS_EXTENSION = ".css";
228
229 private static final String _JSP_EXTENSION = ".jsp";
230
231 private static final String _TEMP_DIR = "css";
232
233 private static Log _log = LogFactoryUtil.getLog(DynamicCSSFilter.class);
234
235 private ServletContext _servletContext;
236 private File _tempDir;
237
238 }