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
MockUtil
class)
Remove them if they are present in your pom files.
Full list of removed modules
- On-premise VDM:
s4hana-api-odata-onpremise-2020
s4hana-api-odata-v4-onpremise-2020
- DataModel - Enterprise Messaging:
messaging-jms
messaging-generator
messaging-core
s4hana-api-messaging
- Framework integrations (
com.sap.cloud.sdk.frameworks
)cxf
eclipselink
eclipselink-javaee
jaxrs-gson
jaxrs
javaee
liquibase
liquibase-javaee
togglz
spring-boot-multitenancy-scp-cf
spring-web
- Testing utilities (
com.sap.cloud.sdk.testutil
)testutil-parent
testutil-core
testutil-resources
- Currency Conversion (
com.sap.cloud.sdk.integration
)integration-object-currency-conversion-simple
integration-object-currency-conversion-simple-artifact
integration-object-currency-conversion-simple-srv
integration-parent
currency-conversion-adapter-simple-integration-objects-mrm
currency-conversion-adapter-simple-integration-objects
currency-conversion-core
currency-conversion-datamodel
currency-conversion-parent
- Quality (
com.sap.cloud.sdk.quality
)common
httpclient-listener
listeners-all
odata-querylistener
pmd-plugin
pmd-rules
rfc-querylistener
- Others:
com.sap.cloud.sdk.services:graph
com.sap.cloud.sdk.services:recast-ai
com.sap.cloud.sdk.services:scp-machine-learning
com.sap.cloud.sdk.plugins:usage-analytics
com.sap.cloud.sdk.plugins:usage-analytics-maven-plugin
com.sap.cloud.sdk.cloudplatform:metering
com.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-annotations
javax.validation:validation-api
javax.ws.rs:javax.ws.rs-api
javax.jms:javax.jms-api
javax:javaee-api
javax:javaee-web-api
javax.ejb:javax.ejb-api
org.hamcrest:hamcrest-core
org.eclipse.persistence:eclipselink
org.eclipse.persistence:javax.persistence
org.liquibase:liquibase-core
org.apache.commons:commons-csv
org.apache.cxf:cxf-core
org.apache.cxf:cxf-rt-management
org.apache.cxf:cxf-rt-transports-http
org.apache.cxf:cxf-rt-bindings-soap
org.apache.cxf:cxf-rt-bindings-xml
org.apache.cxf:cxf-rt-ws-addr
org.apache.cxf:cxf-rt-ws-rm
org.apache.cxf:cxf-rt-ws-policy
org.apache.cxf:cxf-rt-ws-mex
org.apache.cxf:cxf-rt-ws-security
org.apache.cxf:cxf-rt-frontend-jaxws
org.apache.cxf:cxf-rt-frontend-jaxrs
org.apache.cxf:cxf-rt-rs-client
org.apache.cxf:cxf-rt-rs-extension-providers
org.apache.cxf:cxf-rt-rs-extension-search
org.apache.cxf:cxf-rt-rs-security-cors
org.apache.cxf:cxf-rt-rs-security-oauth2
org.apache.cxf:cxf-rt-rs-security-xml
org.apache.cxf:cxf-rt-rs-security-sso-saml
org.slf4j:jul-to-slf4j
org.slf4j:log4j-over-slf4j
org.slf4j:slf4j-simple
org.springframework:spring-framework-bom
org.springframework:spring-security-bom
org.togglz:togglz-core
org.togglz:togglz-servlet
org.togglz:togglz-console
org.togglz:togglz-testing
org.togglz:togglz-junit
com.jayway.jsonpath:json-path
com.fasterxml.jackson:jackson-bom
com.google.guava:guava-testlib
com.sap.cloud.servicesdk:odatav2-connectivity-sdk3
com.sap.cloud.servicesdk:odata-v2-lib
com.sap.cloud.servicesdk.prov:api
com.sap.cloud.servicesdk.prov:odatav4
com.sap.cloud.servicesdk.prov:odata2.web
com.sap.cloud.servicesdk.prov:odata2.xsa
com.sap.cloud.servicesdk.prov:odatav2-hybrid
com.sap.cloud.servicesdk.prov:odatav2-prov
com.sap.cloud:neo-javaee7-wp-api
com.sap.cloud:neo-java-web-api
com.sap.xs.java:xs-env
com.sap.xs2.security:security-commons
com.sap.xs2.security:java-container-security
com.sap.conn.jco:com.sap.conn.jco.sapjco3
com.sap.conn.jco:sapjco3
com.sap.hcp.cf.logging:cf-java-logging-support-logback
com.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
UncheckedFilterExpression
toExpressionFluentHelper
- (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.ODataCalendarAdapter
and child classesODataDateTimeAdapter
,ODataDateTimeOffsetAdapter
, andODataTimeAdapter
. - Remove the obsolete helper classes
FilterExpressionWrapper
,UncheckedFilterExpression
,FilterExpressionHelper
andFilterFunction
- The
ODataTypeValueSerializer
class has been removed as it is no longer needed with the removal of theservicesdk
dependency - 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-client
andodata-v4-core
have been moved out of beta state and are now considered stable - The decprecated modules
s4hana-api-odata-onpremise-2020
ands4hana-api-odata-v4-onpremise-2020
are 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
secretRootKey
parameter 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
RequestAccessor
has been replaced by theRequestHeaderAccessor
- The
RequestHeaderContainer
received 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
,AuthToken
classes and theAuthTokenFacade
interface from thesecurity-scp-cf
into thesecurity
module.- Renamed
DefaultAuthTokenFacade
toScpCfAuthTokenFacade
. - Only functional implementation of
AuthTokenFacade
is theScpCfAuthTokenFacade
in thesecurity-scp-cf
module. - If no implementation of the facade interface can be found via the ServiceLoader pattern, all invocations on the
AuthTokenAccessor
will throw an Exception. - SCP CF related methods on the
AuthTokenAccessor
are removed from there and only accessible via theScpCfAuthTokenFacade
.
- Renamed
- Public API in
RequestHeaderContainer
has 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#fromMultiValueMap
also 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 anThreadContextExecutionException
in theirexecuteWith
methods in case a customFacade
implementation is used that is not a (subtype of)DefaultFacade
. This was changed to make a current shortcoming regarding theexecuteWith
API apparent to users that are using a differentThreadContext
. For example, when running in a CAP context,TenantAccessor.executeWithTenant
doesn'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 aforementionedThreadContextExecutionException
will be thrown to make users aware of the issue. - Introduce support for the BTP Environment Variable Access (Service Binding) library.
- The
ScpCfCloudPlatform
now offers asetServiceBindingAccessor
method, which should be used instead of thesetEnvironmentVariableReader
method to override the access to theVCAP_SERVICES
environment variable.
- The
- Following public behavior in the
cloudplatform-core-scp-cf
module has been changed:ScpCfCloudPlatform#invalidateCaches
: The service bindings are no longer statically cached. Therefore, using theinvalidateCaches
method will no longer affect service bindings (for example as returned by thegetVcapServices
method)ScpCfCloudPlatform#getVcapServices
: This method will no longer thrown aCloudPlatformException
in case no service bindings are found
- Following experimental classes in the
cloudplatform-core
module have been removed:- All classes in the
com.sap.cloud.sdk.cloudplatform.servicebinding
package
- All classes in the
- experimental methods in the
cloudplatform-core-scp-cf
module have been removed:ScpCfCloudPlatform#setServiceBindingsRootLocation
ScpCfCloudPlatform#getServiceBindingsRootLocation
ScpCfCloudPlatform#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
ThreadContext
interface received the following changes:- Method
Property<T> getProperty(String name)
has been replaced withTry<T> getPropertyValue(String name)
- Method
setPropertyIfAbsent
has been changed to now accept aCallable<Property<?>>
instead ofCallable<?>
- Enable
equals
andhashCode
forDefaultThreadContext
. - 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 )
ThreadContextExecutor
changes:ThreadContext
instances 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 likefromCurrentContext
but 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
ThreadContext
is duplicated every time theThreadContextExecutor#execute
method is called. - Therefore, the steps
beforeDestroy()
andafterDestroy()
are removed from theThreadContextListener
.
ThreadContextListener
has 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
ThreadContextDecorator
has been changed to now enable passing arbitraryThreadLocal
related data to new threads created viaDefaultThreadContextExecutorService
- The existing
SecurityThreadContextDecorator
has been improved to pass on the full security library'sSecurityContext
- The related
cds-integration-cloudsdk
module has been improved to pass on the full CDSRequestContext
- The now obsolete
SecurityContextThreadContextListener
has been removed. - The
ScpNeoThreadContextDecorator
has been renamed toScpNeoThreadContextDecoratorInternal
and moved tocom.sap.cloud.sdk.cloudplatform.thread
. Additionally, it is no longer implementing theThreadContextDecorator
interface.
- The existing
- Introduce a new API for running asynchronous tasks:
ThreadContextExecutorService
is an extension ofExecutorService
and can be used to submitCallable
andRunnable
tasks.- It will ensure the current (or a custom)
ThreadContext
is passed on to the newly scheduledThread
- Since it is an
ExecutorService
it can be passed to other frameworks and libraries that create new Threads, e.g. Springs@Async
ThreadContextExecutors
gives static access to a single (default) instance ofThreadContextExecutorService
- The
DefaultThreadContextExecutorService
is also customizable, e.g. to use a differentThreadPool
- Update the
AbstractRequestEnsurer
to use aThreadContextExecutorService
by default to propagate theThreadContext
to its tasks. - Update the
ResilienceDecorationStrategy
to use aThreadContextExecutorService
by default to propagate theThreadContext
to 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-cf
andbtp-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
RequestScopedHttpClientCache
has been removed in favor of theDefaultHttpClientCache
(formerly known asTimeScopedHttpClientCache
- see next note). This change renders theHttpClientsThreadContextListener
useless, which is why this class has also been removed. - The
TimeScopedHttpClientCache
has been renamed toDefaultHttpClientCache
. It is replacing the removedRequestScopedHttpClientCache
as the default implementation ofHttpClientCache
in 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-rules
DecisionTableCell
toDecisionTableCellInner
DecisionTableColumn
toDecisionTableColumnInner
DecisionTableCondition
toDecisionTableColumnInnerCondition
DecisionTableResult
toDecisionTableColumnInnerResult
DecisionTableRow
toDecisionTableRowInner
ElementValuesEnumeration
toElementValuesEnumerationInner
ErrorErrorDetails
toErrorErrorDetailsInner
StructureComponent
toStructureComponentInner
StructureAssociation
toStructureComponentInnerAssociation
StructureAssociationCondition
toStructureComponentInnerAssociationConditionInner
StructureAssociationSource
toStructureComponentInnerAssociationConditionInnerSource
StructureAssociationTarget
toStructureComponentInnerAssociationConditionInnerTarget
TableParameter
toTableParameterInner
TextBranches
toTextBranchesInner
TextCondition
toTextBranchesInnerCondition
TextOperation
toTextBranchesInnerOperationInner
TextPredefined
toTextPredefinedInner
StructureAssociationTarget
toVocabularyInputInner
- Minor re-namings of generated classes in
scp-workflow-cf
ErrorMessageErrorDetails
toErrorMessageErrorDetailsInner
AttachmentsContextGroups
toAttachmentsContextGroupsValue
AttachmentsContextRefs
toAttachmentsContextGroupsValueRefsInner
FormMetadataWorkflowDefinitions
toFormMetadataWorkflowDefinitionsInner
TechnicalErrorErrorDetails
toTechnicalErrorErrorDetailsInner
- Stabilize the following, formerly marked experimental, API:
- In package
com.sap.cloud.sdk.cloudplatform.resilience
stabilise:CacheFilter
classResilienceDecorationStrategy.clearCache()
ResilienceDecorator.clearCache()
- In package
com.sap.cloud.sdk.frameworks.resilience4j
stabilise:Resilience4jDecorationStrategy.clearCache()
Resilience4jDecorationStrategy.Resilience4jDecorationStrategyBuilder
static class
- In package
com.sap.cloud.sdk.cloudplatform.connectivity
stabilise:- Constructor
ScpCfDestinationLoader( Duration timeLimiterTimeout )
ScpCfDestinationTokenExchangeStrategy
class and all related APIScpCfServiceDestinationLoader
class and all related APISecurityConfigurationStrategy
class and all related APIappendDestinationLoader
andprependDestinationLoader
onDestinationAccessor
WrappedDestination
class
- Constructor
- In module
security
Audience
ClientCertificate
DefaultPrincipal
getAuthorizationsByAudience()
inPrincipal
- In module
security-scp-cf
OAuth2ServiceProvider
OAuth2ServiceSettings
OAuth2TokenServiceCache
SecurityContextThreadContextDecorator
- In module
odata-v4-core
BatchResponse
andof()
inBatchResponse
BoundAction
BoundFunction
BoundOperation
toRequest()
inRequestBuilder
SimpleProperty
VdmComplex
VdmEntity
VdmEntitySet
VdmEntityUtil
VdmEnum
VdmObject
- In module
rfc
RemoteFunctionCache
SoapNamespace
- In package