In this blog post, we are going to showcase how to update an existing application to version 4 of the SAP Cloud SDK. The new major version has just been released and comes with a lot of under-the-hood improvements as well as some new features. Since updating a dependency – even to a new major version – shouldn’t come with a lot of effort for consumers, we kept public API as stable as possible to keep the process brief and the effort low. In fact, many consumers will probably not even notice any of the breaking changes we did.
This blog post is for those who do notice that some things have changed and want to see what the development team recommends for the migration.
The Demo Application
Although all changes introduced in version 4 of the SAP Cloud SDK are listed in our Documentation, we still want to put them in a more practical perspective. For that, we are going to migrate a small sample application that uses a slightly outdated SAP Cloud SDK 3 version (3.60.0
to be precise) to the latest one. The demo application has initially been created using the scp-cf-spring
SAP Cloud SDK archetype. It’s main purpose is to interact with the SAP Business Partner service.
For that, we have implemented a Spring Controller that looks as follows:
BusinessPartnerContrtoller.java
In the shown code above, we would like to bring your attention to a few things:
- We are using the
.execute(HttpDestinationProperties)
API in ourfetchPartnersWithId
method. Therefore, we have to catch and handle thecom.sap.cloud.sdk.odatav2.connectivity.ODataException
since it’s a checked exception. - In a similar manner, we are using the
getBusinessPartnerAddressOrFetch()
API to avoid eagerly fetching all addresses when requesting our Business Partners. Inconveniently, this method might also throw ancom.sap.cloud.sdk.odatav2.connectivity.ODataException
, which needs to be handled once again. - Lastly, we want to get access to the HTTP headers the user sent to our application. For that, we are using the
RequestAccessor.tryGetCurrentRequest()
API (seecorrespondenceLanguageEqualsAcceptLanguage
method).
Of course, our implementation also has to be tested. Hence, we set up two integration tests that use Wiremock to decouple our controller from the actual SAP S/4HANA Cloud service:
BusinessPartnerControllerTest.java
Once again, please note a few details about these tests:
- We are using the SAP Cloud SDK’s
MockUtil
class to mock aDestination
that points to our Wiremock backend service. - For our tests, we not only have to mock the actual retrieval of the Business Partner, but we also have to make sure our Wiremock server handles requests to the
/$metadata
endpoint and serves a correct.edmx
specification (done in themockMetadataLookUp
method).
In the following chapters, we will demonstrate how the mentioned pieces of code – both for the productive controller as well as for the tests – can be transformed with the new major version of the SAP Cloud SDK.
1. Replace Deprecated API Usages
One of our main goals for the new major version of the SAP Cloud SDK was to get rid of outdated APIs and modules. Therefore, chances are high that consumers will face compilation issues after updating to the SAP Cloud SDK 4 in case deprecated APIs are still used. To avoid these issues, we recommend replacing usages of deprecated APIs already before actually updating to version 4.
For that, make sure you are using the latest patch version for the SAP Cloud SDK 3 and check whether your compiler warns about deprecated API usages.
Update Cloud SDK 3 Patch Version
In our demo application, we are indeed seeing warnings about following usages of deprecated APIs:
BusinessPartnerController#fetchPartnersWithId
: The.execute(HttpDestinationProperties)
API is deprecatedBusinessPartnerContoller#correspondenceLanguageEqualsAcceptLanguage
: TheRequestAccessor
class is deprecated.BusinessPartnerControllerTest
: TheMockUtil
class is deprecated
Having said that, let us begin refactoring our application to get rid of these warnings first.
Tip: Refactoring
Before refactoring code, make sure you have sufficient test coverage for the classes you are planning to touch. During refactoring, run your tests after each change to check that everything still works as expected. If all tests still pass, commit your changes before continuing so that you can jump back to the last working state in case something goes wrong.
Refactoring is done best in tiny baby-steps instead of one big-bang change.
Replace MockUtil
Let’s first focus on replacing the usage of the deprecated MockUtil
class. As you have seen, it is used only to mock a Destination
. Luckily, the same behavior can be achieved easily with productive SAP Cloud SDK APIs.
Mocking a Destination
With the MockUtil
class entirely removed from our tests, we can also get rid of the corresponding dependency.
Integration-Tests/pom.xml
Replace .execute
Replacing the .execute(HttpDestinationProperties)
API is almost effortless. In our demonstration, we just have to exchange the call with the stable .executeRequest(HttpDestinationProperties)
API.
fetchPartnersWithId
With .executeRequest
When switching to the new .executeRequest
API, you will notice your IDE complaining about the catch (final ODataException e)
clause. This is because the .executeRequest
method does not throw an com.sap.cloud.sdk.odatav2.connectivity.ODataException
.
In fact, the new API does not throw a checked exception at all. Therefore, we can remove the entire try ... catch ...
block, so that the method afterwards looks as below:
Final fetchPartnersWithId
Implementation
Replace RequestAccessor
The RequestAccessor
provides convenient access to the ServletRequest
sent by the user to our application. Unfortunately, the ServletRequest
is very limited when it comes to performing (long-running) asynchronous operations as part of the request processing. Therefore, we introduced a new accessor for getting the most prominent part of the ServletRequest
: The HTTP headers.
As a consequence, we can replace our usage of the RequestAccessor
with the new RequestHeaderAccessor
like so:
correspondenceLanguageEqualsAcceptLanguage
With RequestHeaderAccessor
2. Update the SAP Cloud SDK Version
Updating the SAP Cloud SDK version is very simple and, in fact, has already been done in this blog post. This time, however, we are updating to the new major version:
<dependencyManagement>>
<dependencies>
<dependency>
<groupId>com.sap.cloud.sdk</groupId>
<artifactId>sdk-bom</artifactId>
- <version>3.75.0</version>
+ <version>4.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
Let’s see whether everything is still working:
$ mvn clean verify
...
[ERROR] 'dependencies.dependency.version' for com.sap.hcp.cf.logging:cf-java-logging-support-logback:jar is missing.
...
[ERROR] /application/src/main/java/com/sap/example/controllers/BusinessPartnerController.java:[17,46] package com.sap.cloud.sdk.odatav2.connectivity does not exist
[ERROR] /application/src/main/java/com/sap/example/controllers/BusinessPartnerController.java:[54,24] cannot find symbol
[ERROR] symbol: class ODataException
As shown above, there are still a few errors, which require our attention. So let’s fix them one by one.
Declare Dependency Versions
As part of our efforts to clean up the SAP Cloud SDK, we decided to also slim down the sdk-bom
. This means that we have reduced the managed dependencies down to those that are actually used in our “core” modules. Therefore, if your application is relying on dependency versions that were previously managed by the sdk-bom
, you need to check whether that is still the case.
The compiler error shown in the previous chapter is an indicator that we stopped managing the version of some dependencies our demo application uses. Fixing this is usually rather straight forward.
Just locate the dependency and add the version
tag to it:
<dependency>
<groupId>com.sap.hcp.cf.logging</groupId>
<artifactId>cf-java-logging-support-logback</artifactId>
+ <version>3.6.3</version>
</dependency>
Tip: Finding Dependency Versions
Finding the latest version of a specific Maven dependency can be done by using the Maven Central Search.
All you have to do is to enter the module identifier, which consists of the group and artifact id. In the above example, searching for com.sap.hcp.cf.logging:cf-java-logging-support-logback
will reveal that the latest version is 3.6.3
.
Adjust Exceptions
In the previous chapters, we already migrated some exception that were related to the .execute(HttpDestinationProperties)
API. Apparently, this was not enough as indicated by the second compiler issue from above.
Luckily, fixing this issue is easy once again. All we need to do is to adjust the import
statement in our BusinessPartnerContoller
class:
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination;
import com.sap.cloud.sdk.cloudplatform.requestheader.RequestHeaderAccessor;
-import com.sap.cloud.sdk.odatav2.connectivity.ODataException;
+import com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException;
This exception might be thrown by the getBusinessPartnerAddressOrFetch()
method (used in the addresses
endpoint).
The get...OrFetch
APIs received an under-the-hood improvement to no longer throw the checked exception we were catching previously. Instead, now they might throw an com.sap.cloud.sdk.datamodel.odata.client.exception.ODataException
, which is a runtime exception. Therefore, we are free to apply the same change as for the .execute
replacement: We can remove the try ... catch ...
block and our application will still compile.
Summary
That’s it!
We successfully migrated our demo application from an outdated SAP Cloud SDK 3 version to the new major version by following these steps:
- Update to the latest available minor version for the SAP Cloud SDK 3.
- Replace usages of deprecated APIs.
- Update the SAP Cloud SDK to the new major version.
- Add
version
tags to dependencies that are no longer managed within thesdk-bom
. - Adjust import statements for the
ODataException
.
Share Your Feedback
Do you have any questions on the new features? Or are you struggling with the upgrade?
Don’t hesitate to share your feedback in the comments below, ask a question in the Q&A or to create a dedicated issue on our GitHub.