Customizing JSPs with Dynamic Includes

The liferay-util:dynamic-include tag is a placeholder into which you can inject content—JavaScript code, HTML, and more. The example project demonstrates how to inject content with a dynamic include.

Deploy the Example Project

Start a new Liferay DXP instance by running

docker run -it -m 8g -p 8080:8080 liferay/dxp:2025.q1.6-lts

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

Then, follow these steps:

  1. Download and unzip Customizing JSPs with Dynamic Includes.

    curl https://resources.learn.liferay.com/examples/liferay-n3q9.zip -O
    
    unzip liferay-n3q9.zip
    
  2. From the module root, build and deploy.

    ./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
    
    Note

    This command is the same as copying the deployed jars to /opt/liferay/osgi/modules on the Docker container.

  3. Confirm the deployment in the Liferay Docker container console.

    STARTED com.acme.n3q9.able.web_1.0.0 [1459]
    STARTED com.acme.n3q9.baker.web_1.0.0 [1460]
    
  4. Verify that the example module works. Open your browser to https://localhost:8080.

  5. Navigate to a Site page and click the Edit icon (Edit icon). Add the N3Q9 Baker Portlet to the page. The widget can be found under Sample widgets.

    Add the N3Q9 Baker Portlet to a Site page.

Note how the first two lines come from the N3Q9 Baker Portlet, but the third line is injected from N3Q9 Able module’s dynamic include. This example is composed of two parts:

  • A JSP portlet that names and inserts a dynamic include into a specific line as a placeholder

  • An OSGi module that implements the dynamic include, replacing the placeholder line from the portlet with new JSP code

These modules are useful in different circumstances, and it is important to understand both to implement dynamic includes properly.

Implement the Dynamic Include

The most common use for dynamic includes is to add custom code to Liferay’s default pages and applications without overwriting them entirely. Liferay’s out-of-the-box code contains dynamic includes in places where you might want additional resources, HTML elements, editor modifications, and more. Once you know the dynamic include’s key, you can use it to create a module and inject your content.

  1. Declare the class as an implementation of DynamicInclude with the @Component annotation.

    @Component(service = DynamicInclude.class)
    
  2. In the include method, add your custom content. The sample project uses a simple PrintWriter example.

@Override
public void include(
		HttpServletRequest request, HttpServletResponse response,
		String key)
	throws IOException {

	PrintWriter printWriter = response.getWriter();

	printWriter.println("<h3>Added by N3Q9 Able dynamic include.</h3>");
}
  1. In the register method, specify the dynamic include tag to use. In the sample, the register method targets the dynamic include of Baker module’s view.jsp.
@Override
public void register(DynamicIncludeRegistry dynamicIncludeRegistry) {
	dynamicIncludeRegistry.register("com.acme.n3q9.baker.web#view.jsp");
}

Insert A Dynamic Include

Traditionally, to make a change in the JSP of an out-of-the-box Liferay portlet, you must rewrite it entirely. This gives you full control over the entire portlet, but is difficult to manage. When you upgrade your Liferay instance, Liferay’s changes to the default portlet can conflict with your custom implementation.

If your customization contains several lines of code, it can be challenging to keep the native code separate and up-to-date. Instead, you can insert a dynamic include where you want your customizations to go and implement it in a separate module. This way, you update the portlet by adding a single line of custom code into the default JSP.

To do this, add the liferay-util:dynamic-include tag where you want the dynamic include to be injected. In the sample, the tag is added to the bottom of N3Q9 Baker Portlet’s view.jsp.

<%@ taglib uri="http://liferay.com/tld/util" prefix="liferay-util" %>

<h2>N3Q9 Baker Portlet</h2><br />

<h3>Hello N3Q9 Baker.</h3><br />

<liferay-util:dynamic-include key="com.acme.n3q9.baker.web#view.jsp" />

Make sure the dynamic include key matches the target set in the register() method above.