JSP Overrides Using Portlet Filters

Portlet filters let you intercept portlet requests before they’re processed and portlet responses after they’re processed but before they’re sent back to the client. You can operate on the request and / or response to modify the JSP content. Unlike dynamic includes, portlet filters give you access to all of the content sent back to the client.

We’ll demonstrate using a portlet filter to modify content in Liferay’s Blogs portlet. For reference, you can download the example module.

Create a new module and make sure it specifies these compile-only dependencies, shown here in Gradle format:

dependencies {
    compileOnly group: "javax.portlet", name: "portlet-api", version: "2.0"
    compileOnly group: "javax.servlet", name: "javax.servlet-api", version: "3.0.1"
    compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "2.0.0"
    compileOnly group: "org.osgi", name: "osgi.cmpn", version: "6.0.0"
}

Create an OSGi component class that implements the javax.portlet.filter.RenderFilter interface.

Here’s an example portlet filter implementation for Blogs:

import java.io.IOException;

import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.filter.FilterChain;
import javax.portlet.filter.FilterConfig;
import javax.portlet.filter.PortletFilter;
import javax.portlet.filter.RenderFilter;
import javax.portlet.filter.RenderResponseWrapper;

import org.osgi.service.component.annotations.Component;

import com.liferay.portal.kernel.util.PortletKeys;

@Component(
    immediate = true,
    property = {
         "javax.portlet.name=" + PortletKeys.BLOGS
    },
    service = PortletFilter.class
)
public class BlogsRenderFilter implements RenderFilter {

    @Override
    public void init(FilterConfig config) throws PortletException {

    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain)
         throws IOException, PortletException {

       RenderResponseWrapper renderResponseWrapper = new BufferedRenderResponseWrapper(response);

       chain.doFilter(request, renderResponseWrapper);

       String text = renderResponseWrapper.toString();

       if (text != null) {
         String interestingText = "<input  class=\"field form-control\"";

         int index = text.lastIndexOf(interestingText);

         if (index >= 0) {
          String newText1 = text.substring(0, index);
          String newText2 = "\n<p>Added by Blogs Render Filter!</p>\n";
          String newText3 = text.substring(index);

          String newText = newText1 + newText2 + newText3;

          response.getWriter().write(newText);
         }
       }
    }

}

Make your class a PortletFilter service component by giving it the @Component annotation that has the service attribute service = PortletFilter.class. Target the portlet whose content you’re overriding by assigning it a javax.portlet.name property that’s the same as your portlet’s key. Here’s the example @Component annotation:

@Component(
   immediate = true,
   property = {
           "javax.portlet.name=" + PortletKeys.BLOGS
   },
   service = PortletFilter.class
)

Override the doFilterMethod to operate on the request or response to produce the content you want. The example appends a paragraph stating Added by Blogs Render Filter! to the portlet content:

@Override
public void doFilter(RenderRequest request, RenderResponse response, FilterChain chain)
       throws IOException, PortletException {

    RenderResponseWrapper renderResponseWrapper = new BufferedRenderResponseWrapper(response);

    chain.doFilter(request, renderResponseWrapper);

    String text = renderResponseWrapper.toString();

    if (text != null) {
       String interestingText = "<input  class=\"field form-control\"";

       int index = text.lastIndexOf(interestingText);

       if (index >= 0) {
         String newText1 = text.substring(0, index);
         String newText2 = "\n<p>Added by Blogs Render Filter!</p>\n";
         String newText3 = text.substring(index);

         String newText = newText1 + newText2 + newText3;

         response.getWriter().write(newText);
       }
    }
}

The example uses a RenderResponseWrapper extension class called BufferedRenderResponseWrapper. BufferedRenderResponseWrapper is a helper class whose toString method returns the current response text and whose getWriter method lets you write data to the response before it’s sent back to the client.

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

import javax.portlet.RenderResponse;
import javax.portlet.filter.RenderResponseWrapper;

public class BufferedRenderResponseWrapper extends RenderResponseWrapper {

    public BufferedRenderResponseWrapper(RenderResponse response) {
       super(response);

       charWriter = new CharArrayWriter();
    }

    public OutputStream getOutputStream() throws IOException {
       if (getWriterCalled) {
         throw new IllegalStateException("getWriter already called");
       }

       getOutputStreamCalled = true;

       return super.getPortletOutputStream();
    }

    public PrintWriter getWriter() throws IOException {
       if (writer != null) {
         return writer;
       }

       if (getOutputStreamCalled) {
         throw new IllegalStateException("getOutputStream already called");
       }

       getWriterCalled = true;

       writer = new PrintWriter(charWriter);

       return writer;
    }

    public String toString() {
       String s = null;

       if (writer != null) {
         s = charWriter.toString();
       }

       return s;
    }

    protected CharArrayWriter charWriter;
    protected PrintWriter writer;
    protected boolean getOutputStreamCalled;
    protected boolean getWriterCalled;

}

Once you’ve deployed your module, the portlet’s JSP shows your custom content.

Your portlet filter operates directly on portlet response content. Unlike dynamic includes, portlet filters allow you to work with all of a JSP’s content.

-4 (4 Votes)
Customizing JSPs with Dynamic Includes Previous