Version 4 of the Nexmo Java Client Library Is Here
Published on May 4, 2021

Today we released version 4.0.0 of our Nexmo Java Client Library. While we felt really good about our 3.x versions of the library, we realized that there are a few things holding it back from providing the ideal user experience.

A new major version means a couple of backwards compatibility breaks, but a whole host of shiny new features. I wanted to offer up some migration tips and talk about why we chose this direction.

For a full list of changes, you can find our changelog on the release page

Java 7 Support

It was a difficult decision, but we decided that it was time to drop official support for Java 7. Deciding the next version of Java to target was also a challenge, but we have updated the target version to Java 8.

Oracle Support Ending

Oracle ended public updates of Java 7 back in April of 2015. While they still offer extended support until July of 2020, they have been encouraging users to update for quite some time.

Wikipedia Java Version TableWiki Java Version

It is true that other JDKs are still updated for Java 7, but we believe that it is one of the limiting factors in allowing us to move forward with development.

Recently we Ended Support for Legacy TLS Protocols. Additionally, Maven Central dropped support in May of 2018. This complicates the continuous integration process and makes maintaining the library with Java 7 support extra challenging.

Java Versions Moving Forward

We didn't take this decision lightly and, with the new releases moving to a 6 month release cadence, we don't think future releases will be this drastic. Donald Smith, from Oracle states:

Going from Java 9->10->11 is closer to going from 8->8u20->8u40 than from 7->8->9. It’s scary to see at first when you’re used to major releases about every three years and have a mental model of the huge impact of those major changes. The six-month cadence is not that.

We want to be as inclusive as possible, and have added some features to the client to help with determining the best version of Java to target going forward.

Client Instantiation

Instantiating the NexmoClient object no longer requires you to think about how to authenticate. Instead, you are now provided with a Builder to assist with constructing the client using various configuration options.

Old

Here's how the NexmoClient used to be instantiated for use with both the SMS and Voice API:

AuthMethod tokenAuth = new TokenAuthMethod(NEXMO_API_KEY, NEXMO_API_SECRET);
AuthMethod applicationAuth = new JWTAuthMethod(NEXMO_APPLICATION_ID,
        FileSystems.getDefault().getPath(NEXMO_APPLICATION_PRIVATE_KEY_PATH)
);

NexmoClient client = new NexmoClient(tokenAuth, applicationAuth);

This process required you to understand how the authentication scheme works. We also felt that using names like TokenAuthMethod and JWTAuthMethod weren't intuitive enough.

New

With the new version, this is how you can instantiate NexmoClient for use with both the SMS and voice API:

NexmoClient client = new NexmoClient.Builder()
        .apiKey(NEXMO_API_KEY)
        .apiSecret(NEXMO_API_SECRET)
        .applicationId(NEXMO_APPLICATION_ID)
        .privateKeyPath(NEXMO_PRIVATE_KEY_PATH)
        .build();

You can also provide it with the contents of your private key file, should you be loading this from another source:

NexmoClient client = new NexmoClient.Builder()
        .apiKey(NEXMO_API_KEY)
        .apiSecret(NEXMO_API_SECRET)
        .applicationId(NEXMO_APPLICATION_ID)
        .privateKeyContents(NEXMO_PRIVATE_KEY_CONTENTS)
        .build();

As a user, you should only need to care about the credentials you have. You shouldn't have to concern yourself with which AuthMethod to use with your API key and secret, just provide the Builder with all the credentials that you have.

There are some caveats to providing things this way. If you provide an API key you must also provide either a secret or a signature secret. Failure to do so will cause the build method to throw a NexmoClientCreationException.

The Nexmo Call Control Object

The Nexmo Call Control Object (NCCO) serializers received a major overhaul in this update. Originally, we called our serializers NCCO classes with names like TalkNcco, InputNcco, ConnectNcco. However, this doesn't match the actual naming convention.

Our NCCO Guide says

A Nexmo Call Control Object (NCCO) is a JSON array of actions that is used to control the flow of a Voice API Call.

So, we renamed them to action classes with names like TalkAction, InputAction, and ConnectAction. Additionally, we provided a special collection wrapper called Ncco to tie them all together and handle building the JSON structure.

Old

Here's how you might handle creating an NCCO to have a user record a message:

Route answerRoute = (req, res) -> {
    String recordingUrl = String.format("%s://%s/webhooks/recordings", req.scheme(), req.host());

    TalkNcco intro = new TalkNcco("Please leave a message after the tone, then press #.");

    RecordNcco record = new RecordNcco();
    record.setEventUrl(recordingUrl);
    record.setEndOnSilence(3);
    record.setEndOnKey('#');
    record.setBeepStart(true);

    TalkNcco outro = new TalkNcco("Thank you for your message. Goodbye");

    Ncco[] nccos = new Ncco[]{intro, record, outro};

    res.type("application/json");

    return new ObjectMapper().writer().writeValueAsString(nccos);
};

