com.sap.cds.feature.mt.MtUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cds-feature-mt Show documentation
Show all versions of cds-feature-mt Show documentation
Multi tenancy feature for CDS Services Java
/**************************************************************************
* (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
**************************************************************************/
package com.sap.cds.feature.mt;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.google.common.annotations.VisibleForTesting;
import com.sap.cds.services.environment.CdsProperties.MultiTenancy;
import com.sap.cds.services.runtime.CdsRuntime;
import com.sap.cds.services.utils.CdsErrorStatuses;
import com.sap.cds.services.utils.ErrorStatusException;
import com.sap.cds.services.utils.StringUtils;
import com.sap.cds.services.utils.environment.ServiceBindingUtils;
import com.sap.cds.services.utils.model.DynamicModelUtils;
import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
import com.sap.cloud.mt.subscription.DbCredentials;
import com.sap.cloud.mt.subscription.DbCredentialsBuilder;
import com.sap.cloud.mt.subscription.DbIdentifiersSqLite;
import com.sap.cloud.mt.subscription.DbIdentifiersSql;
import com.sap.cloud.mt.subscription.InstanceLifecycleManager;
import com.sap.cloud.mt.subscription.InstanceLifecycleManagerBuilder;
import com.sap.cloud.mt.subscription.PollingParameters;
import com.sap.cloud.mt.subscription.ServiceManager;
import com.sap.cloud.mt.subscription.ServiceSpecification;
import com.sap.cloud.mt.subscription.exceptions.InternalError;
public class MtUtils {
private static final String HANA_OFFERING_NAME = "hana";
private static final String HANA_HDI_PLAN_NAME = "hdi-shared";
static final String SERVICE_MANAGER = "service-manager";
static final String MT_ENABLED = "mt-enabled";
static final String SQLITE_BINDING_NAME = "mtx-sqlite";
private final DynamicModelUtils dynamicModelUtils;
private final MultiTenancy config;
private final List bindings;
private final ServiceBinding defaultBinding;
public MtUtils(CdsRuntime runtime) {
this.dynamicModelUtils = new DynamicModelUtils(runtime);
this.config = runtime.getEnvironment().getCdsProperties().getMultiTenancy();
// calculate bindings
this.bindings = runtime.getEnvironment().getServiceBindings()
.filter(b -> isServiceManagerBinding(b) || isSchemaBasedMtBinding(b))
.collect(Collectors.toList());
String bindingName = runtime.getEnvironment().getCdsProperties().getDataSource().getBinding();
if (StringUtils.isEmpty(bindingName)) {
if (bindings.size() == 1) {
defaultBinding = bindings.get(0);
} else if (bindings.size() > 1) {
throw new ErrorStatusException(CdsErrorStatuses.NO_UNIQUE_DATASOURCE_SERVICE);
} else {
defaultBinding = null;
}
} else {
defaultBinding = bindings.stream().filter(b -> b.getName().get().equals(bindingName)).findFirst().orElse(null);
}
}
public boolean requiresSubscription() {
boolean deployerConfigured = hasDeployer();
return (defaultBinding != null && (deployerConfigured || isSchemaBasedMtBinding(defaultBinding)))
|| (isSqliteDataSourceEnabled() && deployerConfigured);
}
@SuppressWarnings("deprecation")
private boolean hasDeployer() {
boolean dynamicDeployer = !StringUtils.isEmpty(config.getDeployer().getUrl());
boolean provisioningService = isProvisioningServiceEnabled();
if (dynamicDeployer && provisioningService) {
throw new IllegalArgumentException("Either Sidecar/ProvisioningService or Deployer URL can be specified");
}
return dynamicDeployer || provisioningService;
}
// ProvisioningService:
public boolean isProvisioningServiceEnabled() {
return !StringUtils.isEmpty(getProvisioningServiceUrl());
}
public String getProvisioningServiceUrl() {
return !StringUtils.isEmpty(config.getProvisioning().getUrl()) ? config.getProvisioning().getUrl() : config.getSidecar().getUrl();
}
// Bindings & InstanceLifecycleManagers:
public InstanceLifecycleManager createDefaultInstanceLifecycleManager() {
if (defaultBinding != null) {
return createInstanceLifecycleManager(defaultBinding);
} else if (isSqliteDataSourceEnabled()) {
return createInstanceLifecycleManagerSqlite();
}
return null;
}
public Map createInstanceLifecycleManagers() {
Map ilms = new HashMap<>();
for (ServiceBinding binding : bindings) {
ilms.put(binding.getName().get(), createInstanceLifecycleManager(binding)); // NOSONAR
}
if (isSqliteDataSourceEnabled()) {
ilms.put(SQLITE_BINDING_NAME, createInstanceLifecycleManagerSqlite());
}
return ilms;
}
private boolean isSqliteDataSourceEnabled() {
return bindings.isEmpty() && !Boolean.FALSE.equals(config.getMock().isEnabled()) && (isProvisioningServiceEnabled() || Boolean.TRUE.equals(config.getMock().isEnabled()));
}
private boolean isServiceManagerBinding(ServiceBinding b) {
return ServiceBindingUtils.matches(b, SERVICE_MANAGER);
}
private boolean isSchemaBasedMtBinding(ServiceBinding b) {
return ServiceBindingUtils.matches(b, MT_ENABLED, null);
}
private InstanceLifecycleManager createInstanceLifecycleManagerSqlite() {
InstanceLifecycleManagerBuilder builder = InstanceLifecycleManagerBuilder.create();
String sqliteDirectory = config.getMock().getSqliteDirectory();
if (!StringUtils.isEmpty(sqliteDirectory)) {
builder.dbIdentifiers(new DbIdentifiersSqLite(Paths.get(sqliteDirectory)));
} else {
builder.dbIdentifiers(new DbIdentifiersSqLite(Paths.get(System.getProperty("user.dir"))));
}
try {
return builder.build();
} catch (InternalError internalError) {
throw new ErrorStatusException(CdsErrorStatuses.MT_LIB_SETUP_FAILED, internalError);
}
}
private InstanceLifecycleManager createInstanceLifecycleManager(ServiceBinding binding) {
InstanceLifecycleManagerBuilder builder = InstanceLifecycleManagerBuilder.create();
InstanceLifecycleManager newIlm;
if (isServiceManagerBinding(binding)) {
builder.serviceManager(createServiceManager(binding));
builder.smCacheRefreshInterval(config.getServiceManager().getCacheRefreshInterval());
builder.acceptInstancesWithoutTenant(config.getServiceManager().getAcceptInstancesWithoutTenant().isEnabled());
builder.smCacheResilienceConfig(dynamicModelUtils.getResilienceConfig());
try {
newIlm = builder.build();
} catch (InternalError internalError) {
throw new ErrorStatusException(CdsErrorStatuses.INSTANCE_MANAGER_CLIENT_FAILED, binding.getName().get(), internalError); // NOSONAR
}
} else {
try {
DbCredentials dbCredentials = DbCredentialsBuilder.create()
.credentials(binding.getCredentials())
.build();
builder.dbIdentifiers(new DbIdentifiersSql(Collections.singletonList(dbCredentials)));
newIlm = builder.build();
} catch (InternalError internalError) {
throw new ErrorStatusException(CdsErrorStatuses.MT_LIB_SETUP_FAILED, internalError.getMessage(), internalError);
}
}
return newIlm;
}
@VisibleForTesting
ServiceManager createServiceManager(ServiceBinding binding) {
try {
return new ServiceManager(binding,
ServiceSpecification.Builder.create()
.polling(PollingParameters.Builder.create()
.interval(Duration.ofSeconds(10))
.timeout(Duration.ofMinutes(20))
.build())
.resilienceConfig(dynamicModelUtils.getResilienceConfig())
.build(),
HANA_OFFERING_NAME, HANA_HDI_PLAN_NAME);
} catch (InternalError e) {
throw new ErrorStatusException(CdsErrorStatuses.INSTANCE_MANAGER_CLIENT_FAILED, binding.getName().get(), e); // NOSONAR
}
}
@VisibleForTesting
ServiceBinding getDefaultBinding() {
return defaultBinding;
}
}