001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2006 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.ui;
021:
022: import java.io.CharArrayWriter;
023: import java.io.IOException;
024: import java.io.PrintWriter;
025:
026: import javax.servlet.*;
027: import javax.servlet.http.HttpServletRequest;
028: import javax.servlet.http.HttpServletResponse;
029: import javax.servlet.http.HttpServletResponseWrapper;
030:
031: import org.apache.log4j.NDC;
032:
033: import com.ecyrd.jspwiki.TextUtil;
034: import com.ecyrd.jspwiki.WikiContext;
035: import com.ecyrd.jspwiki.event.WikiEventManager;
036: import com.ecyrd.jspwiki.event.WikiPageEvent;
037: import com.ecyrd.jspwiki.url.DefaultURLConstructor;
038: import com.ecyrd.jspwiki.util.WatchDog;
039:
040: /**
041: * This filter goes through the generated page response prior and
042: * places requested resources at the appropriate inclusion markers.
043: * This is done to let dynamic content (e.g. plugins, editors)
044: * include custom resources, even after the HTML head section is
045: * in fact built. This filter is typically the last filter to execute,
046: * and it <em>must</em> run after servlet or JSP code that performs
047: * redirections or sends error codes (such as access control methods).
048: * <p>
049: * Inclusion markers are placed by the IncludeResourcesTag; the
050: * defult content templates (see .../templates/default/commonheader.jsp)
051: * are configured to do this. As an example, a JavaScript resource marker
052: * is added like this:
053: * <pre>
054: * <wiki:IncludeResources type="script"/>
055: * </pre>
056: * Any code that requires special resources must register a resource
057: * request with the TemplateManager. For example:
058: * <pre>
059: * <wiki:RequestResource type="script" path="scripts/custom.js" />
060: * </pre>
061: * or programmatically,
062: * <pre>
063: * TemplateManager.addResourceRequest( context, TemplateManager.RESOURCE_SCRIPT, "scripts/customresource.js" );
064: * </pre>
065: *
066: * @see TemplateManager
067: * @see com.ecyrd.jspwiki.tags.RequestResourceTag
068: */
069: public class WikiJSPFilter extends WikiServletFilter {
070: /** {@inheritDoc} */
071: public void doFilter(ServletRequest request,
072: ServletResponse response, FilterChain chain)
073: throws ServletException, IOException {
074: WatchDog w = m_engine.getCurrentWatchDog();
075: try {
076: NDC.push(m_engine.getApplicationName() + ":"
077: + ((HttpServletRequest) request).getRequestURI());
078:
079: w.enterState("Filtering for URL "
080: + ((HttpServletRequest) request).getRequestURI(),
081: 90);
082:
083: HttpServletResponseWrapper responseWrapper = new MyServletResponseWrapper(
084: (HttpServletResponse) response);
085:
086: // fire PAGE_REQUESTED event
087: String pagename = DefaultURLConstructor.parsePageFromURL(
088: (HttpServletRequest) request, response
089: .getCharacterEncoding());
090: fireEvent(WikiPageEvent.PAGE_REQUESTED, pagename);
091:
092: super .doFilter(request, responseWrapper, chain);
093:
094: // The response is now complete. Lets replace the markers now.
095:
096: // WikiContext is only available after doFilter! (That is after
097: // interpreting the jsp)
098:
099: try {
100: w.enterState("Delivering response", 30);
101: WikiContext wikiContext = getWikiContext(request);
102: String r = filter(wikiContext, responseWrapper);
103:
104: //String encoding = "UTF-8";
105: //if( wikiContext != null ) encoding = wikiContext.getEngine().getContentEncoding();
106:
107: // Only now write the (real) response to the client.
108: // response.setContentLength(r.length());
109: // response.setContentType(encoding);
110:
111: response.getWriter().write(r);
112:
113: // Clean up the UI messages and loggers
114: if (wikiContext != null) {
115: wikiContext.getWikiSession().clearMessages();
116: }
117:
118: // fire PAGE_DELIVERED event
119: fireEvent(WikiPageEvent.PAGE_DELIVERED, pagename);
120:
121: } finally {
122: w.exitState();
123: }
124: } finally {
125: w.exitState();
126: NDC.pop();
127: NDC.remove();
128: }
129: }
130:
131: /**
132: * Goes through all types and writes the appropriate response.
133: *
134: * @param wikiContext The usual processing context
135: * @param string The source string
136: * @return The modified string with all the insertions in place.
137: */
138: private String filter(WikiContext wikiContext,
139: HttpServletResponse response) {
140: String string = response.toString();
141:
142: if (wikiContext != null) {
143: String[] resourceTypes = TemplateManager
144: .getResourceTypes(wikiContext);
145:
146: for (int i = 0; i < resourceTypes.length; i++) {
147: string = insertResources(wikiContext, string,
148: resourceTypes[i]);
149: }
150:
151: //
152: // Add HTTP header Resource Requests
153: //
154: String[] headers = TemplateManager.getResourceRequests(
155: wikiContext, TemplateManager.RESOURCE_HTTPHEADER);
156:
157: for (int i = 0; i < headers.length; i++) {
158: String key = headers[i];
159: String value = "";
160: int split = headers[i].indexOf(':');
161: if (split > 0 && split < headers[i].length() - 1) {
162: key = headers[i].substring(0, split);
163: value = headers[i].substring(split + 1);
164: }
165:
166: response.addHeader(key.trim(), value.trim());
167: }
168: }
169:
170: return string;
171: }
172:
173: /**
174: * Inserts whatever resources
175: * were requested by any plugins or other components for this particular
176: * type.
177: *
178: * @param wikiContext The usual processing context
179: * @param string The source string
180: * @param type Type identifier for insertion
181: * @return The filtered string.
182: */
183: private String insertResources(WikiContext wikiContext,
184: String string, String type) {
185: if (wikiContext == null) {
186: return string;
187: }
188:
189: String marker = TemplateManager.getMarker(wikiContext, type);
190: int idx = string.indexOf(marker);
191:
192: if (idx == -1) {
193: return string;
194: }
195:
196: log.debug("...Inserting...");
197:
198: String[] resources = TemplateManager.getResourceRequests(
199: wikiContext, type);
200:
201: StringBuffer concat = new StringBuffer(resources.length * 40);
202:
203: for (int i = 0; i < resources.length; i++) {
204: log.debug("...:::" + resources[i]);
205: concat.append(resources[i]);
206: }
207:
208: string = TextUtil.replaceString(string, idx, idx
209: + marker.length(), concat.toString());
210:
211: return string;
212: }
213:
214: /**
215: * Simple response wrapper that just allows us to gobble through the entire
216: * response before it's output.
217: *
218: * @author jalkanen
219: */
220: private static class MyServletResponseWrapper extends
221: HttpServletResponseWrapper {
222: private CharArrayWriter m_output;
223:
224: /**
225: * How large the initial buffer should be. This should be tuned to achieve
226: * a balance in speed and memory consumption.
227: */
228: private static final int INIT_BUFFER_SIZE = 4096;
229:
230: public MyServletResponseWrapper(HttpServletResponse r) {
231: super (r);
232: m_output = new CharArrayWriter(INIT_BUFFER_SIZE);
233: }
234:
235: /**
236: * Returns a writer for output; this wraps the internal buffer
237: * into a PrintWriter.
238: */
239: public PrintWriter getWriter() {
240: return new PrintWriter(m_output);
241: }
242:
243: public ServletOutputStream getOutputStream() {
244: return new MyServletOutputStream(m_output);
245: }
246:
247: class MyServletOutputStream extends ServletOutputStream {
248: CharArrayWriter m_buffer;
249:
250: public MyServletOutputStream(
251: CharArrayWriter aCharArrayWriter) {
252: super ();
253: m_buffer = aCharArrayWriter;
254: }
255:
256: public void write(int aInt) {
257: m_buffer.write(aInt);
258: }
259:
260: }
261:
262: /**
263: * Returns whatever was written so far into the Writer.
264: */
265: public String toString() {
266: return m_output.toString();
267: }
268: }
269:
270: // events processing .......................................................
271:
272: /**
273: * Fires a WikiPageEvent of the provided type and page name
274: * to all registered listeners of the current WikiEngine.
275: *
276: * @see com.ecyrd.jspwiki.event.WikiPageEvent
277: * @param type the event type to be fired
278: * @param pagename the wiki page name as a String
279: */
280: protected final void fireEvent(int type, String pagename) {
281: if (WikiEventManager.isListening(m_engine)) {
282: WikiEventManager.fireEvent(m_engine, new WikiPageEvent(
283: m_engine, type, pagename));
284: }
285: }
286:
287: }
|