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