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.HttpHeaders;
022    import com.liferay.portal.kernel.servlet.ServletContextUtil;
023    import com.liferay.portal.kernel.servlet.ServletResponseUtil;
024    import com.liferay.portal.kernel.servlet.StringServletResponse;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.ContentTypes;
027    import com.liferay.portal.kernel.util.FileUtil;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.StringUtil;
031    import com.liferay.portal.kernel.util.SystemProperties;
032    import com.liferay.portal.kernel.util.Validator;
033    import com.liferay.portal.servlet.filters.BasePortalFilter;
034    import com.liferay.portal.util.PropsUtil;
035    import com.liferay.util.servlet.filters.CacheResponseUtil;
036    
037    import java.io.File;
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 BasePortalFilter {
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                    _servletContextName = GetterUtil.getString(
060                            _servletContext.getServletContextName());
061    
062                    if (Validator.isNull(_servletContextName)) {
063                            _tempDir += "/portal";
064                    }
065    
066                    DynamicCSSUtil.init();
067            }
068    
069            protected String getCacheFileName(HttpServletRequest request) {
070                    CacheKeyGenerator cacheKeyGenerator =
071                            CacheKeyGeneratorUtil.getCacheKeyGenerator(
072                                    DynamicCSSFilter.class.getName());
073    
074                    cacheKeyGenerator.append(request.getRequestURI());
075    
076                    String queryString = request.getQueryString();
077    
078                    if (queryString != null) {
079                            cacheKeyGenerator.append(sterilizeQueryString(queryString));
080                    }
081    
082                    String cacheKey = String.valueOf(cacheKeyGenerator.finish());
083    
084                    return _tempDir.concat(StringPool.SLASH).concat(cacheKey);
085            }
086    
087            protected Object getDynamicContent(
088                            HttpServletRequest request, HttpServletResponse response,
089                            FilterChain filterChain)
090                    throws Exception {
091    
092                    String requestURI = request.getRequestURI();
093    
094                    String requestPath = requestURI;
095    
096                    String contextPath = request.getContextPath();
097    
098                    if (!contextPath.equals(StringPool.SLASH)) {
099                            requestPath = requestPath.substring(contextPath.length());
100                    }
101    
102                    String realPath = ServletContextUtil.getRealPath(
103                            _servletContext, requestPath);
104    
105                    if (realPath == null) {
106                            return null;
107                    }
108    
109                    realPath = StringUtil.replace(
110                            realPath, CharPool.BACK_SLASH, CharPool.SLASH);
111    
112                    File file = new File(realPath);
113    
114                    if (!file.exists()) {
115                            return null;
116                    }
117    
118                    String cacheCommonFileName = getCacheFileName(request);
119    
120                    File cacheContentTypeFile = new File(
121                            cacheCommonFileName + "_E_CONTENT_TYPE");
122                    File cacheDataFile = new File(cacheCommonFileName + "_E_DATA");
123    
124                    if (cacheDataFile.exists() &&
125                            (cacheDataFile.lastModified() >= file.lastModified())) {
126    
127                            if (cacheContentTypeFile.exists()) {
128                                    String contentType = FileUtil.read(cacheContentTypeFile);
129    
130                                    response.setContentType(contentType);
131                            }
132    
133                            return cacheDataFile;
134                    }
135    
136                    String dynamicContent = null;
137    
138                    String content = null;
139    
140                    try {
141                            if (realPath.endsWith(_CSS_EXTENSION) && file.exists()) {
142                                    if (_log.isInfoEnabled()) {
143                                            _log.info("Parsing SASS on CSS " + file);
144                                    }
145    
146                                    content = FileUtil.read(file);
147    
148                                    dynamicContent = DynamicCSSUtil.parseSass(
149                                            request, realPath, content);
150    
151                                    response.setContentType(ContentTypes.TEXT_CSS);
152    
153                                    FileUtil.write(cacheContentTypeFile, ContentTypes.TEXT_CSS);
154                            }
155                            else if (realPath.endsWith(_JSP_EXTENSION) || !file.exists()) {
156                                    if (_log.isInfoEnabled()) {
157                                            _log.info("Parsing SASS on JSP or servlet " + realPath);
158                                    }
159    
160                                    StringServletResponse stringResponse =
161                                            new StringServletResponse(response);
162    
163                                    processFilter(
164                                            DynamicCSSFilter.class, request, stringResponse,
165                                            filterChain);
166    
167                                    CacheResponseUtil.setHeaders(
168                                            response, stringResponse.getHeaders());
169    
170                                    response.setContentType(stringResponse.getContentType());
171    
172                                    content = stringResponse.getString();
173    
174                                    dynamicContent = DynamicCSSUtil.parseSass(
175                                            request, realPath, content);
176    
177                                    FileUtil.write(
178                                            cacheContentTypeFile, stringResponse.getContentType());
179                            }
180                            else {
181                                    return null;
182                            }
183                    }
184                    catch (Exception e) {
185                            _log.error("Unable to parse SASS on CSS " + realPath, e);
186    
187                            if (_log.isDebugEnabled()) {
188                                    _log.debug(content);
189                            }
190    
191                            response.setHeader(
192                                    HttpHeaders.CACHE_CONTROL,
193                                    HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
194                    }
195    
196                    if (dynamicContent != null) {
197                            FileUtil.write(cacheDataFile, dynamicContent);
198                    }
199                    else {
200                            dynamicContent = content;
201                    }
202    
203                    return dynamicContent;
204            }
205    
206            @Override
207            protected void processFilter(
208                            HttpServletRequest request, HttpServletResponse response,
209                            FilterChain filterChain)
210                    throws Exception {
211    
212                    Object parsedContent = getDynamicContent(
213                            request, response, filterChain);
214    
215                    if (parsedContent == null) {
216                            processFilter(
217                                    DynamicCSSFilter.class, request, response, filterChain);
218                    }
219                    else {
220                            if (parsedContent instanceof File) {
221                                    ServletResponseUtil.write(response, (File)parsedContent);
222                            }
223                            else if (parsedContent instanceof String) {
224                                    ServletResponseUtil.write(response, (String)parsedContent);
225                            }
226                    }
227            }
228    
229            protected String sterilizeQueryString(String queryString) {
230                    return StringUtil.replace(
231                            queryString, new String[] {StringPool.SLASH, StringPool.BACK_SLASH},
232                            new String[] {StringPool.UNDERLINE, StringPool.UNDERLINE});
233            }
234    
235            private static final String _CSS_EXTENSION = ".css";
236    
237            private static final String _JSP_EXTENSION = ".jsp";
238    
239            private static final String _TEMP_DIR =
240                    SystemProperties.get(SystemProperties.TMP_DIR) + "/liferay/css";
241    
242            private static Log _log = LogFactoryUtil.getLog(DynamicCSSFilter.class);
243    
244            private ServletContext _servletContext;
245            private String _servletContextName;
246            private String _tempDir = _TEMP_DIR;
247    
248    }