001    /**
002     * Copyright (c) 2000-present 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.PortalWebResourcesUtil;
024    import com.liferay.portal.kernel.servlet.PortletResourcesUtil;
025    import com.liferay.portal.kernel.servlet.ServletResponseUtil;
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.HttpUtil;
030    import com.liferay.portal.kernel.util.JavaConstants;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.util.URLUtil;
034    import com.liferay.portal.servlet.filters.IgnoreModuleRequestFilter;
035    import com.liferay.portal.util.PortalUtil;
036    import com.liferay.portal.util.PropsUtil;
037    
038    import java.io.File;
039    
040    import java.net.URL;
041    
042    import javax.servlet.FilterChain;
043    import javax.servlet.FilterConfig;
044    import javax.servlet.ServletContext;
045    import javax.servlet.http.HttpServletRequest;
046    import javax.servlet.http.HttpServletResponse;
047    
048    /**
049     * @author Eduardo Lundgren
050     * @author Raymond Aug??
051     */
052    public class DynamicCSSFilter extends IgnoreModuleRequestFilter {
053    
054            public static final boolean ENABLED = GetterUtil.getBoolean(
055                    PropsUtil.get(DynamicCSSFilter.class.getName()));
056    
057            @Override
058            public void init(FilterConfig filterConfig) {
059                    super.init(filterConfig);
060    
061                    _servletContext = filterConfig.getServletContext();
062    
063                    File tempDir = (File)_servletContext.getAttribute(
064                            JavaConstants.JAVAX_SERVLET_CONTEXT_TEMPDIR);
065    
066                    _tempDir = new File(tempDir, _TEMP_DIR);
067    
068                    _tempDir.mkdirs();
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                    ServletContext servletContext = _servletContext;
099    
100                    String requestPath = getRequestPath(request);
101    
102                    if (requestPath.endsWith(_CSS_EXTENSION) &&
103                            PortalUtil.isRightToLeft(request)) {
104    
105                            int pos = requestPath.lastIndexOf(StringPool.PERIOD);
106    
107                            requestPath =
108                                    requestPath.substring(0, pos) + "_rtl" +
109                                            requestPath.substring(pos);
110                    }
111    
112                    URL resourceURL = _servletContext.getResource(requestPath);
113    
114                    if (resourceURL == null) {
115                            ServletContext resourceServletContext =
116                                    PortalWebResourcesUtil.getPathServletContext(requestPath);
117    
118                            if (resourceServletContext != null) {
119                                    resourceURL = PortalWebResourcesUtil.getResource(
120                                            resourceServletContext, requestPath);
121                            }
122    
123                            if (resourceURL == null) {
124                                    resourceServletContext =
125                                            PortletResourcesUtil.getPathServletContext(requestPath);
126    
127                                    if (resourceServletContext != null) {
128                                            resourceURL = PortletResourcesUtil.getResource(
129                                                    resourceServletContext, requestPath);
130                                    }
131                            }
132    
133                            if (resourceURL == null) {
134                                    return null;
135                            }
136    
137                            servletContext = resourceServletContext;
138                    }
139    
140                    String cacheCommonFileName = getCacheFileName(request);
141    
142                    File cacheContentTypeFile = new File(
143                            _tempDir, cacheCommonFileName + "_E_CONTENT_TYPE");
144                    File cacheDataFile = new File(
145                            _tempDir, cacheCommonFileName + "_E_DATA");
146    
147                    if (cacheDataFile.exists() &&
148                            (cacheDataFile.lastModified() >=
149                                    URLUtil.getLastModifiedTime(resourceURL))) {
150    
151                            if (cacheContentTypeFile.exists()) {
152                                    String contentType = FileUtil.read(cacheContentTypeFile);
153    
154                                    response.setContentType(contentType);
155                            }
156    
157                            return cacheDataFile;
158                    }
159    
160                    String dynamicContent = null;
161    
162                    String content = null;
163    
164                    try {
165                            if (requestPath.endsWith(_CSS_EXTENSION)) {
166                                    if (_log.isInfoEnabled()) {
167                                            _log.info("Replacing tokens on CSS " + requestPath);
168                                    }
169    
170                                    content = StringUtil.read(resourceURL.openStream());
171    
172                                    dynamicContent = DynamicCSSUtil.replaceToken(
173                                            servletContext, request, content);
174    
175                                    response.setContentType(ContentTypes.TEXT_CSS);
176    
177                                    FileUtil.write(cacheContentTypeFile, ContentTypes.TEXT_CSS);
178                            }
179                            else if (requestPath.endsWith(_JSP_EXTENSION)) {
180                                    if (_log.isInfoEnabled()) {
181                                            _log.info(
182                                                    "Replacing tokens on JSP or servlet " + requestPath);
183                                    }
184    
185                                    BufferCacheServletResponse bufferCacheServletResponse =
186                                            new BufferCacheServletResponse(response);
187    
188                                    processFilter(
189                                            DynamicCSSFilter.class.getName(), request,
190                                            bufferCacheServletResponse, filterChain);
191    
192                                    bufferCacheServletResponse.finishResponse(false);
193    
194                                    content = bufferCacheServletResponse.getString();
195    
196                                    dynamicContent = DynamicCSSUtil.replaceToken(
197                                            servletContext, request, content);
198    
199                                    FileUtil.write(
200                                            cacheContentTypeFile,
201                                            bufferCacheServletResponse.getContentType());
202                            }
203                            else {
204                                    return null;
205                            }
206                    }
207                    catch (Exception e) {
208                            _log.error("Unable to replace tokens in CSS " + requestPath, e);
209    
210                            if (_log.isDebugEnabled()) {
211                                    _log.debug(content);
212                            }
213    
214                            response.setHeader(
215                                    HttpHeaders.CACHE_CONTROL,
216                                    HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
217                    }
218    
219                    if (dynamicContent != null) {
220                            FileUtil.write(cacheDataFile, dynamicContent);
221                    }
222                    else {
223                            dynamicContent = content;
224                    }
225    
226                    return dynamicContent;
227            }
228    
229            protected String getRequestPath(HttpServletRequest request) {
230                    String requestPath = request.getRequestURI();
231    
232                    String contextPath = request.getContextPath();
233    
234                    if (!contextPath.equals(StringPool.SLASH)) {
235                            requestPath = requestPath.substring(contextPath.length());
236                    }
237    
238                    return requestPath;
239            }
240    
241            @Override
242            protected boolean isModuleRequest(HttpServletRequest request) {
243                    String requestURI = request.getRequestURI();
244    
245                    if (PortalWebResourcesUtil.hasContextPath(requestURI)) {
246                            return false;
247                    }
248    
249                    return super.isModuleRequest(request);
250            }
251    
252            @Override
253            protected void processFilter(
254                            HttpServletRequest request, HttpServletResponse response,
255                            FilterChain filterChain)
256                    throws Exception {
257    
258                    Object parsedContent = getDynamicContent(
259                            request, response, filterChain);
260    
261                    if (parsedContent == null) {
262                            processFilter(
263                                    DynamicCSSFilter.class.getName(), request, response,
264                                    filterChain);
265                    }
266                    else {
267                            if (parsedContent instanceof File) {
268                                    ServletResponseUtil.write(response, (File)parsedContent);
269                            }
270                            else if (parsedContent instanceof String) {
271                                    ServletResponseUtil.write(response, (String)parsedContent);
272                            }
273                    }
274            }
275    
276            protected String sterilizeQueryString(String queryString) {
277                    return StringUtil.replace(
278                            queryString, new String[] {StringPool.SLASH, StringPool.BACK_SLASH},
279                            new String[] {StringPool.UNDERLINE, StringPool.UNDERLINE});
280            }
281    
282            private static final String _CSS_EXTENSION = ".css";
283    
284            private static final String _JSP_EXTENSION = ".jsp";
285    
286            private static final String _TEMP_DIR = "css";
287    
288            private static final Log _log = LogFactoryUtil.getLog(
289                    DynamicCSSFilter.class);
290    
291            private ServletContext _servletContext;
292            private File _tempDir;
293    
294    }