001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.catalina.ssi;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.IOException;
022: import java.io.InputStreamReader;
023: import java.io.OutputStream;
024: import java.io.OutputStreamWriter;
025: import java.io.PrintWriter;
026: import java.io.Reader;
027: import java.util.regex.Matcher;
028: import java.util.regex.Pattern;
029:
030: import javax.servlet.Filter;
031: import javax.servlet.FilterChain;
032: import javax.servlet.FilterConfig;
033: import javax.servlet.ServletException;
034: import javax.servlet.ServletRequest;
035: import javax.servlet.ServletResponse;
036: import javax.servlet.http.HttpServletRequest;
037: import javax.servlet.http.HttpServletResponse;
038:
039: import org.apache.catalina.Globals;
040:
041: /**
042: * Filter to process SSI requests within a webpage. Mapped to a content types
043: * from within web.xml.
044: *
045: * @author David Becker
046: * @version $Revision: 531303 $, $Date: 2007-04-23 02:24:01 +0200 (lun., 23 avr. 2007) $
047: * @see org.apache.catalina.ssi.SSIServlet
048: */
049: public class SSIFilter implements Filter {
050: protected FilterConfig config = null;
051: /** Debug level for this servlet. */
052: protected int debug = 0;
053: /** Expiration time in seconds for the doc. */
054: protected Long expires = null;
055: /** virtual path can be webapp-relative */
056: protected boolean isVirtualWebappRelative = false;
057: /** regex pattern to match when evaluating content types */
058: protected Pattern contentTypeRegEx = null;
059: /** default pattern for ssi filter content type matching */
060: protected Pattern shtmlRegEx = Pattern
061: .compile("text/x-server-parsed-html(;.*)?");
062:
063: //----------------- Public methods.
064: /**
065: * Initialize this servlet.
066: *
067: * @exception ServletException
068: * if an error occurs
069: */
070: public void init(FilterConfig config) throws ServletException {
071: this .config = config;
072:
073: if (config.getInitParameter("debug") != null) {
074: debug = Integer.parseInt(config.getInitParameter("debug"));
075: }
076:
077: if (config.getInitParameter("contentType") != null) {
078: contentTypeRegEx = Pattern.compile(config
079: .getInitParameter("contentType"));
080: } else {
081: contentTypeRegEx = shtmlRegEx;
082: }
083:
084: isVirtualWebappRelative = Boolean.parseBoolean(config
085: .getInitParameter("isVirtualWebappRelative"));
086:
087: if (config.getInitParameter("expires") != null)
088: expires = Long.valueOf(config.getInitParameter("expires"));
089:
090: if (debug > 0)
091: config.getServletContext().log(
092: "SSIFilter.init() SSI invoker started with 'debug'="
093: + debug);
094: }
095:
096: public void doFilter(ServletRequest request,
097: ServletResponse response, FilterChain chain)
098: throws IOException, ServletException {
099: // cast once
100: HttpServletRequest req = (HttpServletRequest) request;
101: HttpServletResponse res = (HttpServletResponse) response;
102:
103: // indicate that we're in SSI processing
104: req.setAttribute(Globals.SSI_FLAG_ATTR, "true");
105:
106: // setup to capture output
107: ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream();
108: ResponseIncludeWrapper responseIncludeWrapper = new ResponseIncludeWrapper(
109: config.getServletContext(), req, res, basos);
110:
111: // process remainder of filter chain
112: chain.doFilter(req, responseIncludeWrapper);
113:
114: // we can't assume the chain flushed its output
115: responseIncludeWrapper.flushOutputStreamOrWriter();
116: byte[] bytes = basos.toByteArray();
117:
118: // get content type
119: String contentType = responseIncludeWrapper.getContentType();
120:
121: // is this an allowed type for SSI processing?
122: if (contentTypeRegEx.matcher(contentType).matches()) {
123: String encoding = res.getCharacterEncoding();
124:
125: // set up SSI processing
126: SSIExternalResolver ssiExternalResolver = new SSIServletExternalResolver(
127: config.getServletContext(), req, res,
128: isVirtualWebappRelative, debug, encoding);
129: SSIProcessor ssiProcessor = new SSIProcessor(
130: ssiExternalResolver, debug);
131:
132: // prepare readers/writers
133: Reader reader = new InputStreamReader(
134: new ByteArrayInputStream(bytes), encoding);
135: ByteArrayOutputStream ssiout = new ByteArrayOutputStream();
136: PrintWriter writer = new PrintWriter(
137: new OutputStreamWriter(ssiout, encoding));
138:
139: // do SSI processing
140: long lastModified = ssiProcessor.process(reader,
141: responseIncludeWrapper.getLastModified(), writer);
142:
143: // set output bytes
144: writer.flush();
145: bytes = ssiout.toByteArray();
146:
147: // override headers
148: if (expires != null) {
149: res.setDateHeader("expires", (new java.util.Date())
150: .getTime()
151: + expires.longValue() * 1000);
152: }
153: if (lastModified > 0) {
154: res.setDateHeader("last-modified", lastModified);
155: }
156: res.setContentLength(bytes.length);
157:
158: Matcher shtmlMatcher = shtmlRegEx
159: .matcher(responseIncludeWrapper.getContentType());
160: if (shtmlMatcher.matches()) {
161: // Convert shtml mime type to ordinary html mime type but preserve
162: // encoding, if any.
163: String enc = shtmlMatcher.group(1);
164: res.setContentType("text/html"
165: + ((enc != null) ? enc : ""));
166: }
167: }
168:
169: // write output
170: OutputStream out = null;
171: try {
172: out = res.getOutputStream();
173: } catch (IllegalStateException e) {
174: // Ignore, will try to use a writer
175: }
176: if (out == null) {
177: res.getWriter().write(new String(bytes));
178: } else {
179: out.write(bytes);
180: }
181: }
182:
183: public void destroy() {
184: }
185: }
|