The first thing you’ll do is create your application’s root folder and the folder structure for its web client module. This module holds portlet classes and the web UI. Before you start creating a skeleton structure for the modules, determine the modules that comprise this version of your application. If your application provides service API and implementation classes (which is the case for all Liferay Service Builder applications), you’ll create separate modules for them. This tutorial assumes the Maven project model, although any build tools or folder structure is permissible.
Here are the steps for creating the folder structure:
-
Create the root folder. It is the new home for your application’s independent modules and configuration files. For example, if your application’s name is Tasks, then your root folder could be tasks.
If your application uses Liferay Service Builder, use the following Blade CLI command to generate the parent folder and service implementation and service API modules in it. If the parent folder already exists, it must be empty. This command names the parent folder after the
APPLICATION_NAME
:blade create -t service-builder -p [ROOT_PACKAGE] [APPLICATION_NAME]
The
*-service
and*-api
module folders are described later in this tutorial. -
Create the folder structure for your web client module. Blade CLI and Maven generate project folder structures based on project templates.
For example, navigate to the root folder (e.g.,
tasks
) and run the following Blade CLI command to generate a generic web client module structure:blade create -t mvc-portlet [APPLICATION_NAME]-web
-
In your
*-web
module, replace the/src/main/java/[APPLICATION_NAME]
folder with your root Java package. For example, if your application’s root package name iscom.liferay.tasks.web
, your class’s folder should be/src/main/java/com/liferay/tasks/web
. Also, remove theinit.jsp
andview.jsp
files from thesrc/main/resources/META-INF/resources
folder. You’ll use your existing application’s JSPs instead of these generated default JSPs. -
Verify that your
*-web
module folder resides in your application’s root folder (marked by[APPLICATION_NAME]
below)’s and your*-web
module’s folder structure looks like this:[APPLICATION_NAME]
[APPLICATION_NAME]-web
src
main
java
[ROOT_PACKAGE]
resources
content
Language.properties
META-INF
resources
bnd.bnd
build.gradle
The remaining steps affect the web client module (
*-web
module). -
The
bnd.bnd
file is used to generate your module’sMANIFEST.MF
file when you build your project. Open it and change it to fit your application. There’s further documentation about configuring your module’sbnd.bnd
. For example, here’s the Liferaydictionary-web
module’sbnd.bnd
:Bundle-Name: Liferay Dictionary Web Bundle-SymbolicName: com.liferay.dictionary.web Bundle-Version: 1.0.6
For a more advanced example, examine the
journal-web
module’sbnd.bnd
:Bundle-Name: Liferay Journal Web Bundle-SymbolicName: com.liferay.journal.web Bundle-Version: 2.0.0 Export-Package:\ com.liferay.journal.web.asset,\ com.liferay.journal.web.dynamic.data.mapping.util,\ com.liferay.journal.web.social,\ com.liferay.journal.web.util Liferay-JS-Config: /META-INF/resources/js/config.js Liferay-Releng-Module-Group-Description: Liferay-Releng-Module-Group-Title: Web Content Web-ContextPath: /journal-web
-
Open the
build.gradle
file. Specify all your module’s dependencies here. The one generated for you is pre-populated with content and default dependencies. Add your module’s dependencies in thedependencies {...}
block.Finding Liferay API Modules lists common Liferay API module’s symbolic names. The Javadoc overviews for Liferay DXP 7.1 and Liferay apps list each module’s symbolic name and version. The Configuring Dependencies tutorial demonstrates finding artifact information and specifying dependencies. Liferay DXP provides many Java packages and entire artifacts at runtime in the OSGi container. Your module is activated after installation once all its dependencies resolve. Unresolved dependencies appear in the log. Here’s an example message:
! could not resolve the bundles: ... Unresolved requirement: Import-Package: ... Unresolved requirement: Require-Capability ...
-
Copy your traditional application’s JSP files into the
/src/main/resources/META-INF/resources
folder. In most cases, all of your application’s JSP files belong in the web client module. -
Copy your portlet classes and supporting classes that aren’t related to Service Builder into their respective package folders in the web client module. Organizing classes into sub-packages can make them easier to manage.
For example, here’s the
journal-web
module’s Java source folder structure:journal-web
- …
src/main/java/com/liferay/journal/web/
asset
- [classes]
configuration
- [classes]
dynamic/data/mapping/util
- [classes]
internal
application/list
- [classes]
custom/attributes
- [classes]
dao/search
- [classes]
- …
social
- [classes]
util
- [classes]
- …
-
Now that the necessary classes are in your client module, you must make them comply with OSGi. If you’re a beginner, we recommend using the Declarative Services component framework because Liferay DXP uses it. This tutorial assumes that you’re using Declarative Services. You can, however, use any other OSGi component framework.
Review your traditional application’s XML files and migrate the configuration and metadata information to the portlet class as component properties. You can do this by adding the
@Component
annotation to your portlet class and adding the necessary properties to that annotation. Examine the mapping of the portlet descriptors to component properties. The end result should look similar to the following example:@Component( immediate = true, property = { "com.liferay.portlet.display-category=category.sample", "com.liferay.portlet.icon=/icon.png", "javax.portlet.name=1", "javax.portlet.display-name=Tasks Portlet", "javax.portlet.security-role-ref=administrator,guest,power-user", "javax.portlet.init-param.clear-request-parameters=true", "javax.portlet.init-param.view-template=/view.jsp", "javax.portlet.expiration-cache=0", "javax.portlet.supports.mime-type=text/html", "javax.portlet.resource-bundle=content.Language", "javax.portlet.info.title=Tasks Portlet", "javax.portlet.info.short-title=Tasks", "javax.portlet.info.keywords=Tasks", }, service = Portlet.class ) public class TasksPortlet extends MVCPortlet { ... }
-
Convert all references of the
portletId
(e.g.,58_INSTANCE_4gtH
) to the class name of the portlet, replacing all periods with underscores (e.g.,com_liferay_web_proxy_portlet_WebProxyPortlet
). -
Migrate your traditional application’s resource actions (if it has any), into your client module. Create the
/src/main/resources/resource-actions/default.xml
file, and copy your resource actions there. Make sure to create thesrc/portlet.properties
file and add the following property:resource.actions.configs=resource-actions/default.xml
As an example, see the Directory application’s
default.xml
.Note that the permissions API has changed in 7.1; adapt your permissions helpers accordingly.
-
Add your language keys to the
src/main/resources/content/Language.properties
file. Only include the language keys unique to your application. Liferay DXP’s language keys are available to all portlet applications.
Awesome! You’ve created your application’s web client module and completed some of the most common tasks for modularizing your portlet classes and UI. To convert other parts of your application this tutorial hasn’t covered, examine the Liferay DXP developer tutorials to see how those parts fit into application modules. The tutorials are divided into popular areas so you can easily find the topical information you need.
Lastly, the table below is a quick reference guide that maps files and Java
packages from a traditional portlet plugin to a module for a fictitious
application called tasks-portlet
.
Traditional Plugin | Module |
---|---|
tasks-portlet/docroot/WEB-INF/src/com.liferay.tasks.asset | tasks-web/src/main/java/com.liferay.tasks.asset |
tasks-portlet/docroot/WEB-INF/src/com.liferay.tasks.portlet | tasks-web/src/main/java/com.liferay.tasks.portlet |
tasks-portlet/docroot/WEB-INF/src/content | tasks-web/src/main/resources/content |
tasks-portlet/docroot/WEB-INF/src/resource-actions | tasks-web/src/main/resources/resource-actions |
tasks-portlet/docroot/WEB-INF/src/portlet.properties | tasks-web/src/main/resources/portlet.properties |
tasks-portlet/docroot/init.jsp | tasks-web/src/main/resources/META-INF/resources/init.jsp |
tasks-portlet/docroot/tasks | tasks-web/src/main/resources/META-INF/resources/tasks |
tasks-portlet/docroot/upcoming_tasks | tasks-web/src/main/resources/META-INF/resources/upcoming_tasks |
Many applications only have a web client module. Larger, more complex applications, such as Liferay Service Builder applications, require additional modules to hold their service API and service implementation logic. You’ll learn how to create these modules next.