When there’s an existing service that you want to customize or implement differently, you can override the existing one. To do this, you create and deploy a new, higher-ranked service implementation. But how do you replace a component’s service that’s bound by a static and reluctant reference? Reactivating the component would bind it to the new service but would render the component temporarily inactive. To replace the service and keep the component active, you can change the component’s service reference to target your new service.
Here are the steps for overriding a component’s service reference:
- Find the component and service details.
- Create a custom service.
- Configure the component to use the custom service.
Throughout the tutorial, example modules override-my-service-reference
and
overriding-service-reference
are used. You can download them and build them
using Gradle (bundled with each module) or you can apply the tutorial steps to
configure your own customization. Executing gradlew jar
in each example module
root generates the module JAR to the build/libs
folder.
-
override-my-service-reference
(download): This module’s portlet componentOverrideMyServiceReferencePortlet
’s field_someService
references a service of typeSomeService
. The reference’s policy is static and reluctant. By default, it binds to an implementation calledSomeServiceImpl
. -
overriding-service-reference
(download): Provides a customSomeService
implementation calledCustomServiceImpl
. The module’s configuration file overridesOverrideMyServiceReferencePortlet
’sSomeService
reference so that it binds toCustomServiceImpl
.
The first step to overriding a service reference is finding the name of the component, service reference, and service interface. If you already have them, you can skip the next section.
Find the Component and Service Reference
You must have the following information to create a custom service and configure the component to use it.
- Component name: Name of the component whose service to replace.
- Reference name: Name of the component field that references the service.
- Service interface: Fully qualified name of the referenced service interface.
You can find the component using Liferay DXP’s Application Manager and find the service reference information using Felix Gogo Shell.
Gogo Shell’s Service Component Runtime (SCR) commands help you inspect
components. The Gogo Shell command scr:info [componentName]
lists the
component’s attributes, including the services it uses. Execute the command
using
Liferay Blade CLI or in
Gogo Shell via telnet.
Here’s an example of executing the scr:info
command in a Gogo Shell telnet
session:
scr:info override.my.service.reference.OverrideMyServiceReference
The resulting SCR information includes the component’s service references.
For example, the following information describes the component’s reference to
service SomeService
:
...
Component Description:
Name: override.my.service.reference.portlet.OverrideMyServiceReferencePortlet
...
Reference: _someService
Interface Name: override.my.service.reference.service.api.SomeService
Cardinality: 1..1
Policy: static
Policy option: reluctant
Reference Scope: bundle
...
Copy the following values from the command results. You’ll use them in the custom service and service reference configuration you create later.
- Component: The component name you passed to the
scr:info
command. - Reference: The Reference value.
- Interface: The Interface Name in the Reference section.
Note that the example result confirm’s that the reference’s policy and policy
option are static
and reluctant
, respectively.
Here are the values for the example:
- Component name:
override.my.service.reference.portlet.OverrideMyServiceReferencePortlet
- Reference name:
_someService
- Service interface:
override.my.service.reference.service.api.SomeService
The scr:info
result’s component configuration describes the service component
implementation bound to the reference.
...
Component Configuration:
ComponentId: 2399
State: active
SatisfiedReference: _someService
Target: null
Bound to: 6840
Properties:
component.id = 2400
component.name = override.my.service.reference.service.impl.SomeServiceImpl
objectClass = [override.my.service.reference.service.api.SomeService]
service.bundleid = 524
service.id = 6840
service.scope = bundle
...
The example’s reference is bound to a component named
override.my.service.reference.service.impl.SomeServiceImpl
. By the end of this
tutorial, the reference will be reconfigured to bind to a custom service
implementation.
Once you’ve found the referenced service component implementation, you can implement a replacement for it. If you’ve already created one, you can skip this section.
Create Your Service
It’s time to create your own service implementation. Refer to the appropriate app, app suite, and Liferay DXP module Javadoc for service interface details.
Create a
module and
implement your service in it. Use the @Component
annotation to make the
service a Declarative Services component.
The example custom implementation for service SomeService
looks like this:
@Component(
immediate = true,
service = SomeService.class
)
public class CustomServiceImpl implements SomeService {
@Override
public String doSomething() {
StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName());
sb.append(", which delegates to ");
sb.append(_defaultService.doSomething());
return sb.toString();
}
@Reference (
unbind = "-",
target = "(component.name=override.my.service.reference.service.impl.SomeServiceImpl)"
)
private SomeService _defaultService;
}
The service component above refers to the default service so that it can
delegate work to it in its doSomething
method. The reference targets
the default service by its component name
override.my.service.reference.service.impl.SomeServiceImpl
.
To register your service with the Liferay DXP’s OSGi runtime framework, deploy its module. To bind the component reference to your custom service, you must create and deploy instructions that configure the component reference to target your custom service.
Configure the Component to Use Your Service
You’re ready to change the component’s service reference to target your service. Liferay DXP’s Configuration Admin lets you use configuration files to swap in service references on the fly.
-
Create a configuration file named after the referencing component. Here’s the example component’s configuration file name:
override.my.service.reference.portlet.OverrideMyServiceReferencePortlet.config
-
In the configuration file, add a reference target entry that filters on your custom service. Follow this format for the entry:
[reference].target=[filter]
Replace
[reference]
with the name of the reference you’re overriding. Replace[filter]
with service properties that filter on your custom service.A
.config
file reference target entry for the example looks like this:_someService.target="(component.name\=overriding.service.reference.service.CustomServiceImpl)"
The
.config
file syntax requires surrounding the value in double quotes and escaping the value’s equals sign.A
.cfg
file entry for the example looks like this:_someService.target=(component.name=overriding.service.reference.service.CustomServiceImpl)
-
Optionally, you can add a
cardinality.minimum
entry to specify the number of services the reference can use. Here’s the format:[reference].cardinality.minimum=[int]
Here’s an example cardinality minimum:
_someService.cardinality.minimum=1
-
Deploy the configuration by copying the configuration file into the folder
[Liferay_Home]/osgi/configs
.
Executing scr:info
on your component shows that the custom service
implementation is now bound to the reference.
For example, executing
scr:info override.my.service.reference.portlet.OverrideMyServiceReferencePortlet
reports the following information:
...
Component Description:
Name: override.my.service.reference.portlet.OverrideMyServiceReferencePortlet
...
Reference: _someService
Interface Name: override.my.service.reference.service.api.SomeService
Cardinality: 1..1
Policy: static
Policy option: reluctant
Reference Scope: bundle
...
Component Configuration:
ComponentId: 2399
State: active
SatisfiedReference: _someService
Target: (component.name=overriding.service.reference.CustomServiceImpl)
Bound to: 6841
Properties:
_defaultService.target = (component.name=overriding.service.reference.service.CustomServiceImpl)
component.id = 2398
component.name = overriding.service.reference.service.CustomServiceImpl
objectClass = [override.my.service.reference.service.api.SomeService]
service.bundleid = 525
service.id = 6841
service.scope = bundle
Component Configuration Properties:
_someService.target = (component.name=overriding.service.reference.service.CustomServiceImpl)
...
The example component’s _someService
reference targets custom service
component overriding.service.reference.service.CustomServiceImpl
.
CustomServiceImpl
references default service SomeServiceImpl
to delegates
work to it.
Liferay DXP processed the configuration file and injected the service reference, which in turn bound the custom service to the referencing component!