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.strip;
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.concurrent.ConcurrentLFUCache;
020    import com.liferay.portal.kernel.io.OutputStreamWriter;
021    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.portlet.LiferayWindowState;
025    import com.liferay.portal.kernel.scripting.ScriptingException;
026    import com.liferay.portal.kernel.servlet.HttpHeaders;
027    import com.liferay.portal.kernel.servlet.ServletResponseUtil;
028    import com.liferay.portal.kernel.servlet.StringServletResponse;
029    import com.liferay.portal.kernel.util.CharPool;
030    import com.liferay.portal.kernel.util.GetterUtil;
031    import com.liferay.portal.kernel.util.HttpUtil;
032    import com.liferay.portal.kernel.util.JavaConstants;
033    import com.liferay.portal.kernel.util.KMPSearch;
034    import com.liferay.portal.kernel.util.ParamUtil;
035    import com.liferay.portal.kernel.util.StringPool;
036    import com.liferay.portal.kernel.util.Validator;
037    import com.liferay.portal.servlet.filters.BasePortalFilter;
038    import com.liferay.portal.servlet.filters.dynamiccss.DynamicCSSUtil;
039    import com.liferay.portal.util.MinifierUtil;
040    import com.liferay.portal.util.PropsValues;
041    
042    import java.io.Writer;
043    
044    import java.nio.CharBuffer;
045    
046    import java.util.HashSet;
047    import java.util.Set;
048    import java.util.regex.Matcher;
049    import java.util.regex.Pattern;
050    
051    import javax.servlet.FilterChain;
052    import javax.servlet.FilterConfig;
053    import javax.servlet.http.HttpServletRequest;
054    import javax.servlet.http.HttpServletResponse;
055    
056    /**
057     * @author Brian Wing Shun Chan
058     * @author Raymond Aug??
059     * @author Shuyang Zhou
060     */
061    public class StripFilter extends BasePortalFilter {
062    
063            public static final String SKIP_FILTER =
064                    StripFilter.class.getName() + "SKIP_FILTER";
065    
066            public StripFilter() {
067                    if (PropsValues.MINIFIER_INLINE_CONTENT_CACHE_SIZE > 0) {
068                            _minifierCache = new ConcurrentLFUCache<String, String>(
069                                    PropsValues.MINIFIER_INLINE_CONTENT_CACHE_SIZE);
070                    }
071            }
072    
073            @Override
074            public void init(FilterConfig filterConfig) {
075                    super.init(filterConfig);
076    
077                    for (String ignorePath : PropsValues.STRIP_IGNORE_PATHS) {
078                            _ignorePaths.add(ignorePath);
079                    }
080            }
081    
082            @Override
083            public boolean isFilterEnabled(
084                    HttpServletRequest request, HttpServletResponse response) {
085    
086                    if (isStrip(request) && !isInclude(request) &&
087                            !isAlreadyFiltered(request)) {
088    
089                            return true;
090                    }
091                    else {
092                            return false;
093                    }
094            }
095    
096            protected String extractContent(CharBuffer charBuffer, int length) {
097    
098                    // See LPS-10545
099    
100                    /*String content = charBuffer.subSequence(0, length).toString();
101    
102                    int position = charBuffer.position();
103    
104                    charBuffer.position(position + length);*/
105    
106                    CharBuffer duplicateCharBuffer = charBuffer.duplicate();
107    
108                    int position = duplicateCharBuffer.position() + length;
109    
110                    String content = duplicateCharBuffer.limit(position).toString();
111    
112                    charBuffer.position(position);
113    
114                    return content;
115            }
116    
117            protected boolean hasLanguageAttribute(
118                    CharBuffer charBuffer, int startPos, int length) {
119    
120                    if (!PropsValues.STRIP_JS_LANGUAGE_ATTRIBUTE_SUPPORT_ENABLED) {
121                            return false;
122                    }
123    
124                    if (KMPSearch.search(
125                                    charBuffer, startPos, length, _MARKER_LANGUAGE,
126                                    _MARKER_LANGUAGE_NEXTS) == -1) {
127    
128                            return false;
129                    }
130    
131                    Matcher matcher = _javaScriptPattern.matcher(charBuffer);
132    
133                    if (matcher.find()) {
134                            return true;
135                    }
136    
137                    return false;
138            }
139    
140            protected boolean hasMarker(CharBuffer charBuffer, char[] marker) {
141                    int position = charBuffer.position();
142    
143                    if ((position + marker.length) >= charBuffer.limit()) {
144                            return false;
145                    }
146    
147                    for (int i = 0; i < marker.length; i++) {
148                            char c = marker[i];
149    
150                            char oldC = charBuffer.charAt(i);
151    
152                            if ((c != oldC) && (Character.toUpperCase(c) != oldC)) {
153                                    return false;
154                            }
155                    }
156    
157                    return true;
158            }
159    
160            protected boolean isAlreadyFiltered(HttpServletRequest request) {
161                    if (request.getAttribute(SKIP_FILTER) != null) {
162                            return true;
163                    }
164                    else {
165                            return false;
166                    }
167            }
168    
169            protected boolean isInclude(HttpServletRequest request) {
170                    String uri = (String)request.getAttribute(
171                            JavaConstants.JAVAX_SERVLET_INCLUDE_REQUEST_URI);
172    
173                    if (uri == null) {
174                            return false;
175                    }
176                    else {
177                            return true;
178                    }
179            }
180    
181            protected boolean isStrip(HttpServletRequest request) {
182                    if (!ParamUtil.getBoolean(request, _STRIP, true)) {
183                            return false;
184                    }
185    
186                    String path = request.getPathInfo();
187    
188                    if (_ignorePaths.contains(path)) {
189                            if (_log.isDebugEnabled()) {
190                                    _log.debug("Ignore path " + path);
191                            }
192    
193                            return false;
194                    }
195    
196                    // Modifying binary content through a servlet filter under certain
197                    // conditions is bad on performance the user will not start downloading
198                    // the content until the entire content is modified.
199    
200                    String lifecycle = ParamUtil.getString(request, "p_p_lifecycle");
201    
202                    if ((lifecycle.equals("1") &&
203                             LiferayWindowState.isExclusive(request)) ||
204                            lifecycle.equals("2")) {
205    
206                            return false;
207                    }
208                    else {
209                            return true;
210                    }
211            }
212    
213            protected boolean isStripContentType(String contentType) {
214                    for (String stripContentType : PropsValues.STRIP_MIME_TYPES) {
215                            if (stripContentType.endsWith(StringPool.STAR)) {
216                                    stripContentType = stripContentType.substring(
217                                            0, stripContentType.length() - 1);
218    
219                                    if (contentType.startsWith(stripContentType)) {
220                                            return true;
221                                    }
222                            }
223                            else {
224                                    if (contentType.equals(stripContentType)) {
225                                            return true;
226                                    }
227                            }
228                    }
229    
230                    return false;
231            }
232    
233            protected void outputCloseTag(
234                            CharBuffer charBuffer, Writer writer, String closeTag)
235                    throws Exception {
236    
237                    writer.write(closeTag);
238    
239                    charBuffer.position(charBuffer.position() + closeTag.length());
240    
241                    skipWhiteSpace(charBuffer, writer, true);
242            }
243    
244            protected void outputOpenTag(
245                            CharBuffer charBuffer, Writer writer, char[] openTag)
246                    throws Exception {
247    
248                    writer.write(openTag);
249    
250                    charBuffer.position(charBuffer.position() + openTag.length);
251            }
252    
253            protected void processCSS(
254                            HttpServletRequest request, HttpServletResponse response,
255                            CharBuffer charBuffer, Writer writer)
256                    throws Exception {
257    
258                    outputOpenTag(charBuffer, writer, _MARKER_STYLE_OPEN);
259    
260                    int length = KMPSearch.search(
261                            charBuffer, _MARKER_STYLE_CLOSE, _MARKER_STYLE_CLOSE_NEXTS);
262    
263                    if (length == -1) {
264                            if (_log.isWarnEnabled()) {
265                                    _log.warn("Missing </style>");
266                            }
267    
268                            return;
269                    }
270    
271                    if (length == 0) {
272                            outputCloseTag(charBuffer, writer, _MARKER_STYLE_CLOSE);
273    
274                            return;
275                    }
276    
277                    String content = extractContent(charBuffer, length);
278    
279                    String minifiedContent = content;
280    
281                    if (PropsValues.MINIFIER_INLINE_CONTENT_CACHE_SIZE > 0) {
282                            CacheKeyGenerator cacheKeyGenerator =
283                                    CacheKeyGeneratorUtil.getCacheKeyGenerator(
284                                            StripFilter.class.getName());
285    
286                            String key = String.valueOf(cacheKeyGenerator.getCacheKey(content));
287    
288                            minifiedContent = _minifierCache.get(key);
289    
290                            if (minifiedContent == null) {
291                                    if (PropsValues.STRIP_CSS_SASS_ENABLED) {
292                                            try {
293                                                    content = DynamicCSSUtil.parseSass(
294                                                            request, key, content);
295                                            }
296                                            catch (ScriptingException se) {
297                                                    _log.error("Unable to parse SASS on CSS " + key, se);
298    
299                                                    if (_log.isDebugEnabled()) {
300                                                            _log.debug(content);
301                                                    }
302    
303                                                    if (response != null) {
304                                                            response.setHeader(
305                                                                    HttpHeaders.CACHE_CONTROL,
306                                                                    HttpHeaders.CACHE_CONTROL_NO_CACHE_VALUE);
307                                                    }
308                                            }
309                                    }
310    
311                                    minifiedContent = MinifierUtil.minifyCss(content);
312    
313                                    boolean skipCache = false;
314    
315                                    for (String skipCss :
316                                                    PropsValues.MINIFIER_INLINE_CONTENT_CACHE_SKIP_CSS) {
317    
318                                            if (minifiedContent.contains(skipCss)) {
319                                                    skipCache = true;
320    
321                                                    break;
322                                            }
323                                    }
324    
325                                    if (!skipCache) {
326                                            _minifierCache.put(key, minifiedContent);
327                                    }
328                            }
329                    }
330    
331                    if (Validator.isNotNull(minifiedContent)) {
332                            writer.write(minifiedContent);
333                    }
334    
335                    outputCloseTag(charBuffer, writer, _MARKER_STYLE_CLOSE);
336            }
337    
338            @Override
339            protected void processFilter(
340                            HttpServletRequest request, HttpServletResponse response,
341                            FilterChain filterChain)
342                    throws Exception {
343    
344                    if (_log.isDebugEnabled()) {
345                            String completeURL = HttpUtil.getCompleteURL(request);
346    
347                            _log.debug("Stripping " + completeURL);
348                    }
349    
350                    request.setAttribute(SKIP_FILTER, Boolean.TRUE);
351    
352                    StringServletResponse stringResponse = new StringServletResponse(
353                            response);
354    
355                    processFilter(StripFilter.class, request, stringResponse, filterChain);
356    
357                    String contentType = GetterUtil.getString(
358                            stringResponse.getContentType()).toLowerCase();
359    
360                    if (_log.isDebugEnabled()) {
361                            _log.debug("Stripping content of type " + contentType);
362                    }
363    
364                    response.setContentType(contentType);
365    
366                    if (isStripContentType(contentType) &&
367                            (stringResponse.getStatus() == HttpServletResponse.SC_OK)) {
368    
369                            CharBuffer oldCharBuffer = CharBuffer.wrap(
370                                    stringResponse.getString());
371    
372                            boolean ensureContentLength = ParamUtil.getBoolean(
373                                    request, _ENSURE_CONTENT_LENGTH);
374    
375                            if (ensureContentLength) {
376                                    UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
377                                            new UnsyncByteArrayOutputStream();
378    
379                                    strip(
380                                            request, response, oldCharBuffer,
381                                            new OutputStreamWriter(unsyncByteArrayOutputStream));
382    
383                                    response.setContentLength(unsyncByteArrayOutputStream.size());
384    
385                                    unsyncByteArrayOutputStream.writeTo(response.getOutputStream());
386                            }
387                            else if (!response.isCommitted()) {
388                                    strip(request, response, oldCharBuffer, response.getWriter());
389                            }
390                    }
391                    else {
392                            ServletResponseUtil.write(response, stringResponse);
393                    }
394            }
395    
396            protected void processInput(CharBuffer oldCharBuffer, Writer writer)
397                    throws Exception {
398    
399                    int length = KMPSearch.search(
400                            oldCharBuffer, _MARKER_INPUT_OPEN.length + 1, _MARKER_INPUT_CLOSE,
401                            _MARKER_INPUT_CLOSE_NEXTS);
402    
403                    if (length == -1) {
404                            if (_log.isWarnEnabled()) {
405                                    _log.warn("Missing />");
406                            }
407    
408                            outputOpenTag(oldCharBuffer, writer, _MARKER_INPUT_OPEN);
409    
410                            return;
411                    }
412    
413                    length += _MARKER_INPUT_CLOSE.length();
414    
415                    String content = extractContent(oldCharBuffer, length);
416    
417                    writer.write(content);
418    
419                    skipWhiteSpace(oldCharBuffer, writer, true);
420            }
421    
422            protected void processJavaScript(
423                            CharBuffer charBuffer, Writer writer, char[] openTag)
424                    throws Exception {
425    
426                    int endPos = openTag.length + 1;
427    
428                    char c = charBuffer.charAt(openTag.length);
429    
430                    if (c == CharPool.SPACE) {
431                            int startPos = openTag.length + 1;
432    
433                            for (int i = startPos; i < charBuffer.length(); i++) {
434                                    c = charBuffer.charAt(i);
435    
436                                    if (c == CharPool.GREATER_THAN) {
437    
438                                            // Open script tag complete
439    
440                                            endPos = i + 1;
441    
442                                            int length = i - startPos;
443    
444                                            if ((length < _MARKER_TYPE_JAVASCRIPT.length()) ||
445                                                    (KMPSearch.search(
446                                                            charBuffer, startPos, length,
447                                                            _MARKER_TYPE_JAVASCRIPT,
448                                                            _MARKER_TYPE_JAVASCRIPT_NEXTS) == -1)) {
449    
450                                                    // We have just determined that this is an open script
451                                                    // tag that does not have the attribute
452                                                    // type="text/javascript". Now check to see if it has
453                                                    // the attribute language="JavaScript". If it does not,
454                                                    // then we skip stripping.
455    
456                                                    if (!hasLanguageAttribute(
457                                                                    charBuffer, startPos, length)) {
458    
459                                                            return;
460                                                    }
461                                            }
462    
463                                            // Open script tag has no attribute or has attribute
464                                            // type="text/javascript". Start stripping.
465    
466                                            break;
467                                    }
468                                    else if (c == CharPool.LESS_THAN) {
469    
470                                            // Illegal open script tag. Found a '<' before seeing a '>'.
471    
472                                            return;
473                                    }
474                            }
475    
476                            if (endPos == charBuffer.length()) {
477    
478                                    // Illegal open script tag. Unable to find a '>'.
479    
480                                    return;
481                            }
482                    }
483                    else if (c != CharPool.GREATER_THAN) {
484    
485                            // Illegal open script tag. Not followed by a '>' or a ' '.
486    
487                            return;
488                    }
489    
490                    writer.append(charBuffer, 0, endPos);
491    
492                    charBuffer.position(charBuffer.position() + endPos);
493    
494                    int length = KMPSearch.search(
495                            charBuffer, _MARKER_SCRIPT_CLOSE, _MARKER_SCRIPT_CLOSE_NEXTS);
496    
497                    if (length == -1) {
498                            if (_log.isWarnEnabled()) {
499                                    _log.warn("Missing </script>");
500                            }
501    
502                            return;
503                    }
504    
505                    if (length == 0) {
506                            outputCloseTag(charBuffer, writer, _MARKER_SCRIPT_CLOSE);
507    
508                            return;
509                    }
510    
511                    String content = extractContent(charBuffer, length);
512    
513                    String minifiedContent = content;
514    
515                    if (PropsValues.MINIFIER_INLINE_CONTENT_CACHE_SIZE > 0) {
516                            CacheKeyGenerator cacheKeyGenerator =
517                                    CacheKeyGeneratorUtil.getCacheKeyGenerator(
518                                            StripFilter.class.getName());
519    
520                            String key = String.valueOf(cacheKeyGenerator.getCacheKey(content));
521    
522                            minifiedContent = _minifierCache.get(key);
523    
524                            if (minifiedContent == null) {
525                                    minifiedContent = MinifierUtil.minifyJavaScript(content);
526    
527                                    boolean skipCache = false;
528    
529                                    for (String skipJavaScript :
530                                                    PropsValues.
531                                                            MINIFIER_INLINE_CONTENT_CACHE_SKIP_JAVASCRIPT) {
532    
533                                            if (minifiedContent.contains(skipJavaScript)) {
534                                                    skipCache = true;
535    
536                                                    break;
537                                            }
538                                    }
539    
540                                    if (!skipCache) {
541                                            _minifierCache.put(key, minifiedContent);
542                                    }
543                            }
544                    }
545    
546                    if (Validator.isNotNull(minifiedContent)) {
547                            writer.write(minifiedContent);
548                    }
549    
550                    outputCloseTag(charBuffer, writer, _MARKER_SCRIPT_CLOSE);
551            }
552    
553            protected void processPre(CharBuffer oldCharBuffer, Writer writer)
554                    throws Exception {
555    
556                    int length = KMPSearch.search(
557                            oldCharBuffer, _MARKER_PRE_OPEN.length + 1, _MARKER_PRE_CLOSE,
558                            _MARKER_PRE_CLOSE_NEXTS);
559    
560                    if (length == -1) {
561                            if (_log.isWarnEnabled()) {
562                                    _log.warn("Missing </pre>");
563                            }
564    
565                            outputOpenTag(oldCharBuffer, writer, _MARKER_PRE_OPEN);
566    
567                            return;
568                    }
569    
570                    length += _MARKER_PRE_CLOSE.length();
571    
572                    String content = extractContent(oldCharBuffer, length);
573    
574                    writer.write(content);
575    
576                    skipWhiteSpace(oldCharBuffer, writer, true);
577            }
578    
579            protected void processTextArea(CharBuffer oldCharBuffer, Writer writer)
580                    throws Exception {
581    
582                    int length = KMPSearch.search(
583                            oldCharBuffer, _MARKER_TEXTAREA_OPEN.length + 1,
584                            _MARKER_TEXTAREA_CLOSE, _MARKER_TEXTAREA_CLOSE_NEXTS);
585    
586                    if (length == -1) {
587                            if (_log.isWarnEnabled()) {
588                                    _log.warn("Missing </textArea>");
589                            }
590    
591                            outputOpenTag(oldCharBuffer, writer, _MARKER_TEXTAREA_OPEN);
592                            return;
593                    }
594    
595                    length += _MARKER_TEXTAREA_CLOSE.length();
596    
597                    String content = extractContent(oldCharBuffer, length);
598    
599                    writer.write(content);
600    
601                    skipWhiteSpace(oldCharBuffer, writer, true);
602            }
603    
604            protected boolean skipWhiteSpace(
605                            CharBuffer charBuffer, Writer writer, boolean appendSeparator)
606                    throws Exception {
607    
608                    boolean skipped = false;
609    
610                    for (int i = charBuffer.position(); i < charBuffer.limit(); i++) {
611                            char c = charBuffer.get();
612    
613                            if ((c == CharPool.SPACE) || (c == CharPool.TAB) ||
614                                    (c == CharPool.RETURN) || (c == CharPool.NEW_LINE)) {
615    
616                                    skipped = true;
617    
618                                    continue;
619                            }
620                            else {
621                                    charBuffer.position(i);
622    
623                                    break;
624                            }
625                    }
626    
627                    if (skipped && appendSeparator) {
628                            writer.write(CharPool.SPACE);
629                    }
630    
631                    return skipped;
632            }
633    
634            protected void strip(
635                            HttpServletRequest request, HttpServletResponse response,
636                            CharBuffer charBuffer, Writer writer)
637                    throws Exception {
638    
639                    skipWhiteSpace(charBuffer, writer, false);
640    
641                    while (charBuffer.hasRemaining()) {
642                            char c = charBuffer.get();
643    
644                            writer.write(c);
645    
646                            if (c == CharPool.LESS_THAN) {
647                                    if (hasMarker(charBuffer, _MARKER_INPUT_OPEN)) {
648                                            processInput(charBuffer, writer);
649    
650                                            continue;
651                                    }
652                                    else if (hasMarker(charBuffer, _MARKER_PRE_OPEN)) {
653                                            processPre(charBuffer, writer);
654    
655                                            continue;
656                                    }
657                                    else if (hasMarker(charBuffer, _MARKER_TEXTAREA_OPEN)) {
658                                            processTextArea(charBuffer, writer);
659    
660                                            continue;
661                                    }
662                                    else if (hasMarker(charBuffer, _MARKER_SCRIPT_OPEN)) {
663                                            processJavaScript(charBuffer, writer, _MARKER_SCRIPT_OPEN);
664    
665                                            continue;
666                                    }
667                                    else if (hasMarker(charBuffer, _MARKER_STYLE_OPEN)) {
668                                            processCSS(request, response, charBuffer, writer);
669    
670                                            continue;
671                                    }
672                            }
673                            else if (c == CharPool.GREATER_THAN) {
674                                    skipWhiteSpace(charBuffer, writer, true);
675                            }
676    
677                            skipWhiteSpace(charBuffer, writer, true);
678                    }
679    
680                    writer.flush();
681            }
682    
683            private static final String _ENSURE_CONTENT_LENGTH = "ensureContentLength";
684    
685            private static final String _MARKER_INPUT_CLOSE = "/>";
686    
687            private static final int[] _MARKER_INPUT_CLOSE_NEXTS =
688                    KMPSearch.generateNexts(_MARKER_INPUT_CLOSE);
689    
690            private static final char[] _MARKER_INPUT_OPEN = "input".toCharArray();
691    
692            private static final String _MARKER_LANGUAGE = "language=";
693    
694            private static final int[] _MARKER_LANGUAGE_NEXTS = KMPSearch.generateNexts(
695                    _MARKER_LANGUAGE);
696    
697            private static final String _MARKER_PRE_CLOSE = "/pre>";
698    
699            private static final int[] _MARKER_PRE_CLOSE_NEXTS =
700                    KMPSearch.generateNexts(_MARKER_PRE_CLOSE);
701    
702            private static final char[] _MARKER_PRE_OPEN = "pre".toCharArray();
703    
704            private static final String _MARKER_SCRIPT_CLOSE = "</script>";
705    
706            private static final int[] _MARKER_SCRIPT_CLOSE_NEXTS =
707                    KMPSearch.generateNexts(_MARKER_SCRIPT_CLOSE);
708    
709            private static final char[] _MARKER_SCRIPT_OPEN = "script".toCharArray();
710    
711            private static final String _MARKER_STYLE_CLOSE = "</style>";
712    
713            private static final int[] _MARKER_STYLE_CLOSE_NEXTS =
714                    KMPSearch.generateNexts(_MARKER_STYLE_CLOSE);
715    
716            private static final char[] _MARKER_STYLE_OPEN =
717                    "style type=\"text/css\">".toCharArray();
718    
719            private static final String _MARKER_TEXTAREA_CLOSE = "/textarea>";
720    
721            private static final int[] _MARKER_TEXTAREA_CLOSE_NEXTS =
722                    KMPSearch.generateNexts(_MARKER_TEXTAREA_CLOSE);
723    
724            private static final char[] _MARKER_TEXTAREA_OPEN =
725                    "textarea ".toCharArray();
726    
727            private static final String _MARKER_TYPE_JAVASCRIPT =
728                    "type=\"text/javascript\"";
729    
730            private static final int[] _MARKER_TYPE_JAVASCRIPT_NEXTS =
731                    KMPSearch.generateNexts(_MARKER_TYPE_JAVASCRIPT);
732    
733            private static final String _STRIP = "strip";
734    
735            private static Log _log = LogFactoryUtil.getLog(StripFilter.class);
736    
737            private static Pattern _javaScriptPattern = Pattern.compile(
738                    "[Jj][aA][vV][aA][sS][cC][rR][iI][pP][tT]");
739    
740            private Set<String> _ignorePaths = new HashSet<String>();
741            private ConcurrentLFUCache<String, String> _minifierCache;
742    
743    }