| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| package org.apache.wiki.ui; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.log4j.Logger; |
| import org.apache.log4j.NDC; |
| import org.apache.wiki.WatchDog; |
| import org.apache.wiki.WikiContext; |
| import org.apache.wiki.WikiEngine; |
| import org.apache.wiki.event.WikiEventManager; |
| import org.apache.wiki.event.WikiPageEvent; |
| import org.apache.wiki.url.DefaultURLConstructor; |
| import org.apache.wiki.util.TextUtil; |
| |
| import javax.servlet.FilterChain; |
| import javax.servlet.FilterConfig; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletOutputStream; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.ServletResponse; |
| import javax.servlet.WriteListener; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.servlet.http.HttpServletResponseWrapper; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.UnsupportedEncodingException; |
| import java.nio.charset.Charset; |
| |
| |
| /** |
| * This filter goes through the generated page response prior and |
| * places requested resources at the appropriate inclusion markers. |
| * This is done to let dynamic content (e.g. plugins, editors) |
| * include custom resources, even after the HTML head section is |
| * in fact built. This filter is typically the last filter to execute, |
| * and it <em>must</em> run after servlet or JSP code that performs |
| * redirections or sends error codes (such as access control methods). |
| * <p> |
| * Inclusion markers are placed by the IncludeResourcesTag; the |
| * default content templates (see .../templates/default/commonheader.jsp) |
| * are configured to do this. As an example, a JavaScript resource marker |
| * is added like this: |
| * <pre> |
| * <wiki:IncludeResources type="script"/> |
| * </pre> |
| * Any code that requires special resources must register a resource |
| * request with the TemplateManager. For example: |
| * <pre> |
| * <wiki:RequestResource type="script" path="scripts/custom.js" /> |
| * </pre> |
| * or programmatically, |
| * <pre> |
| * TemplateManager.addResourceRequest( context, TemplateManager.RESOURCE_SCRIPT, "scripts/customresource.js" ); |
| * </pre> |
| * |
| * @see TemplateManager |
| * @see org.apache.wiki.tags.RequestResourceTag |
| */ |
| public class WikiJSPFilter extends WikiServletFilter { |
| |
| private static final Logger log = Logger.getLogger( WikiJSPFilter.class ); |
| private String m_wiki_encoding; |
| private boolean useEncoding; |
| |
| /** {@inheritDoc} */ |
| public void init( FilterConfig config ) throws ServletException |
| { |
| super.init( config ); |
| m_wiki_encoding = m_engine.getWikiProperties().getProperty(WikiEngine.PROP_ENCODING); |
| |
| useEncoding = !(new Boolean(m_engine.getWikiProperties().getProperty(WikiEngine.PROP_NO_FILTER_ENCODING, "false").trim()).booleanValue()); |
| } |
| |
| public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) |
| throws ServletException, IOException |
| { |
| WatchDog w = m_engine.getCurrentWatchDog(); |
| try |
| { |
| NDC.push( m_engine.getApplicationName()+":"+((HttpServletRequest)request).getRequestURI() ); |
| |
| w.enterState("Filtering for URL "+((HttpServletRequest)request).getRequestURI(), 90 ); |
| HttpServletResponseWrapper responseWrapper; |
| |
| responseWrapper = new JSPWikiServletResponseWrapper( (HttpServletResponse)response, m_wiki_encoding, useEncoding); |
| |
| // fire PAGE_REQUESTED event |
| String pagename = DefaultURLConstructor.parsePageFromURL( ( HttpServletRequest )request, |
| Charset.forName( response.getCharacterEncoding() ) ); |
| fireEvent( WikiPageEvent.PAGE_REQUESTED, pagename ); |
| |
| super.doFilter( request, responseWrapper, chain ); |
| |
| // The response is now complete. Lets replace the markers now. |
| |
| // WikiContext is only available after doFilter! (That is after |
| // interpreting the jsp) |
| |
| try |
| { |
| w.enterState( "Delivering response", 30 ); |
| WikiContext wikiContext = getWikiContext( request ); |
| String r = filter( wikiContext, responseWrapper ); |
| |
| if (useEncoding) |
| { |
| OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream(), |
| response.getCharacterEncoding()); |
| out.write(r); |
| out.flush(); |
| out.close(); |
| } |
| else |
| { |
| response.getWriter().write(r); |
| } |
| |
| // Clean up the UI messages and loggers |
| if( wikiContext != null ) |
| { |
| wikiContext.getWikiSession().clearMessages(); |
| } |
| |
| // fire PAGE_DELIVERED event |
| fireEvent( WikiPageEvent.PAGE_DELIVERED, pagename ); |
| |
| } |
| finally |
| { |
| w.exitState(); |
| } |
| } |
| finally |
| { |
| w.exitState(); |
| NDC.pop(); |
| NDC.remove(); |
| } |
| } |
| |
| /** |
| * Goes through all types and writes the appropriate response. |
| * |
| * @param wikiContext The usual processing context |
| * @param response The source string |
| * @return The modified string with all the insertions in place. |
| */ |
| private String filter(WikiContext wikiContext, HttpServletResponse response ) |
| { |
| String string = response.toString(); |
| |
| if( wikiContext != null ) |
| { |
| String[] resourceTypes = TemplateManager.getResourceTypes( wikiContext ); |
| |
| for( int i = 0; i < resourceTypes.length; i++ ) |
| { |
| string = insertResources( wikiContext, string, resourceTypes[i] ); |
| } |
| |
| // |
| // Add HTTP header Resource Requests |
| // |
| String[] headers = TemplateManager.getResourceRequests( wikiContext, |
| TemplateManager.RESOURCE_HTTPHEADER ); |
| |
| for( int i = 0; i < headers.length; i++ ) |
| { |
| String key = headers[i]; |
| String value = ""; |
| int split = headers[i].indexOf(':'); |
| if( split > 0 && split < headers[i].length()-1 ) |
| { |
| key = headers[i].substring( 0, split ); |
| value = headers[i].substring( split+1 ); |
| } |
| |
| response.addHeader( key.trim(), value.trim() ); |
| } |
| } |
| |
| return string; |
| } |
| |
| /** |
| * Inserts whatever resources |
| * were requested by any plugins or other components for this particular |
| * type. |
| * |
| * @param wikiContext The usual processing context |
| * @param string The source string |
| * @param type Type identifier for insertion |
| * @return The filtered string. |
| */ |
| private String insertResources(WikiContext wikiContext, String string, String type ) |
| { |
| if( wikiContext == null ) |
| { |
| return string; |
| } |
| |
| String marker = TemplateManager.getMarker( wikiContext, type ); |
| int idx = string.indexOf( marker ); |
| |
| if( idx == -1 ) |
| { |
| return string; |
| } |
| |
| log.debug("...Inserting..."); |
| |
| String[] resources = TemplateManager.getResourceRequests( wikiContext, type ); |
| |
| StringBuilder concat = new StringBuilder( resources.length * 40 ); |
| |
| for( int i = 0; i < resources.length; i++ ) |
| { |
| log.debug("...:::"+resources[i]); |
| concat.append( resources[i] ); |
| } |
| |
| string = TextUtil.replaceString( string, |
| idx, |
| idx+marker.length(), |
| concat.toString() ); |
| |
| return string; |
| } |
| |
| /** |
| * Simple response wrapper that just allows us to gobble through the entire |
| * response before it's output. |
| */ |
| private static class JSPWikiServletResponseWrapper extends HttpServletResponseWrapper { |
| |
| ByteArrayOutputStream m_output; |
| private ByteArrayServletOutputStream m_servletOut; |
| private PrintWriter m_writer; |
| private HttpServletResponse m_response; |
| private boolean useEncoding; |
| |
| /** |
| * How large the initial buffer should be. This should be tuned to achieve |
| * a balance in speed and memory consumption. |
| */ |
| private static final int INIT_BUFFER_SIZE = 0x8000; |
| |
| |
| public JSPWikiServletResponseWrapper( HttpServletResponse r, final String wiki_encoding, boolean useEncoding) |
| throws UnsupportedEncodingException { |
| super(r); |
| m_output = new ByteArrayOutputStream(INIT_BUFFER_SIZE); |
| m_servletOut = new ByteArrayServletOutputStream(m_output); |
| m_writer = new PrintWriter(new OutputStreamWriter(m_servletOut, wiki_encoding), true); |
| this.useEncoding = useEncoding; |
| |
| m_response = r; |
| } |
| |
| /** |
| * Returns a writer for output; this wraps the internal buffer |
| * into a PrintWriter. |
| */ |
| public PrintWriter getWriter() |
| { |
| return m_writer; |
| } |
| |
| public ServletOutputStream getOutputStream() |
| { |
| return m_servletOut; |
| } |
| |
| public void flushBuffer() throws IOException |
| { |
| m_writer.flush(); |
| super.flushBuffer(); |
| } |
| |
| class ByteArrayServletOutputStream extends ServletOutputStream |
| { |
| ByteArrayOutputStream m_buffer; |
| |
| public ByteArrayServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) |
| { |
| super(); |
| m_buffer = byteArrayOutputStream; |
| } |
| |
| @Override |
| public void write(int aInt) throws IOException |
| { |
| m_buffer.write( aInt ); |
| } |
| |
| @Override |
| public boolean isReady() |
| { |
| return false; |
| } |
| |
| @Override |
| public void setWriteListener(WriteListener writeListener) |
| { |
| } |
| |
| } |
| |
| /** |
| * Returns whatever was written so far into the Writer. |
| */ |
| public String toString() |
| { |
| try |
| { |
| flushBuffer(); |
| } catch (IOException e) |
| { |
| log.error( e ); |
| return StringUtils.EMPTY; |
| } |
| |
| try |
| { |
| if (useEncoding) |
| { |
| return m_output.toString(m_response.getCharacterEncoding()); |
| } |
| |
| return m_output.toString(); |
| } |
| catch( UnsupportedEncodingException e ) |
| { |
| log.error( e ); |
| return StringUtils.EMPTY; |
| } |
| } |
| } |
| |
| |
| // events processing ....................................................... |
| |
| |
| /** |
| * Fires a WikiPageEvent of the provided type and page name |
| * to all registered listeners of the current WikiEngine. |
| * |
| * @see org.apache.wiki.event.WikiPageEvent |
| * @param type the event type to be fired |
| * @param pagename the wiki page name as a String |
| */ |
| protected final void fireEvent( int type, String pagename ) |
| { |
| if ( WikiEventManager.isListening(m_engine) ) |
| { |
| WikiEventManager.fireEvent(m_engine,new WikiPageEvent(m_engine,type,pagename)); |
| } |
| } |
| |
| } |