Update to Version 4 of the SAP Cloud SDK
Introduction
Version 4 of the SAP Cloud SDK is here and we highly recommend updating to it as soon as possible. It brings various improvements while keeping the necessary adjustments to an absolute minimum.
There is a good chance that just bumping the version to 4.0.0 is already enough, without the need for any further adjustments.
In particular, updating may be straightforward if you are not using any deprecated API or spawn new threads.
For more information also check out our version 4 release blog post.
Before You Update
Before you increase the SAP Cloud SDK version to 4 it can be beneficial to migrate any usage of deprecated API first. Follow the deprecation note of the Javadoc in case you are still using deprecated API from the SAP Cloud SDK. All deprecated API is removed With version 4 (exception being audit logging).
Full list of deprecated API
| API | Replacement | Comment |
| RequestAccessor | RequestHeaderAccessor | |
| RequestFacade | RequestHeaderFacade | |
| DefaultRequestFacade | DefaultRequestHeaderFacade | |
| RequestThreadContextListener | RequestHeaderThreadContextListener | |
| retrieveAccessTokenHeaderViaUserTokenExchange | retrieveAccessTokenHeaderViaUserTokenGrant | |
| retrieveAccessTokenViaUserTokenExchange | retrieveAccessTokenViaUserTokenGrant | |
| OAuth2ServiceSettings | - | Moved to security package |
| ScpCfHttpDestination.Builder.uri | ScpCfHttpDestination.builder(name, uri) | |
| Default(Erp)HttpDestination.Builder.uri | Default(Erp)HttpDestination.builder(uri) | |
| Default(Erp)HttpDestination.Builder.network | Default(Erp)HttpDestination.Builder.proxyType | |
| DestinationHeaderProvider.getHeaders | DestinationHeaderProvider.getHeaders | Argument type changed to DestinationRequestContext |
| ScpCfDestinationRetrievalStrategy.SUBSCRIBER_THEN_PROVIDER | ScpCfDestinationRetrievalStrategy.CURRENT_TENANT_THEN_PROVIDER | |
| ScpCfDestinationRetrievalStrategy.ALWAYS_SUBSCRIBER | ScpCfDestinationRetrievalStrategy.CURRENT_TENANT | Or use the new ONLY_SUBSCRIBER |
| ScpNeoDestinationRetrievalStrategy.SUBSCRIBER_THEN_PROVIDER | ScpNeoDestinationRetrievalStrategy.CURRENT_TENANT_THEN_PROVIDER | |
| ScpNeoDestinationRetrievalStrategy.ALWAYS_SUBSCRIBER | ScpNeoDestinationRetrievalStrategy.CURRENT_TENANT | Or use the new ONLY_SUBSCRIBER |
| CachingDecorator | GenericDecorator | |
| RetryConfiguration.ofDefaults | RetryConfiguration.of | |
| ResilienceDecorator.invalidateCache | ResilienceDecorator.clearAllCacheEntries | Or use the new clearCache |
| ResilienceDecorationStrategy.invalidateCache | ResilienceDecorationStrategy.clearAllCacheEntries | Or use the new clearCache |
| Resilience4jDecorationStrategy.invalidateCache | Resilience4jDecorationStrategy.clearAllCacheEntries | Or use the new clearCache |
| CloudLoggerFactory | LoggerFactory | |
| (OData V2) .execute | .executeRequest | |
| (OData V2) .withErrorHandler | - | Catch the dedicated exceptions |
| (OData V2) .getHeadersForRequestOnly | .getHeaders | There is no implicit metadata request anymore |
| (OData V2) .getHeadersForRequestAndImplicitRequests | .getHeaders | There is no implicit metadata request anymore |
| (OData V2) .onRequestOnly | - | There is no implicit metadata request anymore |
| (OData V2) .onRequestAndImplicitRequests | - | There is no implicit metadata request anymore |
| (OData V2) .cachingMetadata | - | There is no implicit metadata request anymore |
| (OData V2) .withoutCachingMetadata | - | There is no implicit metadata request anymore |
| (OData V2) .and | .withHeaders | |
| (OData V2) .toQuery | .toRequest | |
| (OData V2) .ignoringVersionIdentifier | .matchAnyVersionIdentifier | Or use the new disableVersionIdentifier |
| MockUtil | - | Removed without replacement |
| MockUtil.mockDestination | DestinationAccessor.appendDestinationLoader | Use new DefaultDestinationLoader().registerDestination(...) |
| MockUtil.mockTenant | TenantAccessor.executeWithTenant | |
| MockUtil.mockPrincipal | TenantAccessor.executeWithPrincipal | |
| All classes in com.sap.cloud.sdk.s4hana.connectivity.rfc.servlet.response | - | Removed without replacement |
| All classes in package com.sap.cloud.sdk.cloudplatform.servlet.exception | - | Removed without replacement |
| All classes in package com.sap.cloud.sdk.cloudplatform.servlet.response | - | Removed without replacement |
Note on the MockUtil class:
Most productive API allows for easy mocking already.
Please reach out to us directly if you use this class and are not sure how to replace it.
Update Your POM.XML
Begin by upgrading the version to 4.0.0.
Make sure to apply this for all the places where you declare the SAP Cloud SDK version.
Typically, this is the sdk-bom in the dependency management section.
In case you are using any of the code generators (OData, OpenAPI) also increase the version for those.
You can run mvn dependency:tree | grep 'com.sap.cloud.sdk' to verify that all entries are consistently on version 4.
Finally, version 4 discontinued some (legacy) modules and plugins. Most notably are:
- Usage analytics maven plugin
- Test utilities (contained the
MockUtilclass)
Remove them if they are present in your pom files.
Full list of removed modules
- On-premise VDM:
s4hana-api-odata-onpremise-2020s4hana-api-odata-v4-onpremise-2020
- DataModel - Enterprise Messaging:
messaging-jmsmessaging-generatormessaging-cores4hana-api-messaging
- Framework integrations (
com.sap.cloud.sdk.frameworks)cxfeclipselinkeclipselink-javaeejaxrs-gsonjaxrsjavaeeliquibaseliquibase-javaeetogglzspring-boot-multitenancy-scp-cfspring-web
- Testing utilities (
com.sap.cloud.sdk.testutil)testutil-parenttestutil-coretestutil-resources
- Currency Conversion (
com.sap.cloud.sdk.integration)integration-object-currency-conversion-simpleintegration-object-currency-conversion-simple-artifactintegration-object-currency-conversion-simple-srvintegration-parentcurrency-conversion-adapter-simple-integration-objects-mrmcurrency-conversion-adapter-simple-integration-objectscurrency-conversion-corecurrency-conversion-datamodelcurrency-conversion-parent
- Quality (
com.sap.cloud.sdk.quality)commonhttpclient-listenerlisteners-allodata-querylistenerpmd-pluginpmd-rulesrfc-querylistener
- Others:
com.sap.cloud.sdk.services:graphcom.sap.cloud.sdk.services:recast-aicom.sap.cloud.sdk.services:scp-machine-learningcom.sap.cloud.sdk.plugins:usage-analyticscom.sap.cloud.sdk.plugins:usage-analytics-maven-plugincom.sap.cloud.sdk.cloudplatform:meteringcom.sap.cloud.sdk.cloudplatform:metering-scp-neo
Managing Dependencies
With version 4 we also reduce the amount of dependencies the SAP Cloud SDK brings. This gives you more flexibility and requires less dependency management.
Because the sdk-bom now brings fewer dependencies, you may need to add explicit version declarations to dependencies that were managed by the sdk-bom.
If this is the case Maven will inform you about it with a message similar to 'dependencies.dependency.version' for ... is missing.
Full list of dependencies removed from the BOM
io.swagger:swagger-annotationsjavax.validation:validation-apijavax.ws.rs:javax.ws.rs-apijavax.jms:javax.jms-apijavax:javaee-apijavax:javaee-web-apijavax.ejb:javax.ejb-apiorg.hamcrest:hamcrest-coreorg.eclipse.persistence:eclipselinkorg.eclipse.persistence:javax.persistenceorg.liquibase:liquibase-coreorg.apache.commons:commons-csvorg.apache.cxf:cxf-coreorg.apache.cxf:cxf-rt-managementorg.apache.cxf:cxf-rt-transports-httporg.apache.cxf:cxf-rt-bindings-soaporg.apache.cxf:cxf-rt-bindings-xmlorg.apache.cxf:cxf-rt-ws-addrorg.apache.cxf:cxf-rt-ws-rmorg.apache.cxf:cxf-rt-ws-policyorg.apache.cxf:cxf-rt-ws-mexorg.apache.cxf:cxf-rt-ws-securityorg.apache.cxf:cxf-rt-frontend-jaxwsorg.apache.cxf:cxf-rt-frontend-jaxrsorg.apache.cxf:cxf-rt-rs-clientorg.apache.cxf:cxf-rt-rs-extension-providersorg.apache.cxf:cxf-rt-rs-extension-searchorg.apache.cxf:cxf-rt-rs-security-corsorg.apache.cxf:cxf-rt-rs-security-oauth2org.apache.cxf:cxf-rt-rs-security-xmlorg.apache.cxf:cxf-rt-rs-security-sso-samlorg.slf4j:jul-to-slf4jorg.slf4j:log4j-over-slf4jorg.slf4j:slf4j-simpleorg.springframework:spring-framework-bomorg.springframework:spring-security-bomorg.togglz:togglz-coreorg.togglz:togglz-servletorg.togglz:togglz-consoleorg.togglz:togglz-testingorg.togglz:togglz-junitcom.jayway.jsonpath:json-pathcom.fasterxml.jackson:jackson-bomcom.google.guava:guava-testlibcom.sap.cloud.servicesdk:odatav2-connectivity-sdk3com.sap.cloud.servicesdk:odata-v2-libcom.sap.cloud.servicesdk.prov:apicom.sap.cloud.servicesdk.prov:odatav4com.sap.cloud.servicesdk.prov:odata2.webcom.sap.cloud.servicesdk.prov:odata2.xsacom.sap.cloud.servicesdk.prov:odatav2-hybridcom.sap.cloud.servicesdk.prov:odatav2-provcom.sap.cloud:neo-javaee7-wp-apicom.sap.cloud:neo-java-web-apicom.sap.xs.java:xs-envcom.sap.xs2.security:security-commonscom.sap.xs2.security:java-container-securitycom.sap.conn.jco:com.sap.conn.jco.sapjco3com.sap.conn.jco:sapjco3com.sap.hcp.cf.logging:cf-java-logging-support-logbackcom.squareup.okhttp3:okhttp
Dependencies for the SAP Java Buildpack
If you are using the SAP Java Buildpack (SJB) together with a .war based deployment you should check out our new BOM dedicated to the SJB.
You can use the new BOM by replacing sdk-bom with sdk-sjb-bom in your dependency management section.
For more information and help in case of dependency problems refer to the dedicated guide on managing dependencies.
Run mvn validate to verify your updated project configuration.
Working with Threads and the ThreadContext
Up until now, working with threads was cumbersome.
Any code that runs a task in a new thread / asynchronously had to preserve the ThreadContext like so:
ThreadContextExecutor executor = new ThreadContextExecutor();
Callable operationWithContext = () -> executor.execute(() -> operation());
invokeAsynchronously(operationWithContext);
With version 4 you can simplify your code for running asynchronous tasks with the newly introduced ThreadContextExecutors:
Future runningTask = ThreadContextExecutors.submit(() -> operation());
The new API also integrates conveniently with CAP and can be integrated to work with Spring's @Async.
Refer to the full documentation for more details on these features.
Get Support
Having trouble with any of the update steps? Please don't hesitate to reach out to us!
You can find our public and internal support channels here.
Further Changes
At this point the update is probably already complete. There are more technically breaking API changes in version 4 that likely won't affect your project. Still, they are listed here for completeness and easy access.
OData
For the OData features most notable are:
- (OData V2) Change any reference of
UncheckedFilterExpressiontoExpressionFluentHelper - (OData V2) Change any exception handling for
com.sap.cloud.sdk.odatav2.connectivity.ODataException- Catch
com.sap.cloud.sdk.datamodel.odata.client.exception.*instead - This should only apply for any usages of
get...OrFetch()methods on entities
- Catch
Full list of OData related changes
- Remove
com.sap.cloud.sdk.s4hana.datamodel.odata.adapter.ODataCalendarAdapterand child classesODataDateTimeAdapter,ODataDateTimeOffsetAdapter, andODataTimeAdapter. - Remove the obsolete helper classes
FilterExpressionWrapper,UncheckedFilterExpression,FilterExpressionHelperandFilterFunction - The
ODataTypeValueSerializerclass has been removed as it is no longer needed with the removal of theservicesdkdependency - Remove the obsolete class
com.sap.cloud.sdk.s4hana.connectivity.RequestBody - For OData v2 generated classes (the OData v2 VDM), the method signature has changed for lazily resolving navigation properties in entity classes.
A checked exception is no longer thrown for methods named fetchX() and getXOrFetch():
If you still want to evaluate the error details (like HTTP payload or status code), please feel free to explore the hierarchy and properties of our OData unchecked exception types.
Entity entity;
try {
entity.fetchProperty();
entity.getPropertyOrFetch()
}
catch( com.sap.cloud.sdk.odatav2.connectivity.ODataException e ) {
} - Removed the now obsolete method
VdmEntityUtil#fromFields - The modules
odata-clientandodata-v4-corehave been moved out of beta state and are now considered stable - The decprecated modules
s4hana-api-odata-onpremise-2020ands4hana-api-odata-v4-onpremise-2020are removed
Kubernetes
In case you are running on Kubernetes like Kyma or Gardener you can benefit from an improved integration with version 4.1.0 of the SAP Cloud SDK.
- Make sure your dependency tree contains the latest version of the SAP BTP Environment Variable Access (aka Service Binding) Library (minimum
0.5.1) - Make sure to use the latest version of the SAP BTP Service Operator
- Make sure your secrets are mounted under
/etc/secrets/sapbtp/ - The
secretRootKeyparameter in bindings is no longer necessary - Manually adding the service plan for XSUAA is no longer necessary
CloudPlatform
On the CloudPlatform and related accessor classes the most notable changes are:
- The deprecated
RequestAccessorhas been replaced by theRequestHeaderAccessor - The
RequestHeaderContainerreceived minor changes to the collection types used - The latest version of the SAP BTP Service Operator is now supported
Full list of CloudPlatform related changes
- Moved the
AuthTokenAccessor,AuthTokenclasses and theAuthTokenFacadeinterface from thesecurity-scp-cfinto thesecuritymodule.- Renamed
DefaultAuthTokenFacadetoScpCfAuthTokenFacade. - Only functional implementation of
AuthTokenFacadeis theScpCfAuthTokenFacadein thesecurity-scp-cfmodule. - If no implementation of the facade interface can be found via the ServiceLoader pattern, all invocations on the
AuthTokenAccessorwill throw an Exception. - SCP CF related methods on the
AuthTokenAccessorare removed from there and only accessible via theScpCfAuthTokenFacade.
- Renamed
- Public API in
RequestHeaderContainerhas the following breaking changes:RequestHeaderContainer#getHeaderNames()now returns aList<String>instead ofSet<String>. Only headers which have at least one non-null value are returned by the method.RequestHeaderContainer#getHeaderValues()now returns aList<String>instead ofCollection<String>DefaultRequestHeaderContainer#fromMultiValueMapalso allows generic inputs to be passed. The signature of the method has changed fromDefaultRequestHeaderContainer#fromMultiValueMap( Map<String, Collection<String>> headers )toDefaultRequestHeaderContainer#fromMultiValueMap( Map<String, ? extends Iterable<String>> headers )
- The
Accessor(e.g.TenantAccessor) classes will now throw anThreadContextExecutionExceptionin theirexecuteWithmethods in case a customFacadeimplementation is used that is not a (subtype of)DefaultFacade. This was changed to make a current shortcoming regarding theexecuteWithAPI apparent to users that are using a differentThreadContext. For example, when running in a CAP context,TenantAccessor.executeWithTenantdoesn't correctly update the CDS Context so that the operation doesn't lead to the expected result. Before, this error would go unnoticed, which led to the wrong impression that everything was working as expected. Now, the aforementionedThreadContextExecutionExceptionwill be thrown to make users aware of the issue. - Introduce support for the BTP Environment Variable Access (Service Binding) library.
- The
ScpCfCloudPlatformnow offers asetServiceBindingAccessormethod, which should be used instead of thesetEnvironmentVariableReadermethod to override the access to theVCAP_SERVICESenvironment variable.
- The
- Following public behavior in the
cloudplatform-core-scp-cfmodule has been changed:ScpCfCloudPlatform#invalidateCaches: The service bindings are no longer statically cached. Therefore, using theinvalidateCachesmethod will no longer affect service bindings (for example as returned by thegetVcapServicesmethod)ScpCfCloudPlatform#getVcapServices: This method will no longer thrown aCloudPlatformExceptionin case no service bindings are found
- Following experimental classes in the
cloudplatform-coremodule have been removed:- All classes in the
com.sap.cloud.sdk.cloudplatform.servicebindingpackage
- All classes in the
- experimental methods in the
cloudplatform-core-scp-cfmodule have been removed:ScpCfCloudPlatform#setServiceBindingsRootLocationScpCfCloudPlatform#getServiceBindingsRootLocationScpCfCloudPlatform#setServiceBindingLoader
ThreadContext
The ThreadContext interface and all related API have received substantial changes.
In most cases the above mentioned change should suffice.
If this is not the case for your project and you find yourself affected by any of the below changes please reach out to us directly.
Full list of ThreadContext related changes
- The
ThreadContextinterface received the following changes:- Method
Property<T> getProperty(String name)has been replaced withTry<T> getPropertyValue(String name) - Method
setPropertyIfAbsenthas been changed to now accept aCallable<Property<?>>instead ofCallable<?> - Enable
equalsandhashCodeforDefaultThreadContext. - Method
getThread()has been removed
- Method
- The
Property<T>class has been expanded with convenience methods to decorate a callable:Callable<Property<?>> decorateCallable( Callable<?> valueGenerator )Callable<Property<?>> decorateConfidentialCallable( Callable<?> valueGenerator )
ThreadContextExecutorchanges:ThreadContextinstances are no longer implicitly shared when usingThreadContextExecutor. Instead:fromCurrentContext()will duplicate the current context into a new context objectfromNewContext()will use a new, empty context objectfromCurrentOrNewContext()behaves likefromCurrentContextbut will fall back to a new context, if none is availableusing(context)will duplicate the provided context and its properties into a new context object- Duplicating the current context solves persistence and concurrency issues with dynamic thread life cycles.
- Additionally, the
ThreadContextis duplicated every time theThreadContextExecutor#executemethod is called. - Therefore, the steps
beforeDestroy()andafterDestroy()are removed from theThreadContextListener.
ThreadContextListenerhas no longer access to values from a potential parent thread-context. Instead, any values from a potential parent context are already present and can be altered.- The
ThreadContextDecoratorhas been changed to now enable passing arbitraryThreadLocalrelated data to new threads created viaDefaultThreadContextExecutorService- The existing
SecurityThreadContextDecoratorhas been improved to pass on the full security library'sSecurityContext - The related
cds-integration-cloudsdkmodule has been improved to pass on the full CDSRequestContext - The now obsolete
SecurityContextThreadContextListenerhas been removed. - The
ScpNeoThreadContextDecoratorhas been renamed toScpNeoThreadContextDecoratorInternaland moved tocom.sap.cloud.sdk.cloudplatform.thread. Additionally, it is no longer implementing theThreadContextDecoratorinterface.
- The existing
- Introduce a new API for running asynchronous tasks:
ThreadContextExecutorServiceis an extension ofExecutorServiceand can be used to submitCallableandRunnabletasks.- It will ensure the current (or a custom)
ThreadContextis passed on to the newly scheduledThread - Since it is an
ExecutorServiceit can be passed to other frameworks and libraries that create new Threads, e.g. Springs@Async ThreadContextExecutorsgives static access to a single (default) instance ofThreadContextExecutorService- The
DefaultThreadContextExecutorServiceis also customizable, e.g. to use a differentThreadPool - Update the
AbstractRequestEnsurerto use aThreadContextExecutorServiceby default to propagate theThreadContextto its tasks. - Update the
ResilienceDecorationStrategyto use aThreadContextExecutorServiceby default to propagate theThreadContextto its tasks.
Others
The most notable remaining changes are:
- The OpenAPI generator has been updated to version
6.0.1 - Minor class renaming in the modules
scp-workflow-cfandbtp-business-rules - Marking formerly experimental API as stable, most prominently the OData V4 API
Full list of other changes
- Update OpenAPI generator to version 6.0.1, bringing several improvements:
- The generator now conforms to a list of reserved keywords to prevent naming clashes
- The generator now generates dedicated classes for some nested list types that were formerly only represented via
List<Object>
- Enable customization of SAP Passport properties, e.g.
SapPassportPostProcessor customProcessor = builder -> builder.withStaticPreviousSystemId("FooBar");SapPassportAccessor.setPassportFacade(new SapPassportFacade(new SapPassportFactory(customProcessor));
- The
RequestScopedHttpClientCachehas been removed in favor of theDefaultHttpClientCache(formerly known asTimeScopedHttpClientCache- see next note). This change renders theHttpClientsThreadContextListeneruseless, which is why this class has also been removed. - The
TimeScopedHttpClientCachehas been renamed toDefaultHttpClientCache. It is replacing the removedRequestScopedHttpClientCacheas the default implementation ofHttpClientCachein theHttpClientAccessor. Furthermore, the isolation strategy has been changed: Http clients are now also cached in case either the current tenant or principal (or both) are missing. - Minor re-namings of generated classes in
btp-business-rulesDecisionTableCelltoDecisionTableCellInnerDecisionTableColumntoDecisionTableColumnInnerDecisionTableConditiontoDecisionTableColumnInnerConditionDecisionTableResulttoDecisionTableColumnInnerResultDecisionTableRowtoDecisionTableRowInnerElementValuesEnumerationtoElementValuesEnumerationInnerErrorErrorDetailstoErrorErrorDetailsInnerStructureComponenttoStructureComponentInnerStructureAssociationtoStructureComponentInnerAssociationStructureAssociationConditiontoStructureComponentInnerAssociationConditionInnerStructureAssociationSourcetoStructureComponentInnerAssociationConditionInnerSourceStructureAssociationTargettoStructureComponentInnerAssociationConditionInnerTargetTableParametertoTableParameterInnerTextBranchestoTextBranchesInnerTextConditiontoTextBranchesInnerConditionTextOperationtoTextBranchesInnerOperationInnerTextPredefinedtoTextPredefinedInnerStructureAssociationTargettoVocabularyInputInner
- Minor re-namings of generated classes in
scp-workflow-cfErrorMessageErrorDetailstoErrorMessageErrorDetailsInnerAttachmentsContextGroupstoAttachmentsContextGroupsValueAttachmentsContextRefstoAttachmentsContextGroupsValueRefsInnerFormMetadataWorkflowDefinitionstoFormMetadataWorkflowDefinitionsInnerTechnicalErrorErrorDetailstoTechnicalErrorErrorDetailsInner
- Stabilize the following, formerly marked experimental, API:
- In package
com.sap.cloud.sdk.cloudplatform.resiliencestabilise:CacheFilterclassResilienceDecorationStrategy.clearCache()ResilienceDecorator.clearCache()
- In package
com.sap.cloud.sdk.frameworks.resilience4jstabilise:Resilience4jDecorationStrategy.clearCache()Resilience4jDecorationStrategy.Resilience4jDecorationStrategyBuilderstatic class
- In package
com.sap.cloud.sdk.cloudplatform.connectivitystabilise:- Constructor
ScpCfDestinationLoader( Duration timeLimiterTimeout ) ScpCfDestinationTokenExchangeStrategyclass and all related APIScpCfServiceDestinationLoaderclass and all related APISecurityConfigurationStrategyclass and all related APIappendDestinationLoaderandprependDestinationLoaderonDestinationAccessorWrappedDestinationclass
- Constructor
- In module
securityAudienceClientCertificateDefaultPrincipalgetAuthorizationsByAudience()inPrincipal
- In module
security-scp-cfOAuth2ServiceProviderOAuth2ServiceSettingsOAuth2TokenServiceCacheSecurityContextThreadContextDecorator
- In module
odata-v4-coreBatchResponseandof()inBatchResponseBoundActionBoundFunctionBoundOperationtoRequest()inRequestBuilderSimplePropertyVdmComplexVdmEntityVdmEntitySetVdmEntityUtilVdmEnumVdmObject
- In module
rfcRemoteFunctionCacheSoapNamespace
- In package