Reputation: 677
I am interacting with the SAP Business One Service Layer using code generated via the SAP Cloud SDK.
One thing that must be accounted for with the Service Layer is the potential for deadlocks in the database. The general recommendation is to retry the operation if a deadlock is detected.
I am wondering if there is a way to elegantly wrap all calls made by the SAP Cloud SDK with a resilience decorator to retry in the case of a deadlock.
The current logic for checking for a deadlock is to check the ODataCode:
public boolean checkIfDeadlockError(Throwable serviceLayerThrowable){
if (serviceLayerThrowable instanceof ODataServiceErrorException serviceErrorException) {
return serviceErrorException.getOdataError().getODataCode().equals("-2038");
}
return false;
}
Right now I am having to ensure I wrap every single call with the same resilience decorator, which is tedious.
Upvotes: 0
Views: 85
Reputation: 563
The SAP Cloud SDK allows you to specify a
retryOnExceptionPredicate(Predicate<Throwable> retryOnExceptionPredicate)
with the RetryConfiguration. With that you can specify in which cases an exception should be retried.
For example, you could do an instanceof
check like so: var predicate = e -> e instanceof IllegalArgumentException
.
Alternatively, you could catch the relevant exception within your code and return some other representation of the result.
Upvotes: 1
Reputation: 2756
Adding as answer to clarify comment...
So given some class SapBusinessOneSdk
, which is a specific implementation, wrap it on your own code. For example:
import sap.business.one.SapBusinessOneSdk; // only class that knows about the specific implementation
public class SapService {
private final SapBusinessOneClient client; // assuming this is thread-safe
// assuming some dependency injection (DI) to configure
public SapService(SapConfig config) {
SapBusinessOneSdk.clientBuilder()
.connectionString(config.getConnectionString())
.build();
}
public MyResponse doSomething(MyRequest request) {
try {
// map app domain request to SDK request
var sdkRequest = SapBusinessOneSdk.requestBuilder()
.id(request.getId())
...
.build();
// add retry here
// old school: https://stackoverflow.com/questions/13239972/how-do-you-implement-a-re-try-catch
var response = client.thisIsSoSappy(sdkRequest);
// map response to app specific response
return MyResponse.builder().
.message(response.getMessage())
.build();
} catch(Exception e) {
// avoid rest of app being aware of the specific SDK exception classes
throw new AppSpecificException(..., e);
}
}
}
Then use SapService
anywhere that's needed in the other service layer classes. For example:
public class SomeAppService {
private final SapService sap;
// assuming DI to inject the service
public SomeAppService(SapService sap) {
this.sap = sap;
}
public void doSomethingThatInvolvesSap(BusinessContext context) {
// map context to a request
var request = MyRequest.builder()...build();
// call SAP, via the wrapper
var response = sap.doSomething(request);
// do something with the response...
}
}
Note that if the SDK changes (or even a complete replacement to another SAP provider), the only class that has to change is SapService
.
Upvotes: 0