Skip to content

Custom servlet filter hooks

Introduction

Custom servlet filter hook classes operate on a search request before it begins Funnelback processing, and after result rendering, just before a response is returned to the user. For general customization, User interface hook scripts are an easier and more flexible option, however in some advanced cases, it is desirable to use this lower level customization system.

One specific capability enabled by this type of hook is auditing or direct manipulation of the specific byte output returned to Funnelback users.

Developing custom servlet filter hooks

Note: When developing a custom servlet filter hooks it will be useful to be familiar with Java's servlet filter mechanism, on which this mechanism is based.

To implement a custom servlet filter hook, a Groovy class extending the following base class must be implemented at $SEARCH_HOME/conf/$COLLECTION_NAME/GroovyServletFilterHookPublicUIImpl.groovy

package com.funnelback.springmvc.web.filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * Base class for groovy servlet filter hooks within Funnelback's Public UI,
 * allowing code to audit log or manipulate the request/response before it is
 * processed/returned to the user.
 *
 * One instance of this class will be created per request processed.
 *
 * @see com.funnelback.publicui.search.web.filters.GroovyFilter GroovyServletFilter -
 *      Where implementations are invoked
 */
public class GroovyServletFilterHook {

    /**
     * Allows the GroovyServletFilterHook implementation to perform actions on
     * the request before it is populated by Funnelback.
     * 
     * @param request
     *            Representation of the user's original request. May be wrapped
     *            and returned to affect request processing.
     * @return By default, the request provided. Potentially an alternate or
     *         wrapped request.
     */
    public ServletRequest preFilterRequest(ServletRequest request) {
        return request;
    }

    /**
     * Allows the GroovyServletFilterHook implementation to perform actions on
     * the response before it is populated by Funnelback.
     *
     * @param request
     *            Representation of the user's request (useful in limiting
     *            actions to only certain types of requests).
     * @param response
     *            The response object to which Funnelback's response will be
     *            written, unless it is changed/wrapped by this method's return
     *            value.
     * @return By default, the response provided. Potentially an alternate or
     *         wrapped response
     * @see com.funnelback.publicui.search.web.filters.utils.
     *      InterceptableHttpServletResponseWrapper
     *      InterceptableHttpServletResponseWrapper - May be helpful in wrapping
     *      the response.
     */
    public ServletResponse preFilterResponse(ServletRequest request, ServletResponse response) {
        return response;
    }

    /**
     * Allows the GroovyServletFilterHook implementation to perform actions
     * after Funnelback has processed the request.
     *
     * @param request
     *            Representation of the user's original request. Useful as a
     *            reference, but it will be too late to meaningfully modify the
     *            request when this method runs.
     * @param response
     *            Representation of the Funnelback response to the user's
     *            request. Unless the response was wrapped above, it may be too
     *            late to change it, but details could be logged here. If the
     *            response were wrapped appropriately, sending of response
     *            content to the user may have been delayed, making changing of
     *            response headers or even content possible here.
     */
    public void postFilterResponse(ServletRequest request, ServletResponse response) {
    }

}

The methods implemented by this class, preFilterRequest, preFilterResponse and postFilterResponse, allow the hook class to intercept and alter the ServletRequest and ServletResponse objects as necessary.

Note that access to the Funnelback search transaction object model is not available from within this hook class, and that all requests to Funneback's search interface will be processed through these methods, so the performance of their implementations must be considered during development.

Example

This example shows how to write all search request responses to a very simple audit log file

import com.funnelback.springmvc.web.filter.GroovyServletFilterHook;
import com.funnelback.publicui.search.web.filters.utils.InterceptableHttpServletResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.io.output.TeeOutputStream;
  
public class GroovyServletFilterHookPublicUIImpl extends GroovyServletFilterHook {
  
    private ByteArrayOutputStream baos = null;
  
    public ServletResponse preFilterResponse(ServletRequest request, ServletResponse response) {
        // Skip processing on non /s/search.* endpoints
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String path = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
        if (path.startsWith("/search.")) {
 
            baos = new ByteArrayOutputStream();
 
            // We send output back to the user and to the audit-buffer simultaneously, since that's more efficient.
            //
            // One could, instead, just buffer the output and use postFilterResponse to write the output to
            // the original stream only after it's been logged - That would allow an error to be returned
            // if the audit logging failed for some reason (instead of the user still receiving the results
            // anyway)
            OutputStream teeOutputStream = new TeeOutputStream( response.getOutputStream(), baos);
  
            InterceptableHttpServletResponseWrapper wrappedResponse = new InterceptableHttpServletResponseWrapper(response, teeOutputStream);
  
            return wrappedResponse;
        } else {
            return response;
        }
    }
 
    public void postFilterResponse(ServletRequest request, ServletResponse response) {
        if (baos != null) {
            new File("/tmp/audit.log").append(baos.toByteArray());
        }
    }
}

top

Funnelback logo
v15.18.0