Overriding Reluctant Service References

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:

  1. Find the component and service details
  2. Create a custom service
  3. 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 component OverrideMyServiceReference’s field _someService references a service of type SomeService. The reference’s policy is static and reluctant. By default, it binds to an implementation called SomeServiceImpl.

  • overriding-service-reference (download): Provides a custom SomeService implementation called CustomServiceImpl. The module’s configuration file overrides OverrideMyServiceReference’s SomeService reference so that it binds to CustomServiceImpl.

overriding-service-refs-default-impl.png

Figure 1: Prior to overriding the service reference in example portlet module override-my-service-reference, the portlet’s message indicates it’s calling the default service implementation override.my.service.reference.service.impl.SomeServiceImpl

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 Portal’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.OverrideMyServiceReference
...
Reference: _someService
    Interface Name: override.my.service.reference.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.OverrideMyServiceReference
  • Reference name: _someService
  • Service interface: override.my.service.reference.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.SomeServiceImpl
        objectClass = [override.my.service.reference.SomeService]
        service.bundleid = 524
        service.id = 6840
        service.scope = bundle
...

The example’s reference is bound to a component named override.my.service.reference.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 Portal 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 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 Portal’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 Portal’s Configuration Admin lets you use configuration files to swap in service references on the fly.

  1. Create a configuration file named after the referencing component.

    Here’s the configuration file name for the example component:

    override.my.service.reference.OverrideMyServiceReference.config
    

    Note: For deploying to Liferay DXP DE 7.0 Fix Pack 8 or later, use file suffix .config. For earlier Liferay DXP DE 7.0 Fix Packs or Liferay CE Portal 7.0 GA3 or earlier, use suffix .cfg.

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

    The example configuration file specifies the following reference target entry:

    _someService.target=(component.name=overriding.service.reference.service.CustomServiceImpl)
    
  3. 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
    
  4. 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.OverrideMyServiceReference reports the following information:

...
Component Description:
  Name: override.my.service.reference.OverrideMyServiceReference
  ...
  Reference: _someService
    Interface Name: override.my.service.reference.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=override.my.service.reference.SomeServiceImpl)
            component.id = 2398
            component.name = overriding.service.reference.CustomServiceImpl
            objectClass = [override.my.service.reference.SomeService]
            service.bundleid = 525
            service.id = 6841
            service.scope = bundle
      Properties:
        _someService.target = (component.name=overriding.service.reference.CustomServiceImpl)
        ...

The example component’s _someService reference targets custom service component overriding.service.reference.CustomServiceImpl. CustomServiceImpl references default service SomeServiceImpl to delegates work to it.

overriding-service-refs-result.png

Figure 2: Because the example component’s service reference is overriden by the configuration file deployment, the portlet indicates it’s calling the custom service.

Liferay Portal processed the configuration file and injected the service reference, which in turn bound the custom service to the referencing component!

Related Topics

0 (0 Votes)
Using the WAB Generator Previous