Announcing RingCentral Java SDK 1.0

Tyler Liu
RingCentral Developers
8 min readOct 22, 2019

We just released a new RingCentral SDK for Java to Bintray:

https://bintray.com/tylerlong/maven/ringcentral

It is currently in beta and we will release a stable version soon. There are so many improvements and changes (most of them are backwards compatible) that I think it deserves a blog article.

What’s New in the RingCentral Java SDK 1.0?

  1. Take advantages of Kotlin programming language
  2. Separate packages for PubNub support
  3. Better exception reporting
  4. Namespaced classes to avoid naming collision
  5. Extensive comments
  6. Fewer dependencies
  7. Support all API request content types
  8. Better SCIM API support
  9. By default no background timers to refresh tokens
  10. Get rid of Java generics
  11. Support multiple query parameters with the same key
  12. Almost all code is now auto-generated
  13. New HttpEventListener
  14. Expose low level API
  15. Sample code for all the endpoints

Take advantage of Kotlin programming language

Kotlin is a great fit for developing server-side applications, allowing you to write concise and expressive code while maintaining full compatibility with existing Java-based technology stacks and a smooth learning curve.

Kotlin is fully compatible with all Java-based frameworks, which lets you stay on your familiar technology stack while reaping the benefits of a more modern language.

We considered some of the code in previous version of the Java SDK “verbose”. So we rewrote part of the codebase with Kotlin programming language. Overall we think Kotlin code is more readable and easier to maintain. Also since Kotlin is 100% compatible with Java, this change has no impact to Java developers.

Separate packages for PubNub support

The RingCentral API supports two types of push notifications, WebHooks and PubNub. Earlier versions of our Java SDK included PubNub support directly within the core SDK. However, not all RingCentral SDK users need PubNub and the PubNub SDK is quite heavyweight, so it makes more sense to move PubNub to a separate package: ringcentral-pubnub.

Better exception reporting

With the old RingCentral Java SDK, we often get support cases from developers reporting that things don’t work as expected. Developers would encounter exceptions, but they would not know what the issue is because the old RingCentral Java SDK provides too limited information with the exception object. It provides only the short error message. Sometimes we also need to know the request body, request headers, full response body and response headers.

With the new SDK v1.0, things have changed a lot. Whenever there is an HTTP exception, a RestException will be thrown, with an exception message that contains every detail about the HTTP request, including the request and response body, headers, etc.

Below is an example exception thrown by the new RingCentral Java SDK 1.0:

With the information above, I clearly know what is wrong and I also clearly see the request and response. Did I write any code to extract the information above? Nope, it’s not necessary. The SDK prepares the exception message for me.

Namespaced classes to avoid naming collision

We create classes using Restful APIs — let’s take a look at a call log for example:

/restapi/v1.0/account/{accountId}/call-log/restapi/v1.0/account/{accountId}/extension/{extensionId}/call-log

There are two call-log endpoints and, in this case, this call-log could be either the account level call log or the extension level call log. And fortunately no matter which level it is, their data structure is the same, so we don’t need to distinguish them.

But there are cases that there are naming collisions, for example:

/restapi/v1.0/account/{accountId}/service-info/restapi/v1.0/account/{accountId}/extension/{extensionId}/meeting/service-info

There are two service-info endpoints. One is for account while the other is for meeting. They are two completely different things, so that we need to create two different classes. Problem is we cannot have two classes with the same name, unless they are in different namespaces.

Previously, we used a hacked way to solve the problem, we made exceptions and renamed the second service-info to meeting-service-info. It’s not an ideal approach, because it might cause confusion to developers. We prefer a set of pre-defined rules to follow with ideally no exceptions.

In the latest change, we put things that might name-collide into different namespaces. So that they can have their original names without any collision.

Extensive comments

RingCentral’s API endpoints are continuously growing, so we’re offering more and more features via our RESTful API. Developers and even internally and externally face a challenge: where can we find information about the features? Especially when there are lots of request parameters.

Let’s take the GetTokenRequest class for example:

With smart IDEs, developers can navigate to the classes and read comments with ease.

Compared to the old versions of the SDK, we provide more information via comments:

  • Enum: possible values for a property
  • Maximum: maximum value for a property
  • Minimum: minimum value for a property
  • Default: default value for a property
  • Required: whether this property is required

With extensive comments, developers can get almost all the information they need while developing, without leaving their IDEs.

Fewer dependencies

Now the library only depends on com.alibaba.fastjson and com.squareup.okhttp3.okhttp if you don’t need PubNub subscriptions.

Support all API request content types