New

Now, you can do something like this:

Route answerRoute = (req, res) -> {
    String recordingUrl = String.format("%s://%s/webhooks/recordings", req.scheme(), req.host());

    TalkAction intro = new TalkAction.Builder("Please leave a message after the tone, then press #.").build();

    RecordAction record = new RecordAction.Builder()
            .eventUrl(recordingUrl)
            .endOnSilence(3)
            .endOnKey('#')
            .beepStart(true)
            .build();

    TalkAction outro = new TalkAction.Builder("Thank you for your message. Goodbye").build();

    res.type("application/json");

    return new Ncco(intro, record, outro).toJson();
};

Or, you could build the objects and wrap them in an Ncco without creating the additional local variables:

return new Ncco(
        new TalkAction.Builder("Please leave a message after the tone, then press #.").build(),
        new RecordAction.Builder()
                .eventUrl(recordingUrl)
                .endOnSilence(3)
                .endOnKey('#')
                .beepStart(true)
                .build(),
        new TalkAction.Builder("Thank you for your message. Goodbye").build()
).toJson();

The goal with this change was to create a more intuitive experience for creating objects with lots of properties.

For some items, like TalkAction with only a text property, it seems like more code. However, the benefit is fully realized when your actions become a little more complex.

We want to look into other ways we can assist with this, perhaps by using factory methods to provide some easy shortcuts.

Number Insight Requests

Our InsightClient had quite a few methods on it with various parameter combinations. As the number of options for number insight grows, the parameter list is only going to grow with it.

There's a common trend in most of these updates and, once again, we went with the builder pattern.

Old

To perform standard number insight request with Caller ID (CNAM) information you would do something like this:

StandardInsightResponse response = client.getInsightClient()
        .getStandardNumberInsight(INSIGHT_NUMBER, null, true);

Notice that you have to use null in the second parameter in order to get access to the cnam parameter. This feels wrong.

New

Now, you can do this:

StandardInsightRequest request = new StandardInsightRequest.Builder(INSIGHT_NUMBER)
        .cnam(true)
        .build();

StandardInsightResponse response = client.getInsightClient()
        .getStandardNumberInsight(request);

We didn't get rid of the existing method, but we did decide to deprecate it in favor of using the request builders with removal in the next major version.

I talked about how the builder pattern can increase the verbosity of code. So, for the number insight request objects, we have also included some static factory methods for common use cases.

While you can build request objects like this:

AdvancedInsightRequest request = new AdvancedInsightRequest.Builder(INSIGHT_NUMBER).build();

You can also use a provided static factory method like this:

AdvancedInsightRequest request = AdvancedInsightRequest.withNumber(INSIGHT_NUMBER);

We want to play around with these factory methods a bit more, especially for our new action classes.

Scope Limiting

The standard workflow in the 3.x versions has always been to go through NexmoClient to get access to other clients that give access to the API. This is still the case. However, most of our Endpoint and Method classes were declared public. This has made providing updates challenging, because we don't want to break the public interface we've created as it requires a major version update.

As a reminder, you should always use NexmoClient to obtain instances of other clients and access the API:

NexmoClient client = new NexmoClient.Builder()
        .apiKey(NEXMO_API_KEY)
        .apiSecret(NEXMO_API_SECRET)
        .build();

SmsClient smsClient = client.getSmsClient();

TextMessage message = new TextMessage("Acme Inc", TO_NUMBER, "Hello World!");

SmsSubmissionResponse response = smsClient.submitMessage(message);

There shouldn't be any need to instantiate a new Endpoint or Method class, as these are used internally and are subject to change.

We've updated the scope of most internal classes to the package default scope. While this doesn't provide true encapsulation, we're doing this to discourage any use of these classes directly so we can better update them. This required some package changes as well, you may notice that some packages have been removed or renamed.

Conclusion

First off, sorry we broke things! But we hope that these changes can put us in a better position to provide updates in the future.

If you have any issues in the migration process, or notice any oddities with the version 4 library, don't hesitate to Submit an Issue and let us know!

Don't forget to check out our updated building blocks on Nexmo Developer.

Steve CrowVonage Alumni

Steve is a self-proclaimed Mathlete, and King of Snark. He is also a lover of Greyhounds, twisty puzzles, and European Board Games. When not talking math to non-math people, and Java to non-Java people, he can be found sipping coffee and hacking on code.

Ready to start building?

Experience seamless connectivity, real-time messaging, and crystal-clear voice and video calls-all at your fingertips.

Subscribe to Our Developer Newsletter

Subscribe to our monthly newsletter to receive our latest updates on tutorials, releases, and events. No spam.