Disclaimer: This blog post is only applicable for the SAP Cloud SDK version of at most 2.19.2. We plan to continuously migrate these blog posts into our List of Tutorials. Feel free to check out our updated Tutorials on the SAP Cloud SDK.
FluentIterable, Optional, Function, Predicate and Supplier with corresponding native Java 8 types. This usually means that you have to adjust the existing Guava imports and replace them with their Java 8 counterparts. For instance, differences between Guava's and Java's Optional are described in the Guava Javadoc. An example for the required changes is depicted below.import com.google.common.base.Optional;
...
ProxyConfiguration proxyConfiguration =
DestinationAccessor.getDestination("dest")
.getProxyConfiguration()
.orNull();import java.util.Optional;
...
ProxyConfiguration proxyConfiguration =
DestinationAccessor.getDestination("dest")
.getProxyConfiguration()
.orElse(null);
Executable no longer inherits from Callable and is now annotated with @FunctionalInterface. RequestContextExecutor has been adjusted accordingly to accept both Callable and Executable as Lambda expressions. This enables a simpler use of this class:new RequestContextExecutor().execute(new Executable() {
@Override
public void execute() throws Exception {
...
}
);new RequestContextExecutor().execute(() -> {
...
});java.time classes of Java 8. In particular, in the Java virtual data model (VDM), the deprecated Calendar type has been replaced with LocalDateTime, LocalTime, and ZonedDateTime. When adapting your code, you will be able to see the new expected type on the respective entity fields.HystrixUtil command key methods have been removed. The CachingCommand now uses CacheKey.ofTenantAndUserIsolation() by default. While the best-effort isolation allowed to generically construct a cache key with the highest possible level of isolation, it beared the potential risk of an unexpected cache isolation, for example due to a security configuration mistake. Therefore, we decided to require an explicit specification of the expected isolation instead. If the requested isolation level cannot be provided, an exception is thrown instead of silently reverting to a weaker isolation.// weaker than expected isolation possible at runtime
CacheKey cacheKey = CacheKey.newBestEffortIsolatedKey(); // explicitly specify and enforce isolation at runtime
CacheKey cacheKey = CacheKey.ofTenantAndUserIsolation();ALLOW_MOCKED_AUTH_HEADERenvironment variable to true to mock a tenant and still use the destination service (without principal propagation).MockUtil.mockDefaults(). Since this represented a deviation of behavior between tests, a local container, and code that runs on SAP Cloud Platform, the audit log is no longer mocked by this method. In contrast, if you need to mock an audit log, you now have to explicitly call MockUtil.mockAuditLog().private static final MockUtil mockUtil = new MockUtil();
@Test
public void test()
{
mockUtil.mockDefaults(); // implicitly mocked an audit log
}private static final MockUtil mockUtil = new MockUtil();
@Test
public void test()
{
mockUtil.mockDefaults(); // no longer mocks an audit log
mockUtil.mockAuditLog(); // mock the audit log explicitly now
}Optionals in TenantAccessor, UserAccessor, and SecretStoreAccessor. For example, in order to retrieve the current tenant, previous versions only offered a method getCurrentTenant() that could throw a TenantNotAvailableException. Since the flow of an application can depend on the availability of a tenant, the previous APIs required developers to rely on the anti-pattern of using exceptions for controlling the code flow. Therefore, the SDK now offers new methods such as getCurrentTenantIfAvailable() which avoid the use of an exception here.Tenant getCurrentTenant()
throws TenantNotAvailableException,
TenantAccessException;Optional<Tenant> getCurrentTenantIfAvailable()
throws TenantAccessException;
Tenant getCurrentTenant()
throws TenantNotAvailableException,
TenantAccessException;ErpEndpoint as well ErpConfigContext. Since both classes eventually turned out to be very similar, the purpose and use of both classes became confusing for developers. In particular, since an ErpEndpoint always had to be constructed from a given ErpConfigContext, developers often had to wrap an ErpConfigContext into an ErpEndpoint without an obvious reason. Therefore, we decided to only offer ErpConfigContext to represent the context for transparently connecting to SAP S/4HANA systems via different protocols.new DefaultBusinessPartnerService()
.getAllBusinessPartner()
.execute(new ErpEndpoint(new ErpConfigContext()));new DefaultBusinessPartnerService()
.getAllBusinessPartner()
.execute(new ErpConfigContext());RequestContextListeners were initialized using ServletContextListeners. The downside of this was that RequestContextListeners would not be initialized when being used within certain test setups or when using another ServletContextListener. Therefore, the SDK-internal RequestContextListener instances are now initialized using the ServiceLoader mechanism, allowing developers to more easily use SDK components within tests and during application startup.@WebListener
public class MyServletContextListener implements ServletContextListener
{
@Override
public void contextInitialized( ServletContextEvent servletContextEvent )
{
// explicitly register RequestContextListeners
new RequestContextExecutor().withListeners(
new DestinationsRequestContextListener(),
new ScpNeoDestinationsRequestContextListener(),
new TenantRequestContextListener(),
new UserRequestContextListener()
).execute(...);
}
...
}@WebListener
public class MyServletContextListener implements ServletContextListener
{
@Override
public void contextInitialized( ServletContextEvent servletContextEvent )
{
new RequestContextExecutor().execute(...);
}
...
}requestContextExecutor() has been removed from the class MockUtil. It is now possible to simply use new RequestContextExecutor() instead.private static final MockUtil mockUtil = new MockUtil();
@Test
public void test()
{
mockUtil.requestContextExecutor().execute(...);
}@Test
public void test()
{
new RequestContextExecutor().execute(...);
}RequestContext now uses a custom class Property to represent a property with a certain value or exception if the value could not be determined.Optional<Object> getProperty( String name )
throws RequestContextPropertyException;Optional<Property<?>> getProperty( String name )
throws RequestContextPropertyException;ServletContextListener to register a Neo-specific concurrency strategy for Hystrix in ScpNeoHystrixBootstrapListener. This is now obsolete since the SDK internally uses the standard Java ServiceLoader mechanism offered by Hystrix to register this strategy.@WebListener
public class MyServletContextListener implements ServletContextListener
{
@Override
public void contextInitialized( ServletContextEvent servletContextEvent )
{
// ensure correct use of HystrixConcurrencyStrategy on Neo
new ScpNeoHystrixBootstrapListener().bootstrap();
...
}
...
}@WebListener
public class MyServletContextListener implements ServletContextListener
{
@Override
public void contextInitialized( ServletContextEvent servletContextEvent )
{
// nothing special required here anymore
...
}
...
}CacheKey with unified of constructor methods.CacheKey.newGlobalKey();
CacheKey.newTenantIsolatedKey();
CacheKey.newUserIsolatedKey();
CacheKey.newUserOrTenantIsolatedKey();
CacheKey.newTenantIsolatedOrGlobalKey();
CacheKey.newBestEffortIsolatedKey();
// and many moreCacheKey.ofNoIsolation();
CacheKey.ofTenantIsolation();
CacheKey.ofTenantAndUserIsolation();
CacheKey.of(nullableTenantId, nullableUserName);ConvertedObject with of constructor methods and fixing the misspelled term "convertable", replacing it with the corrected term "convertible".ConvertedObject.fromConverted(nullableValue);
ConvertedObject.fromNull();
ConvertedObject.notConvertable();ConvertedObject.of(nullableValue);
ConvertedObject.ofNull();
ConvertedObject.ofNotConvertible();getXOrNull. Instead, these methods are now called getXIfPresent to better reflect the return type Optional.Optional<Customer> customer =
businessPartner.getCustomerOrNull(); // returns Optional, not nullOptional<Customer> customer = businessPartner.getCustomerIfPresent();USE_MOCKED_TENANT and USE_MOCKED_USER for fine-granular control over mocking of the current tenant and user at runtime.true, the application will use a mocked tenant (with an empty tenant identifier "") and/or mocked user (with an empty user name "") instead of an actual tenant or user, respectively.ALLOW_MOCKED_AUTH_HEADER environment variable on Cloud Foundry, but in contrast to this variable it works on both Neo and Cloud Foundry and takes precedence over an actual tenant and user.MockUtil now uses the AuditLog, CloudPlatform, GenericDestination, Destination, RfcDestination, Tenant, User, and SecretStore type of the respective Cloud platform for mocking based on the chosen dependency of Cloud Foundry or Neo. This makes it easier to run the same code within tests, a local container, and on SAP Cloud Platform and allows to more easily test application logic that relies on the environment-specific implementation of a platform abstraction.private static final MockUtil mockUtil = new MockUtil();
@Test
public void test()
{
final Tenant currentTenant = mockUtil.mockCurrentTenant();
// currentTenant was mocked as an instance of Tenant
assertTrue( currentTenant instanceof ScpCfTenant ); // would fail
}private static final MockUtil mockUtil = new MockUtil();
@Test
public void test()
{
final Tenant currentTenant = mockUtil.mockCurrentTenant();
// currentTenant is now mocked as an instance of ScpCfTenant
assertTrue( currentTenant instanceof ScpCfTenant ); // works!
}s4sdk tag!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 16 | |
| 13 | |
| 11 | |
| 9 | |
| 9 | |
| 9 | |
| 9 | |
| 9 | |
| 7 | |
| 7 |