Foros de discusión

Hook - Servlet Filter for html output modification

Gerhards Udo, modificado hace 7 años.

Hook - Servlet Filter for html output modification

New Member Mensajes: 13 Fecha de incorporación: 3/10/12 Mensajes recientes
Hi all,

I'm currently working on a servlet filter which should be able to modify the generated html output (just like the strip filter).

I have already created an according hook with the needed java class(es) and the according liferay-hook.xml. The hook is successfully deployed in liferay. But everytime the filter is invoked by the portal, I only receive an empty response with no content type from the portal, but the page is successfully shown in my browser?!?

My code looks like follows:

liferay-hook.xml:
<!--?xml version="1.0"?-->


<hook>
	<servlet-filter>
		<servlet-filter-name>LiferaSecureEmailFilter</servlet-filter-name>
		<servlet-filter-impl>eu.gerhards.liferay.servlet.LiferaySecureEmailFilterServlet</servlet-filter-impl>
		<init-param>
			<param-name>regex</param-name>
			<param-value>&lt;a\s+(href\s*=\s*["|'](mailto:\s*(.*?))["|'])&gt;(.*?)&lt;/a&gt;</param-value>
		</init-param>
		<init-param>
			<param-name>shift</param-name>
			<param-value>3</param-value>
		</init-param>
		<init-param>
			<param-name>atReplacement</param-name>
			<param-value>[at]</param-value>
		</init-param>
		<init-param>
			<param-name>newHref</param-name>
			<param-value>href="javascript:linkTo_UnCryptMailto(\"%s\",3);"</param-value>
		</init-param>
		<init-param>
			<param-name>ensureContentLength</param-name>
			<param-value>true</param-value>
		</init-param>
	</servlet-filter>
	<servlet-filter-mapping>
		<servlet-filter-name>LiferaSecureEmailFilter</servlet-filter-name>
		<after-filter>Strip Filter</after-filter>
		<url-pattern>/c/*</url-pattern>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>REQUEST</dispatcher>
	</servlet-filter-mapping>
	<servlet-filter-mapping>
		<servlet-filter-name>LiferaSecureEmailFilter</servlet-filter-name>
		<after-filter>Strip Filter</after-filter>
		<url-pattern>/group/*</url-pattern>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>REQUEST</dispatcher>
	</servlet-filter-mapping>
	<servlet-filter-mapping>
		<servlet-filter-name>LiferaSecureEmailFilter</servlet-filter-name>
		<after-filter>Strip Filter</after-filter>
		<url-pattern>/user/*</url-pattern>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>REQUEST</dispatcher>
	</servlet-filter-mapping>
	<servlet-filter-mapping>
		<servlet-filter-name>LiferaSecureEmailFilter</servlet-filter-name>
		<after-filter>Strip Filter</after-filter>
		<url-pattern>/web/*</url-pattern>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>REQUEST</dispatcher>
	</servlet-filter-mapping>
</hook>


Main filter class:

package eu.gerhards.liferay.servlet;

import java.io.IOException;
import java.io.Writer;
import java.nio.CharBuffer;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;

import com.liferay.portal.kernel.io.OutputStreamWriter;
import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.servlet.BufferCacheServletResponse;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ParamUtil;

import eu.gerhards.liferay.match.EmailRegexReplace;

public class LiferaySecureEmailFilterServlet implements Filter {
	
	private Log _log = LogFactoryUtil.getLog(getClass());
	
	private static final String _ENSURE_CONTENT_LENGTH = "ensureContentLength";
    public static final String SKIP_FILTER = LiferaySecureEmailFilterServlet.class.getName() + "SKIP_FILTER";
	
	protected String emailMatchExpression = null;
    private FilterConfig filterConfig = null;
    protected EmailRegexReplace replaceWorker = new EmailRegexReplace();
	private String atReplacement = null;
	private Integer shift = null;
	private String newHref = null;

    public void init(FilterConfig filterConfig)
    {
    	this._log.info("Initializing LiferaySecureEmailFilterServlet ... ");
    	
        this.filterConfig = filterConfig;
		
		if (this.filterConfig != null &amp;&amp; StringUtils.isNotEmpty(filterConfig.getInitParameter("init.param.regex"))) {
			this._log.info("Setting 'regex' parameter:"+ filterConfig.getInitParameter("init.param.regex"));
			this.emailMatchExpression = filterConfig.getInitParameter("init.param.regex").trim();
		}
		
		if (this.filterConfig != null &amp;&amp; StringUtils.isNotEmpty(filterConfig.getInitParameter("init.param.atReplacement"))) {
			this._log.info("Setting 'atReplacement' parameter:"+ filterConfig.getInitParameter("init.param.atReplacement"));
			this.atReplacement  = filterConfig.getInitParameter("init.param.atReplacement").trim();
		}
		
		if (this.filterConfig != null &amp;&amp; StringUtils.isNotEmpty(filterConfig.getInitParameter("init.param.shift"))) {
			this._log.info("Setting 'shift' parameter:"+ filterConfig.getInitParameter("init.param.shift"));
			this.shift  = Integer.valueOf(filterConfig.getInitParameter("init.param.shift").trim());
		}
		
		if (this.filterConfig != null &amp;&amp; StringUtils.isNotEmpty(filterConfig.getInitParameter("init.param.newHref"))) {
			this._log.info("Setting 'newHref' parameter:"+ filterConfig.getInitParameter("init.param.newHref"));
			this.newHref   = filterConfig.getInitParameter("init.param.newHref").trim();
		}
		
		this._log.info("Initializing Filter =&gt; 'pattern':"+this.emailMatchExpression+", 'atReplacement': "+this.atReplacement+", 'newHref': "+this.newHref+", 'shift': "+String.valueOf(this.shift));
       
		this.replaceWorker.init(this.emailMatchExpression, this.atReplacement, this.newHref, this.shift);
    }

    /**
     * Filter the request / response
     * 
     * @param request
     * @param response
     * @param chain
     * 
     * @return
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
    	this._log.info("Filtering response ... ");
    	
    	BufferCacheServletResponse bufferCacheServletResponse = new BufferCacheServletResponse((HttpServletResponse) response);
    	
    	String contentType = GetterUtil.getString(bufferCacheServletResponse.getContentType());
    
    	chain.doFilter(request, response);
    	
    	if (this.replaceContentType(contentType) &amp;&amp; !this.isAlreadyFiltered((HttpServletRequest) request)) {
    		
    		this._log.info("Setting flag in order to stop looping on this filter ...");
    	
	    	request.setAttribute(SKIP_FILTER, Boolean.TRUE);
	    	
	    	CharBuffer oldCharBuffer = bufferCacheServletResponse.getCharBuffer();
	    	
	    	// Ensure content length
	    	boolean ensureContentLength = ParamUtil.getBoolean((HttpServletRequest) request, _ENSURE_CONTENT_LENGTH);
	    	
	    	this._log.info("Ensure content length: '"+ensureContentLength+"'");
	    	
	    	// Replace in case of valid content types
	    	if (bufferCacheServletResponse.getStatus() == HttpServletResponse.SC_OK &amp;&amp; ensureContentLength) {
	    		
	    		this._log.info("Ensure content length is set on request!");
	    		
	    		UnsyncByteArrayOutputStream unsyncByteArrayOutputStream = new UnsyncByteArrayOutputStream();
	            this.replace((HttpServletRequest) request, (HttpServletResponse) response, oldCharBuffer,new OutputStreamWriter(unsyncByteArrayOutputStream));
	            response.setContentLength(unsyncByteArrayOutputStream.size());
	            unsyncByteArrayOutputStream.writeTo(response.getOutputStream());
	    		
	    	} else if (bufferCacheServletResponse.getStatus() == HttpServletResponse.SC_OK &amp;&amp; !response.isCommitted()){
	    		
	    		this._log.info("Normal request!");
	    		
	    		this.replace((HttpServletRequest) request, (HttpServletResponse) response, oldCharBuffer, response.getWriter());
	    		
	    	}
    	}
    }
    
    /**
     * Method to decide if the response should be processed
     * 
     * @param contentType
     * @return
     */
    protected boolean replaceContentType(String contentType) {
    	
    		this._log.info("Checking content type '"+contentType+"' if it can be parsed");
    	
    		return contentType.trim().equals("text/html*") || contentType.trim().equals("text/html");
    }
    
    /**
     * Search and replace all email addresses in response
     * 
     * @param request
     * @param response
     * @param charBuffer
     * @param writer
     * @throws IOException
     */
    protected void replace(HttpServletRequest request, HttpServletResponse response, CharBuffer charBuffer, Writer writer) throws IOException {
    	
    	this._log.info("Replacing all email addresses in content!");
    	
    	this._log.info("Content is ..." + charBuffer.toString());
    	
    	StringBuilder strBuilder = new StringBuilder();
    	strBuilder.append(charBuffer.array());
    	StringBuilder content = this.replaceWorker.process(strBuilder);
    	writer.write(content.toString());
    	writer.flush();
    	
    }
    
    /**
     * This method checks if the filter was already processed on the current request
     * 
     * @param request
     * @return
     */
    protected boolean isAlreadyFiltered(HttpServletRequest request) {
		if (request.getAttribute(SKIP_FILTER) != null) {
			this._log.info("Request was already processed by this filter! Flag is set to "+request.getAttribute(SKIP_FILTER).toString());
			return true;
		} else {
			this._log.info("Request was not already processed by this filter!");
			return false;
		}
	}
    
    public void destroy()
    {
        filterConfig = null;
    }
}


Can someone telling me, what I'm doing wrong and give me a hint and point me in the right direction?

I'm using LR7 on Wildfly.

Thanks a lot.

Best regards

Udo
thumbnail
David H Nebinger, modificado hace 7 años.

RE: Hook - Servlet Filter for html output modification

Liferay Legend Mensajes: 14914 Fecha de incorporación: 2/09/06 Mensajes recientes
Hey, Udo.

I'm wondering if you're not just stepping on your own code by reusing attributes set by the strip filter. If strip filter is already in play, the SKIP_FILTER attribute would already be set so you'd be skipping your own logic.

Try making all of the attribute keys unique to your own filter so there's no overlap involved.






Come meet me at the LSNA!
Gerhards Udo, modificado hace 7 años.

RE: Hook - Servlet Filter for html output modification

New Member Mensajes: 13 Fecha de incorporación: 3/10/12 Mensajes recientes
Hello David,

thanks for your answer.

I got it working now. I did a complete rewrite and now the filter is stripping and compressing the content. And also obfuscating given email addresses in the source code. emoticon

Best regards

Udo