Skip to main content

Introducing Core Libraries in Java

· 6 min read

APIMatic has introduced core libraries to provide a stable runtime that powers all functionality of our SDKs. In this release, we have revamped our Java SDKs to improve the code quality and provide better test coverage.

Refer to Introducing core libraries in APIMatic generated SDKs changelog to understand the importance of core libraries in our SDKs.

Core Libraries

The core libraries consist of classes that are used to make up the Java SDK. They help the SDKs to create and execute requests and responses using the best practices of Java language.

Some of the main features that core libraries introduce in our Java SDKs are:

  • Processing of the request
  • Request creation
  • Execution of the request
  • Response handling
  • Unit testing

All these features are rigorously tested to guarantee the highest level of speed and stability. To aid this testing, core interfaces are introduced along with core libraries. These libraries include:

  1. apimatic-core
  2. apimatic-core-interfaces
  3. apimatic-requests-client-adapter

Core Interfaces

Core interfaces provide an abstraction later on top of core libraries. This results in a cleaner interface and an abstraction for the core functionality of these classes for our SDK users. Consequently, it enhances the scalability of our Java SDKs.

These core interfaces are discussed below:

  • HttpClient

    • To send HTTP requests and read the responses
    • HttpClient interface is an integral part of the client functionality which is implemented by OkClient
  • CompatibilityFactory

    • For backward compatibility
  • Authentication

    • To setup methods for authentication
  • Method

    • Enumeration containing HttpMethods

Benefits of Core Libraries in Java SDKs

Here are the benefits of introducing core libraries in Java SDKs.

Wrapped Http Client

The OkHttp client adapter library is used by the Java SDK when sending an HTTP request. Earlier versions of the SDK came with an integrated client implementation for sending HTTP requests using this module. With the addition of core libraries, the implementation of the OkHttp client has been divided into a separate package and is added to the SDK as a dependency.

Previously, the logger was started in the client but now, it is being injected. The client offers a simple and elegant alternative to the SDK for implementing the same capabilities. We now have client support for unit testing to ensure improved component dependability and performance.

/**
* To send HTTP Requests and read the responses.
*
*/
public interface HttpClient {

/**
* Execute a given Request to get string/binary response back.
*
* @param request The given HttpRequest to execute.
* @param endpointConfiguration The overridden configuration for request.
* @return CompletableFuture of Response after execution.
*/
public CompletableFuture<Response> executeAsync(final Request request,
CoreEndpointConfiguration endpointConfiguration);

/**
* Execute a given Request to get string/binary response back.
*
* @param request The given Request to execute.
* @param retryConfiguration The overridden retry configuration for request.
* @return The converted response.
* @throws IOException exception to be thrown while converting response.
*/
public Response execute(final Request request, CoreEndpointConfiguration endpointConfiguration) throws IOException;
}

Improved SDK

The Java SDK is now lighter in size. All utilities have been moved to the core library and all responsibilities of the request creation and response handling also lie with the core libraries, making SDKs clean and easy to maintain.

Cleaner API Call

Simplified and cleaner API call code is the finest benefit added to our Java SDKs through core libraries. Now, in addition to moving all of the serialization, deserialization, and request parameter validation code into the core libraries, we have also included the code for executing API calls and handling errors. Applying authorization parameters to the requests also introduces abstraction.

Before, a Java endpoint used to look like this:

/**
* @param dates Required parameter: Example:
* @return Returns the ServerResponse response from the API call
* @throws ApiException Represents error response from the server.
* @throws IOException Signals that an I/O exception of some sort has occurred.
*/
public ServerResponse dateArray(
final List<LocalDate> dates) throws ApiException, IOException {
HttpRequest request = buildDateArrayRequest(dates);
HttpResponse response = getClientInstance().execute(request, false);
HttpContext context = new HttpContext(request, response);

return handleDateArrayResponse(context);
}

/**
* Builds the HttpRequest object for dateArray.
*/
private HttpRequest buildDateArrayRequest(
final List<LocalDate> dates) {
//validating required parameters
if (null == dates) {
throw new NullPointerException("The parameter \"dates\" is a required parameter and cannot be null.");
}

//the base uri for api requests
String baseUri = config.getBaseUri();

//prepare query string for API call
final StringBuilder queryBuilder = new StringBuilder(baseUri
+ "/query/datearray");

//load all query parameters
Map<String, Object> queryParameters = new HashMap<>();
queryParameters.put("dates", DateTimeHelper.toSimpleDate(dates));

//load all headers for the outgoing API request
Headers headers = new Headers();
headers.add("accept", "application/json");

//prepare and invoke the API call request to fetch the response
HttpRequest request = getClientInstance().get(queryBuilder, headers, queryParameters,
null);

// Invoke the callback before request if its not null
if (getHttpCallback() != null) {
getHttpCallback().onBeforeRequest(request);
}

return request;
}

/**
* Processes the response for dateArray.
* @return An object of type ServerResponse
*/
private ServerResponse handleDateArrayResponse(
HttpContext context) throws ApiException, IOException {
HttpResponse response = context.getResponse();

//invoke the callback after response if its not null
if (getHttpCallback() != null) {
getHttpCallback().onAfterResponse(context);
}

//Error handling using HTTP status codes
int responseCode = response.getStatusCode();

//return null on 404
if (responseCode == 404) {
return null;
}
//handle errors defined at the API level
validateResponse(response, context);

//extract result from the http response
String responseBody = ((HttpStringResponse) response).getBody();
ServerResponse result = ApiHelper.deserialize(responseBody,
ServerResponse.class);

return result;
}

Now, because of core libraries, a Java SDK endpoint looks like this instead:

/**
* @param dates Required parameter: Example:
* @return Returns the ServerResponse response from the API call
* @throws ApiException Represents error response from the server.
* @throws IOException Signals that an I/O exception of some sort has occurred.
*/
public ServerResponse dateArray(
final List<LocalDate> dates) throws ApiException, IOException {
return prepareDateArrayRequest(dates).execute();
}


/**
* Builds the ApiCall object for dateArray.
*/
private ApiCall<ServerResponse, ApiException> prepareDateArrayRequest(
final List<LocalDate> dates) throws IOException {
return new ApiCall.Builder<ServerResponse, ApiException>()
.globalConfig(getGlobalConfiguration())
.requestBuilder(requestBuilder -> requestBuilder
.server(Server.ENUM_DEFAULT.value())
.path("/query/datearray")
.queryParam(param -> param.key("dates")
.value(DateTimeHelper.toSimpleDate(dates)))
.headerParam(param -> param.key("accept").value("application/json"))
.httpMethod(HttpMethod.GET))
.responseHandler(responseHandler -> responseHandler
.deserializer(
response -> ApiHelper.deserialize(response, ServerResponse.class))
.globalErrorCase(GLOBAL_ERROR_CASES))
.build();
}