Service invocation

The asset-integration-starter project contains a com.example.service.client.CustomServiceClient class to illustrate the service invocation.

The CustomServiceClient class obtains reference to the SystemGateway object for the system represented by an identifier Foo by calling SystemGatewayFactory.getSystemGateway method with Foo as an argument. SystemGatewayFactory.getSystemGateway method thus gives a handle to any target system by specifying its systemId. Once the handle is obtained in terms of SystemGateway object, it can be used to invoke any service on the respective target system. The following is the corresponding code snippet from CustomServiceClient class:
private SystemGateway systemGateway = SystemGatewayFactory.getSystemGateway("Foo");
Note: System identifier has been hard coded in this example only for the sake of clarity. For real implementations, avoid hard coding system identifiers for invoking the services from the same or any other plugin. Content Integration Framework supports aliased configurations of same system. Aliased configurations are made by specifying the desired system identifier, followed by a separator (space) and desired alias for the system. For example, Foo can be configured in Platform Configuration using any of the following identifiers – Foo, Foo Staging, Foo Prod, Foo Demo etc. More than one configuration can coexist for the same system by making use of aliased system identifiers. Aliased identifiers refer the same plugin at runtime but execute within separate context. Hence, it is important to invoke services within the right context by using the identifiers used in Platform configurations. Plugin can use following API to obtain the identifier thus used in Platform configuration. Using identifiers obtained from this API makes the plugin compatible for aliased configurations.
String systemId = executionContext.getSystemConfig().getIdentifier();

SystemGateway

The com.hcl.unica.system.integration.service.gateway.SystemGateway provides an overloaded method executeService, for executing any service on the target system. One version of this method offers a way to execute any service declared in service declaration files (<ASSET_PICKER_HOME>/conf/custom-plugin-services.yml and <ASSET_PICKER_HOME>/conf/plugin-services.yml) for the respective system. And the other version offers a way to execute an ad hoc HTTP call on the target system without declaring any explicit service for it in the service declaration file. The following are the two versions of the executeService method with their signatures:

  • <RQ, RS> RS executeService(String serviceName, RQ serviceInput, Class<? extends ServiceGateway<RQ, RS>> gatewayClass) throws ServiceExecutionException
    This is a generic method and works with the type parameters RQ & RS. The significance of RQ & RS is same as mentioned earlier. This method helps to execute an already declared service. The invocationDemo method in CustomServiceClient class demonstrates the use of this method. It accepts the following arguments:
    • String serviceName

      This must be the name of service to be executed. Name of the service must exactly match with its corresponding declaration in service declaration file.

    • RQ serviceInput

      This is an input to the service to be executed. The type parameter RQ represents the type of input required for the service being invoked.

    • Class<? extends ServiceGateway<RQ, RS>> gatewayClass

      It must be same as the return value of getServiceInterface method in corresponding service implementation. It helps the Content Integration Framework to identify the right input for the service being executed and returns the output of desired type. The RQ and RS type parameters used for gatewayClass argument represents the type of input supplied on service invocation and the type of response returned by the service on completion, respectively.

    On successful completion, this method returns the object of type represented by the type parameter RS. Thus, the third argument to the executeService method, gatewayClass, governs the type of input that goes into the service and the type of value that service returns.

  • <T> HttpResponse<T> executeService(HttpRequest request, Class<T> expectedResponse) throws ServiceExecutionException
    This is also a generic method, where the type parameter T represents the type of response expected out of the remote HTTP call. It helps to make an ad-hoc HTTP call to the target system without declaring an explicit service for it in service declaration file. The adHocInvocationDemo method in the CustomServiceClient class demonstrates the use of this method. It accepts the following listed arguments:
    • HttpRequest request

      This must be an object of com.hcl.unica.system.model.request.HttpRequest class. HttpRequest provides a builder interface for constructing the object with required details. This object essentially encapsulates the details required for making an HTTP call, such as absolute URL, HTTP request method, HTTP request headers & HTTP request body or HTTP request payload.

    • Class<T> expectedResponse

      This must tell the type of response expected from remote URL. Jackson and JAXB types can also be used. Deserialization of JSON/XML will happen automatically in such case.

    On successful completion, this method returns the object com.hcl.unica.system.model.response.HttpResponse, encapsulating the response object from the remote call. The type of response encapsulated by the HttpResponse will be the same as the expectedResponse argument to the executeService method. The HttpResponse object gives access to the HTTP response status code, response headers, and response cookies, in addition to the response payload.

Both versions of the executeService method can throw the com.hcl.unica.system.integration.exception.ServiceExecutionException or one of its subtypes if anything goes wrong during service execution. The object of this exception can be consulted for the immediate cause of service execution failure. Likewise, if the invoked service represents a REST/HTTP service (ad-hoc service invocations are always HTTP calls), and the failure occurs out of HTTP interaction, an optional HttpResponse object can also be obtained from the exception. In such cases, the HttpServiceExecutionException is thrown by the executeService methods. The presence of HttpResponse depends on whether the HTTP interaction happened or not. The HttpServiceExecutionException might be received because of an exception in any logic executed prior to the actual HTTP call, such as buildRequest method in a declared service.

The executeService method can also throw a SystemNotFoundException if the plugin for the specified target system is not present, or the corresponding system is not onboarded in Unica Platform. Similarly, it can throw a ServiceNotFoundException if the specified service is either not declared in service declaration file or not implemented by the plugin.

Note:
  • You will observe that the type of the input to the custom-service is same as the type used for service implementation in the com.example.service.rest.CustomService class or the com.example.service.functional.CustomService class. The type of output is same as the one used for defining CustomServiceGateway interface whose class object is returned from getServiceInterface method in both versions of CustomService implementations.
  • The com.example.service.rest.CustomService class and the com.example.service.functional.CustomService class represents the same service implemented with two different approaches. The service declaration files in asset-integration-starter project namely the META-INF/rest-content-services.yml and the META-INF/functional-content-services.yml have an entry for custom-service pointing to the respective versions of the factoryClass. These two versions are provided only for illustration purpose. For all practical purposes, only one version of the service implementation is expected by the Content Integration Framework. Irrespective of the approach used for service implementation, the method for service invocation remains the same.

Multi-partitioned clients

From the perspective of service implementation, the ExecutionContext and SystemConfig objects, passed to various callback methods, contain client application and partition specific information. And from the perspective of service invocation, services executed using executeService method, from the SystemGateway class, runs against the system configured for the right client application and the partition of the user accessing Unica Content Integration. Hence, neither the implementation nor the invoker need to work with partitioning and other contextual details, explicitly. Content Integration Framework handles it automatically.