Adding a New Product Type

This tutorial will show you how to add a new product type by implementing three interfaces: CPType, ScreenNavigationCategory, and ScreenNavigationEntry.

Product types can be used to group products that share similar characteristics. Liferay Commerce provides three product types out-of-the-box: Simple, Grouped, and Virtual.

Out-of-the-box product types

Deploy an Example

In this section, we will get an example product type up and running on your instance of Liferay Commerce. Follow these steps:

  1. Start Liferay Commerce.

    docker run -it -p 8080:8080 liferay/portal:
  2. Download and unzip the Acme Commerce Product Type.

    curl -O
  3. Build and deploy the example.

    ./gradlew deploy$(docker ps -lq)


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

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

    STARTED com.acme.c1n4.web_1.0.0
  5. Verify that the example product type was added. Open your browser to https://localhost:8080. Click the Applications Menu (Applications Menu) and navigate to CommerceProducts. Then click on the (+) icon to add a new product. The new product type (“Example”) will be present in the list of types to choose from.


In Liferay Commerce 2.1 and earlier, find the products page by navigating to Control PanelCommerceProducts.

New product type

Congratulations, you’ve successfully built and deployed a new product type that implements CPType.

Next, let’s dive deeper to learn more.

Walk Through the Example

In this section, we will review the example we deployed. We will create two classes: a product type class and a screen navigation entry class for a custom screen. Walk through the following:

Annotate the Product Type Class for OSGi Registration

Our product type class implements the CPType interface:

    property = {
    service = CPType.class
public class C1N4CPType implements CPType {

The product type name must be a unique value so that Liferay Commerce can distinguish our product type from existing product types.

The commerce.product.type.display.order value indicates how far into the list of product types our product type will appear in the UI. For example, the virtual product type has a value of 15. Giving our product type a value of 16 ensures that it will appear immediately after the virtual type.

Review the CPType Interface

Implement the following methods of CPType in the product type class:

public void deleteCPDefinition(long cpDefinitionId) throws PortalException;

This method is where any custom deletion logic for the product type will be added.

public String getLabel(Locale locale);

This returns a text label that describes the product type. See the implementation in for a reference in retrieving the label with a language key.

public String getName();

This returns the name of our product type. This name may be a language key that corresponds to the name that will appear in the UI.

Annotate the Screen Navigation Entry Class for OSGi Registration

Our screen navigation entry class implements both the ScreenNavigationCategory and ScreenNavigationEntry interfaces:

    property = {
    service = {ScreenNavigationCategory.class, ScreenNavigationEntry.class}
public class C1N4ScreenNavigationEntry
    implements ScreenNavigationCategory, ScreenNavigationEntry<CPDefinition> {

It is important to provide a distinct key for the navigation screen class so that Liferay Commerce can distinguish it as a separate screen from the existing screens. Reusing a key that is already in use will override the existing associated navigation screen.

The screen.navigation.category.order and screen.navigation.entry.order values determine what position in the product type screens this screen will appear. For example, the Details screen class has these values set to 10; setting them to 11 will ensure that our custom screen appears after it in the list.

Review the ScreenNavigationCategory Interface

Implement the following methods in the screen navigation entry class:

public String getCategoryKey();

This returns a unique identifier for the category used for the screen navigation entry.

public String getLabel(Locale locale);

This returns a text label for the screen navigation entry that will be displayed in the UI. See the implementation in for a reference in retrieving the label with a language key.

public String getScreenNavigationKey();

This returns a key to indicate where our screen should appear in Liferay. Return the string value "cp.definition.general" so it properly appears among the other screens for products.

Review the ScreenNavigationEntry Interface

Continue to build on the screen navigation entry class with the following methods:

String getCategoryKey();

This returns a unique identifier for the screen navigation category used by our screen.

String getEntryKey();

This returns a unique identifier for the screen navigation entry. It returns the same value as getCategoryKey.

String getScreenNavigationKey();

This is the same method as getScreenNavigationKey for the ScreenNavigationCategory interface. We implemented this method by returning the string value "cp.definition.general".

public void render(
        HttpServletRequest httpServletRequest,
        HttpServletResponse httpServletResponse)
    throws IOException;

This is where we add the code to render a customized screen for our product type.

Complete the Product Type

The product type is comprised of backend logic for deleting the product, logic to render the screen in the navigation menu, and the custom screen itself. Do the following:

Configure the ServletContext for the Module

Define the ServletContext in our ScreenNavigationEntry class using the symbolic name of our bundle so that it can find the JSP in our module:

@Reference(target = "(osgi.web.symbolicname=com.acme.c1n4.web)")
private ServletContext _servletContext

The value we set for osgi.web.symbolicname matches the value for Bundle-SymbolicName in our bnd.bnd file. These values must match for the ServletContext to locate the JSP.

We declare a unique value for Web-ContextPath in our bnd.bnd file so the ServletContext is correctly generated. In our example, Web-ContextPath is set to /c1n4-web. See bnd.bnd for a reference on these values.

Implement the ScreenNavigationEntry’s render Method

public void render(
        HttpServletRequest httpServletRequest,
        HttpServletResponse httpServletResponse)
    throws IOException {

        _servletContext, httpServletRequest, httpServletResponse,

Use a JSPRenderer to render the JSP for our product type’s custom screen (in our example, c1n4.jsp). Provide the ServletContext as a parameter to find the JSP we have created.

Override the ScreenNavigationEntry’s isVisible Method

public boolean isVisible(User user, CPDefinition cpDefinition) {
    if (cpDefinition == null) {
        return false;

    return Objects.equals(
        cpDefinition.getProductTypeName(), getCategoryKey());

Implement logic here to determine when to show the custom screen. In our example, we only check whether the product type from the CPDefinition matches our example product type.

Add the Product Type Deletion Logic to deleteCPDefinition

Our example does not require any logic to be added to deleteCPDefinition.

Add a JSP to Render the Custom Screen

In our example, we are adding a JSP that prints “Hello C1N4.”.

<h1>Hello C1N4.</h1>

Implement any other inputs or actions desired on the custom screen here, such as a form or MVC action command. See MVC Action Command for more information on adding an MVC action command that can be accessed from the JSP.

Add the Language Key to

Add the language key and its value to a file within our module:

c1n4-commerce-product-type=C1N4 Commerce Product Type
c1n4-screen-navigation-entry=C1N4 Screen Navigation Entry

See Localizing Your Application for more information.


Congratulations! You now know the basics for implementing the CPType interface, and have implemented a new product type with its own custom screen to Liferay Commerce.

Additional Information