oo

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:

  1. 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
    
  2. Build and deploy the example.

    cd liferay-t2p5
    
    ./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)
    
    note

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

  3. 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
    
  4. Add the T2P5 Portlet widget from the Samples category to a widget page. The T2P5 Portlet appears.

    You've added the T2P5 Portlet to a page.

  5. 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.

You've added the T2P5 Portlet to a page.

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.

note

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")
}
note

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.

note

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 ActionRequests 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.

note

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.