Using 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, transforming content, adding or modifying request and response attributes, suspending portlet phases to get user inputs, and more.
Follow these steps to create portlet filters:
-
Identify the target portlet by its full name (e.g.,
com_liferay_blogs_web_portlet_BlogsPortlet
). -
Determine the portlet phase you want to intercept and implement the corresponding portlet filter interface from the
javax.portlet.filter
package.- Action Phase -
ActionFilter
- Event Phase -
EventFilter
- Render Phase -
RenderFilter
- Resource Serving Phase -
ResourceFilter
See Portlets for more information about each portlet phase.
- Action Phase -
-
Declare the portlet filter a Component within the OSGi framework using the
@Component
annotation and identify it as aPortletFilter.class
service.NotePortlet filters are OSGi Declarative Service (DS) Components. Filters can also be applied to a portlet using a
portlet.xml
descriptor or a@PortletLifecycleFilter
annotation. -
Enter the following properties into the
@Component
declaration."javax.portlet.name=[portlet_name]"
: This property sets the filter’s target portlet."service.ranking:Integer=[priority]"
: This property sets the filter’s ranking, with the higher integers executing first. Ensure the filter starts at the beginning of the filter chain by assigning it the highest ranking.
-
Override the filter’s
doFilter
method to add the desired functionality.
The following example uses a RenderFilter
to audit the render phase for the Blogs portlet.
Deploying 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 steps to download, build, and deploy the sample Portlet Filter to the new docker container:
-
Download and unzip the example module.
curl https://resources.learn.liferay.com/dxp/latest/en/liferay-development/liferay-internals/extending-liferay/liferay-b4k8.zip -O
unzip liferay-b4k8.zip
-
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
). -
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]
-
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.
Auditing a Portlet’s Render Phase with a Portlet Filter
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.isInfoEnabled()) {
long count = _count.longValue();
long averageRenderTime = _totalTime.longValue() / count;
_log.info(
"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:
-
Notes the render phase start time.
long startTime = System.currentTimeMillis();
-
Executes the
doFilter
method for theFilterChain
to invoke everyRenderFilter
in the chain.filterChain.doFilter(renderRequest, renderResponse);
-
Calculates the time it takes for the Blogs portlet to complete the render phase.
long renderTime = (System.currentTimeMillis() - startTime) / 1000;
-
Adds the current render time to the total time of all renders.
_totalTime.add(renderTime);
-
Increments the total number of portlet renders.
_count.increment();
-
Uses the
LongAdder
utility to store the portlet’s average render time and total number of renders, and then uses theLog
utility to display these values along with the portlet’s current render time.
if (_log.isInfoEnabled()) {
long count = _count.longValue();
long averageRenderTime = _totalTime.longValue() / count;
_log.info(
"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.