oo

Auditing Portlet Activity with Portlet Filters

Portlet filters intercept requests and responses at the start of each portlet request processing phase so you can add functionality there. This makes them useful for auditing portlet activities during their render, action, event, and resource serving phases.

Follow these steps to create portlet filters for auditing portlet activities:

  1. Identify the target portlet by its full name (e.g., com_liferay_blogs_web_portlet_BlogsPortlet).

  2. Determine the portlet phase you want to audit and implement the corresponding portlet filter interface from the javax.portlet.filter package.

    See Portlets for more information about each portlet phase.

  3. Declare the portlet filter a Component within the OSGi framework using the @Component annotation and identify it as a PortletFilter.class service.

    note

    Portlet filters are OSGi Declarative Service (DS) Components. Filters can also be applied to a portlet using a portlet.xml descriptor or a @PortletLifecycleFilter annotation. See Portlet 3.0 Specification for details.

  4. Enter the following properties into the @Component declaration.

    • "javax.portlet.name=[portlet_Name]": This property sets the filter’s target portlet.
    • "service.ranking:Integer=100": This property sets the filter’s ranking, with the higher integers executing first. Ensure the filter starts up at the beginning of the filter chain by assigning it the highest ranking.
  5. Override the filter’s doFilter method to audit the desired aspects of the portlet phase.

The following example uses a RenderFilter to audit the render phase for the Blogs portlet.

Deploy the Sample Portlet Filter

Start a new Liferay instance by running

docker run -it -m 8g -p 8080:8080 liferay/portal:7.4.3.112-ga112

Sign in to Liferay at http://localhost:8080. Use the email address test@liferay.com and the password test. When prompted, change the password to learn.

Then, follow these step to download, build, and deploy the sample Portlet Filter to the new docker container:

  1. Download and unzip the example module.

    curl https://resources.learn.liferay.com/dxp/latest/en/liferay-internals/extending-liferay/liferay-b4k8.zip -O
    
    unzip liferay-b4k8.zip
    
  2. Run the following gradlew command to build the JAR file and deploy it to your new Docker container:

    cd liferay-b4k8
    
    ./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
    

    The JAR is generated in the build/libs folder (i.e., b4k8-impl/build/libs/com.acme.b4k8.impl-1.0.0.jar).

  3. Confirm the module was successfully deployed and started via the container console.

    Processing com.acme.b4k8.impl-1.0.0.jar
    STARTED com.acme.b4k8.impl_1.0.0 [1656]
    
  4. Verify the portlet filter is working by adding the Blogs widget to a Site Page.

    Whenever a render request is made to the Blogs portlet, the container console shows a warning message with an audit of its render time, average render time, and total number of renders.

    WARN [http-nio-8080-exec-2][B4K8PortletFilter:54] Blogs portlet rendered in 3 ms with an average of 3 ms out of 1 renders.
    WARN [http-nio-8080-exec-10][B4K8PortletFilter:54] Blogs portlet rendered in 0 ms with an average of 1 ms out of 2 renders.
    

Sample Render Filter Code

The provided sample filter targets the Blogs portlet and audits its render phase using the RenderFilter interface.

@Component(
   property = {
      "javax.portlet.name=com_liferay_blogs_web_portlet_BlogsPortlet",
      "service.ranking:Integer=100"
   },
   service = PortletFilter.class
)
public class B4K8PortletFilter implements RenderFilter {

   @Override
   public void destroy() {
   }

   @Override
   public void doFilter(
         RenderRequest renderRequest, RenderResponse renderResponse,
         FilterChain filterChain)
      throws IOException, PortletException {

      long startTime = System.currentTimeMillis();

      filterChain.doFilter(renderRequest, renderResponse);

      long renderTime = (System.currentTimeMillis() - startTime) / 1000;

      _totalTime.add(renderTime);

      _count.increment();

      if (_log.isWarnEnabled()) {
         long count = _count.longValue();

         long averageRenderTime = _totalTime.longValue() / count;

         _log.warn(
            "Blogs portlet rendered in " + renderTime +
               " ms with an average of " + averageRenderTime +
                  " ms out of " + count + " renders.");
      }
   }

   @Override
   public void init(FilterConfig filterConfig) throws PortletException {
   }

   private static final Log _log = LogFactoryUtil.getLog(
      B4K8PortletFilter.class);

   private final LongAdder _count = new LongAdder();
   private final LongAdder _totalTime = new LongAdder();

}

In this code, the filter is first declared an OSGi DS Component and identified as a PortletFilter.class service. As part of this declaration, it also sets two properties: the first property targets the BlogsPortlet, and the second property sets its priority to 100.

The portlet filter proceeds to implement the RenderFilter interface, which extends the PortletFilter interface. This interface includes three methods (i.e., init, destroy, doFilter) and performs its filtering tasks on both the render request to the Blogs portlet and its response.

  • init: Called when the portlet filter is first deployed to Liferay and initialized within the portlet container.

  • destroy: Called to remove the portlet filter from service.

  • doFilter: Called by the portlet container each time a render request/response pair is passed through the chain due to a client request.

    In this example, doFilter audits the Blogs portlet in the following ways:

    1. Notes the render phase start time.

      long startTime = System.currentTimeMillis();
      
    2. Executes the doFilter method for the FilterChain to invoke every RenderFilter in the chain.

      filterChain.doFilter(renderRequest, renderResponse);
      
    3. Calculates the time it takes for the Blogs portlet to complete the render phase.

      long renderTime = (System.currentTimeMillis() - startTime) / 1000;
      
    4. Adds the current render time to the total time of all renders.

      _totalTime.add(renderTime);
      
    5. Increments the total number of portlet renders.

      _count.increment();
      
    6. Uses the LongAdder utility to store the portlet’s average render time and total number of renders, and then uses the Log utility to display these values along with the portlet’s current render time.

      if (_log.isWarnEnabled()) {
         long count = _count.longValue();
      
         long averageRenderTime = _totalTime.longValue() / count;
      
         _log.warn(
            "Blogs portlet rendered in " + renderTime +
               " ms with an average of " + averageRenderTime +
                  " ms out of " + count + " renders.");
      }
      

    Whenever a render request is made, this doFilter is called.