We added support for all the content type consumed by the RingCentral server:

  • multipart/form-data
  • text/*
  • application/json
  • application/x-www-form-urlencoded

Previously we didn’t support text/* because it is rarely used. It is only needed when you want to update the text of a Glip team messaging post.

Better SCIM API support

The SCIM API is special and hard to support because it has special property names, such as :

urn:ietf:params:scim:schemas:extension:enterprise:2.0:User

Most programming languages don’t support special characters in property names. We fixed it by defining @JSONField mapping:

@JSONField(name = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User")
public EnterpriseUser urn_ietf_params_scim_schemas_extension_enterprise_2_0_User;

By default no background timers to refresh tokens

Previously, the SDK defaulted to start background timers to refresh tokens. Some developers want to manage the tokens themself and it’s annoying that the tokens would refresh without their explicit instructions.

Automatic refresh might be useful in some quick and dirty prototype projects, but it is not a reliable way to keep the token alive, because for each token, you need to have a RestClient instance running 24x7, otherwise there is no timer to refresh it. Developers need to write token management code anyway for serious applications, so it’s better to just get rid of the auto refresh feature.

There are several solutions for token refresh, please choose one based on your requirements:

  • If your app is simple and each run won’t last longer than an hour. You don’t need to bother refreshing the token, because a new access token is valid for an hour.
  • For some simple apps, it is also viable to do authorization to get new token for every run, especially for private apps which use password flow authentication. Getting a new token for every run, then there is no need to refresh an existing token.
  • Create a background timer (or use system provided cron job solution) to do token refresh. You can set the timer to run every half hour, so that you always have a valid access token.
  • Refresh on demand by catching token expired exceptions. Whenever you get such an exception, then you know it’s time to refresh token.
  • If you call the API very infrequently, you can just do a refresh explicitly before each API call. Or you can validate the token before each API call to decide whether you should refresh the token or not. A reliable way to determine token validness is to issue a real API call.

Just in case you really miss the token auto refresh feature. We provided you with a sugar method rc.autoRefresh() . This method creates a background timer to refresh the token every 30 minutes.

Get rid of Java generics

We have the feeling that Java generics has more constrains than C# generics.

Java uses the notion of type erasure to implement generics. In short the underlying compiled classes are not actually generic. They compile down to Object and casts. In effect Java generics are a compile time artifact and can easily be subverted at runtime.

C# on the other hand, by virtue of the CLR, implement generics all they way down to the byte code. The CLR took several breaking changes in order to support generics in 2.0. The benefits are performance improvements, deep type safety verification and reflection.

Previously, when you invoked an API call:

GetMessageInfoResponse messageInfo = restClient.restApi().account().extension().sms()
.post(postParameters, GetMessageInfoResponse.class);

The issues is: the IDE/complier cannot tell you what the response type is(GetMessageInfoResponse in this case). You have to look up documentation/API reference, then you write your code. It wasn’t a good experience.

After we got rid of the generics feature, the above API call became:

GetMessageInfoResponse response = rc.restapi().account().extension().sms().post(postParameters)

IDE/compiler will tell you what the return type is, there is no longer a need to guess or look it up. In Java 10 or later versions, the code could be even further simplified to:

var response = rc.restapi().account().extension().sms().post(postParameters)

Support multiple query parameters with the same key

getParameters.type = new String[]{"Sms", "Fax"};

Will be translated to ?type=Sms&type=Fax, which is recognized by RingCentral server side.

Almost all code is now auto-generated

Except for some sugar and utility code. The code is generated based on Swagger / OpenAPI 2.0 spec. As long as future APIs follow supported code generation, as RingCentral releases more APIs, we should be able to easily support them in the SDK by re-generating code.

New HttpEventListener

We use listener pattern to provide you information about Restful API calls. You will get all the details about that HTTP call, including request & response, body & headers.

So if you want to build some kind of middleware, such as auto rate limit threshold, HttpEventListener is a good place to start. For more information, please reference this test case.

Expose low level API

We tried our best to make the SDK developer friendly and take advantage of Java’s types. But if you want to use low level HTTP APIs, we give you the flexibility to do that as well:

ResponseBody responseBody = rc.get("/restapi/v1.0/account/~/extension/~", null);
String responseString = responseBody.string();
GetExtensionInfoResponse extensionInfo = JSON.parseObject(responseString, GetExtensionInfoResponse.class);
assertNotNull(extensionInfo);
assertNotNull(extensionInfo.extensionNumber);

From the example above, you can see that you can specify the API endpoint directly by specifying a string. And you get a ResponseBody object back which could be deserialized to a Java object..

Sample code for all the endpoints

Together with this new SDK, we provide sample code for all the endpoints. So no matter which API endpoint you want to invoke, you can always find code snippets to reference.

Summary

We have made a lot of improvements to RingCentral Java SDK 1.0 and we recommend you to try it now.

To learn more about our SDK’s visit our developer site and if you’re ever stuck make sure to go to our developer forum.

Want to stay up to date and in the know about new APIs and features? Join our Game Changer Program and earn great rewards for building your skills and learning more about RingCentral!

--

--