
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_HEADER
environment 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
}
Optional
s 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());
RequestContextListener
s were initialized using ServletContextListener
s. The downside of this was that RequestContextListener
s 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 more
CacheKey.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 null
Optional<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 |
---|---|
19 | |
18 | |
16 | |
10 | |
6 | |
6 | |
6 | |
6 | |
6 | |
5 |