Invoking a Service Locally
Service Builder services that are deployed to DXP/Portal can be invoked from other classes in the same JVM. These services are local to the classes.
Service Builder services are Declarative Services (DS) Components, which means they’re managed in a container. Consumers request components from the container and the container provides a matching component instance.
A benefit of implementing a class as DS component is that other components it depends on must be available for it to activate. If your component fails to activate because of an unsatisfied component dependency, the runtime framework reports the issue.
Here you’ll invoke a Service Builder service from a portlet DS component. You’ll use an example portlet application that has a form for adding new entries. The form is in a JavaServer Page (JSP). Submitting the form triggers the portlet to invoke the service for creating an entry and persisting it.
Call a Service from a Portlet
Start a new Liferay DXP instance by running
docker run -it -m 8g -p 8080:8080 liferay/dxp:2024.q1.1
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:
-
Download and unzip the example.
curl https://resources.learn.liferay.com/dxp/latest/en/building-applications/data-frameworks/service-builder/service-builder-basics/liferay-t2p5.zip -O
unzip liferay-t2p5.zip
-
Build and deploy the example.
cd liferay-t2p5
./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
NoteThis command is the same as copying module JARs to
/opt/liferay/osgi/modules
on the Docker container. -
Confirm the deployment in the Docker container console.
STARTED com.acme.t2p5.api_1.0.0 STARTED com.acme.t2p5.service_1.0.0 STARTED com.acme.t2p5.web_1.0.0
-
Add the T2P5 Portlet widget from the Samples category to a widget page. The T2P5 Portlet appears.
-
Enter an entry name and description in the text fields and Click Submit. For example,
Name:
Trim the Hedges
Description:
Use hedge clippers to trim the hedges into a nice shape.
A new entry with the name and description appears in the T2P5 Entries list.
You invoked a Service Builder service from a portlet. Learn how it works, starting with the service API.
Examine the Service API
The t2p5-api
module project’s T2P5EntryLocalService
class has a method called addT2P5Entry(String description, String name)
.
The addT2P5Entry
method creates a T2P5Entry
with the given description and name and persists the entry.
The t2p5-service
module project’s T2P5EntryLocalServiceImpl
class implements the T2P5EntryLocalService
interface.
The t2p5-api
module’s bnd.bnd
file declares exports for the com.acme.t2p5.service
package, com.acme.t2p5.model
package, and other packages for consumers to use. Here’s the bnd.bnd
file:
Bundle-Name: Acme T2P5 API
Bundle-SymbolicName: com.acme.t2p5.api
Bundle-Version: 1.0.0
Export-Package:\
com.acme.t2p5.exception,\
com.acme.t2p5.model,\
com.acme.t2p5.service,\
com.acme.t2p5.service.persistence
The t2p5-web
module’s portlet application depends on the T2P5EntryLocalService
class. The web module’s build.gradle
file declares a dependency on the t2p5-api
project.
dependencies {
compileOnly group: "com.liferay.portal", name: "release.portal.api"
compileOnly project(":t2p5-api")
}
For information on finding artifacts and specifying dependencies, please see Configuring Dependencies.
Examine the Portlet
The t2p5-web
module’s T2P5Portlet
class handles requests to add T2P5Entry
instances. Here’s the T2P5Portlet
class:
@Component(
property = {
"com.liferay.portlet.display-category=category.sample",
"javax.portlet.display-name=T2P5 Portlet",
"javax.portlet.init-param.view-template=/view.jsp"
},
service = Portlet.class
)
public class T2P5Portlet extends MVCPortlet {
public void addT2P5Entry(
ActionRequest actionRequest, ActionResponse actionResponse)
throws PortalException {
_t2p5EntryLocalService.addT2P5Entry(
ParamUtil.getString(actionRequest, "description"),
ParamUtil.getString(actionRequest, "name"));
}
@Reference
private T2P5EntryLocalService _t2p5EntryLocalService;
}
T2P5Portlet
is an MVCPortlet
. It has a T2P5EntryLocalService
field called _t2p5EntryLocalService
and an action-handling method called addT2P5Entry
.
The _t2p5EntryLocalService
field’s @Reference
annotation signals the runtime framework to inject a T2P5EntryLocalService
component instance into the field.
For more information on using the @Reference
annotation and acessing services in other ways, see Dependency Injection in Core Frameworks.
The addT2P5Entry
method calls T2P5EntryLocalService
’s addT2P5Entry
method, passing in description and name parameters retrieved from the ActionRequest
.
The portlet’s view.jsp
template (next) submits ActionRequest
s to T2P5Portlet
.
Examine the JSP
The view.jsp
provides a form for adding entries and shows all the current entries.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<%@ page import="com.acme.t2p5.model.T2P5Entry" %><%@
page import="com.acme.t2p5.service.T2P5EntryLocalServiceUtil" %>
<%@ page import="java.util.List" %>
<portlet:defineObjects />
<h4>T2P5 Portlet</h4>
<hr />
<h5>Add T2P5 Entry</h5>
<portlet:actionURL name="addT2P5Entry" var="addT2P5EntryURL" />
<aui:form action="<%= addT2P5EntryURL %>">
<aui:input name="name" type="text" />
<aui:input name="description" type="text" />
<aui:button type="submit" value="submit" />
</aui:form>
<hr />
<h5>T2P5 Entries</h5>
<%
List<T2P5Entry> t2p5Entries = T2P5EntryLocalServiceUtil.getT2P5Entries(-1, -1);
%>
<c:choose>
<c:when test="<%= t2p5Entries.size() > 0 %>">
<table>
<tbody>
<c:forEach items="<%= t2p5Entries %>" var="entry">
<tr>
<td>${entry}</td>
</tr>
</c:forEach>
</tbody>
</table>
</c:when>
<c:otherwise>
<em>There are no T2P5 entries.</em>
</c:otherwise>
</c:choose>
The JSP uses tags from these tag libraries:
- Core JSTL
- Portlet
- Liferay’s Alloy UI (
aui
)
It imports these classes:
T2P5Entry
T2P5EntryLocalServiceUtil
java.util.List
The page’s Add T2P5 Entry section provides a form for adding an entry. The <portlet:defineObjects />
tag makes standard portlet objects available to the template. The aui
tags use these objects.
The <portlet:actionURL name="addT2P5Entry" var="addT2P5EntryURL" />
tag maps the addT2P5EntryURL
variable to a portlet action named addT2P5Entry
. Submitting an ActionRequest
with this actionURL
invokes the portlet’s method addT2P5Entry
because it maps to the actionUrl
name addT2P5Entry
.
The <aui:form>
renders text fields for an entry’s name and description. On submitting the form, its values are passed along with an ActionRequest
to the portlet method.
For more information on portlet actions, see Invoking Actions with MVC Portlet.
The page’s Entries section lists all the entries. It gets all the entries by calling T2P5EntryLocalServiceUtil.getT2P5Entries(-1, -1)
The -1
min and max range values tell the method to return all the entries.
You’ve invoked a Service Builder service from a portlet application. These services are easy to use in MVC Portlet.
What’s Next
Now that you know Service Builder basics, you can explore Defining Entities to create relationships between entities, localize entities, support queries, and more. Or you can dive into Business Logic with Service Builder.