For those of you who are already familiar with Lagom services, this blog post should be a breeze, and you can skip directly to Section B, where the primary focus is to differentiate between three possible ways to implement a service call in Lagom. And for those of you who are new to Lagom, I’ll make sure there is a small brief and enough links to get you acquainted with Section A. Also, in Section C, I’ve shared a small demo on Java with Maven as build tool, so that you can download and try different aspects of playing with headers in Lagom.
A. Lagom Services – Overview
As you may have seen in the documentation of Lagom for implementing services, services are implemented through the service descriptor interface, allowing us to create our own services in a matter of minutes, by providing simple specifications. To give a small brief, we need 3 primary ingredients to kick-start our lagom service:
- The API module,
- The implementation or IMPL module, and
- A module binding definition for first two ingredients
The API specifies the path and the method signature for the service call as below. The only important thing here (other than the basics of inheritance) is that the number of parameters in the method signature should be the same as the total number of path parameters and query parameters in our service call.
The implementation part can be provided further in two ways:
- By providing the Host and pathCall to an external service, i.e., as an unmanaged service. We won’t be elaborating much on this aspect in this blog post.
- By providing your own implementation to the method signature in the service-impl module, as below. Do note that we are using the functional interface ServiceCall to provide this implementation, and in order for the service to work correctly, the implementation should always return either a ServiceCall or an interface that implements it.
We have two different ways to bind the different kinds of implementations with our api.
- For unmanaged services, we use the bindClient method, which automatically binds our API calls with the external service, that we define in our pom.xml (in case of maven based lagom project, for example). In this case, it doesn’t matter what our method signature is named, but the pathcall should be the same as that of the external service.
- For managed services, we use the bindService method to bind the method signatures concerned with our API calls with their implementations in the IMPL module. In this case, it doesn’t matter how we define our path call, as long as the number of parameters are the same in the path call and the method signature.
B. HeaderServiceCall – What, Why and How?
Lagom has provided two more functional interfaces other than the basic interface of ServiceCall for implementing our services: ServerServiceCall and HeaderServiceCall.
Let’s pick the three interfaces and look at their benefits on by one.
The interface ServiceCall has the following methods that allow us to play around with request and response entities in the following ways.
- invoke(): Simply takes as parameter the request data and gives back the response data. This should only be used when the request message is not used.
- handleRequestHeader(Function handler): This one allows us to use the request header to somewhat manipulate or impact the processing of our request.
- handleResponseHeader(BiFunction handler): This one allows us to use the response header to somewhat manipulate the response returned.
- withResponseHeader(): This is simply a convenience method for invoking handleResponseHeader with the default response header.
However, the interface does not allow us the use of request and response headers, unless we use it to call another method (or service call) that uses ServerServiceCall or HeaderServiceCall. You can see this bit in the demo project that is shared in section C, how I’ve forcefully added my own header, while I’m calling a ServerServiceCall from ServiceCall. However, I wasn’t able to read the request header passed to the ServiceCall and neither was I able to modify the response header.
Now, in the real world scenarios, just passing along the response data is not enough. We need to play with the request and response headers as well. In more technical terms, when dealing with server service calls, it is never a good idea to invoke a service call without headers, which is the primary limitation of ServiceCall. This interface allows the utility to do the pass request and response header to the service call when the method call is being invoked programmatically. (as you can see in the demo when I call the method readServerServiceCall from within the method readServiceCall). How?
- By defining the method invokeWithHeaders(RequestHeader, Object) such that it takes as parameters a Pair of RequestHeader and the Request data, and returns a Pair of ResponseHeader and Response data.
- By overriding the methods handleResponseHeader and handleRequestHeader of ServiceCall to, internally, first call invokeWithHeaders and then call the invoke method with headers.
However, again, ServerServiceCall doesn’t allow us to access the request header passed to this service, neither does it allow to return a custom response header along with response entity, unless we call it programmatically and invoke it with custom request and response headers. But again, what’s the use of doing that if we can’t use it for validating our requests or for manipulating our response? Please feel free to drop your comments if you disagree or have an alternate theory.
If you want to really handle the headers in a service call, it is recommended by Lagom that you use HeaderServiceCall, rather than its parents.
And if you still ask why?
Firstly, unlike its parents, it makes the invokeWithHeaders method abstract, allowing the service call to be implemented as a lambda.
Secondly, It even provides a convenience method for providing a HeaderServiceCall when a method accepts a less specific type, eg, a ServerServiceCallfor. All you have to do is use the static method of, as in simply implement the lambda HeaderServiceCall.of((requestHeader, request) -> ???) , and you are good to go.
Thirdly, it prohibits the usage of the invoke() method directly and overrides it to throw UnsupportedOperationException, making sure that the request and response headers are always dealt with, even if it is passed as the default one.
And last but not the least, with HeaderServiceCall into play, you can use your request header for purposes like authentication and validation, and manipulate the response header as per your requirement. Your service will be able to override the default response header (ResponseHeader.OK with status code 200) and return different response headers with varying status codes (401, 404, 500, etc.) along with multiple headers in the response header. You can see the same demonstrated the method down below:
Here is a small demo of all that we have discussed above. Clone the git project, lagom-header-service, on your system and play around with the three types of service calls.
There are many ways yet in which we can play with headers, but Lagom recommends us to use HeaderServiceCall for the same. If you have any questions, queries or suggestions, please drop a comment below. Like & share this blog, we are all here to learn! Cheers. 🙂