001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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    /**
046     * @author Eduardo Lundgren
047     * @author Raymond Aug??
048     */
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    }