com.sap.cloud.mt.subscription.SubscriberSidecarImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of multi-tenant-subscription Show documentation
Show all versions of multi-tenant-subscription Show documentation
Spring Boot Enablement Parent
/******************************************************************************
* © 2020 SAP SE or an SAP affiliate company. All rights reserved. *
******************************************************************************/
package com.sap.cloud.mt.subscription;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sap.cloud.mt.subscription.exceptions.InternalError;
import com.sap.cloud.mt.subscription.exceptions.*;
import com.sap.cloud.mt.subscription.exits.Exits;
import com.sap.cloud.mt.subscription.json.*;
import com.sap.xsa.core.instancemanager.client.InstanceCreationOptions;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class SubscriberSidecarImpl extends AbstractSubscriber {
private static final String SIDECAR_PROVISIONING_ENDPOINT = "/mtx/v1/provisioning/tenant/";
private static final String SIDECAR_UPGRADE_ENDPOINT = "/mtx/v1/model/asyncUpgrade";
private static final String SIDECAR_STATUS_ENDPOINT = "/mtx/v1/model/status/";
private static final Logger logger = LoggerFactory.getLogger(SubscriberSidecarImpl.class);
private static final String APPLICATION_JSON = "application/json";
private static final String STATUS_CALLBACK = "STATUS_CALLBACK";
private static final String MT_LIB_CALLBACK_URL = "MTX_STATUS_CALLBACK";
private final Exits exits;
private final String sidecarUrl;
private final String baseUiUrl;
private final String urlSeparator;
private final SecurityChecker securityChecker;
private final SaasRegistry saasRegistry;
SubscriberSidecarImpl(String sidecarUrl,
String baseUiUrl,
String urlSeparator,
Exits exits,
SecurityChecker securityChecker, SaasRegistry saasRegistry) throws InternalError {
this.sidecarUrl = sidecarUrl;
this.exits = exits;
this.baseUiUrl = baseUiUrl;
this.urlSeparator = urlSeparator;
this.saasRegistry = saasRegistry;
if (exits.getUnSubscribeExit() == null) throw new InternalError("No unsubscribe exit found");
this.securityChecker = securityChecker;
}
@Override
public void unsubscribe(String tenantId, DeletePayload deletePayload, String jwt) throws InternalError, UnknownTenant, ParameterError, AuthorityError {
unsubscribe(tenantId, deletePayload, jwt, null, null);
}
@Override
public void unsubscribe(String tenantId, DeletePayload deletePayload, String jwt, String saasRegistryUrl,
String asyncUnsubscribeCallBackUrl) throws InternalError, UnknownTenant, ParameterError, AuthorityError {
boolean asyncCall = isAsyncCall(saasRegistryUrl);
securityChecker.checkSubscriptionAuthority();
Tools.checkTenantId(tenantId);
boolean processUnsubscribe = false;
if (asyncCall) {
processUnsubscribe = exits.getUnSubscribeExit().onBeforeAsyncUnsubscribe(tenantId, Cloner.clone(deletePayload));
} else {
processUnsubscribe = exits.getUnSubscribeExit().onBeforeUnsubscribe(tenantId, Cloner.clone(deletePayload));
}
if (processUnsubscribe) {
if (asyncCall) {
long startTime = System.nanoTime();
try {
unsubscribeViaSidecar(tenantId, jwt, deletePayload, true, saasRegistryUrl, asyncUnsubscribeCallBackUrl);
} catch (InternalError e) {
waitSomeTime(startTime);
saasRegistry.callBackSaasRegistry(false, e.getMessage(), null, saasRegistryUrl);
throw e;
}
} else {
unsubscribeViaSidecar(tenantId, jwt, deletePayload, false, null, asyncUnsubscribeCallBackUrl);
exits.getUnSubscribeExit().onAfterUnsubscribe(tenantId, Cloner.clone(deletePayload));
}
} else {
logger.debug("Unsubscribe exit returned false=> No un-subscription performed");
throw new InternalError("Unsubscribe exit returned false=> No unsubscription performed");
}
}
private boolean isAsyncCall(String saasRegistryUrl) {
return saasRegistryUrl != null && !saasRegistryUrl.isEmpty();
}
@Override
public void unsubscribeCallback(SidecarUnSubscribeCallBackPayload sidecarPayload, String jwt) throws AuthorityError, InternalError {
securityChecker.checkSubscriptionAuthority();
boolean success = sidecarPayload.status.equals(SaasRegistry.SUCCEEDED);
if (success) {
exits.getUnSubscribeExit().onAfterAsyncUnsubscribe(sidecarPayload.tenantId, Cloner.clone(sidecarPayload.saasRequestPayload));
} else {
logger.error(sidecarPayload.message);
}
try {
saasRegistry.callBackSaasRegistry(success, sidecarPayload.message, null, sidecarPayload.saasCallbackUrl);
} catch (InternalError e) {
logger.error(e.getMessage());
try {
saasRegistry.callBackSaasRegistry(false, e.getMessage(), null, sidecarPayload.saasCallbackUrl);
} catch (InternalError internalError) {
logger.error(internalError.getMessage());
}
}
}
@Override
public List getApplicationDependencies(String jwt) throws AuthorityError {
securityChecker.checkSubscriptionAuthority();
return exits.getDependencyExit() != null ? exits.getDependencyExit().onGetDependencies() : new ArrayList<>();
}
@Override
public String subscribe(String tenantId, SubscriptionPayload subscriptionPayload, String jwt) throws InternalError, ParameterError, AuthorityError {
return subscribe(tenantId, subscriptionPayload, jwt, null, null);
}
@Override
public String subscribe(String tenantId, SubscriptionPayload subscriptionPayload, String jwt, String saasRegistryUrl,
String asyncSubscribeCallBackUrl) throws InternalError, ParameterError, AuthorityError {
boolean asyncCall = isAsyncCall(saasRegistryUrl);
securityChecker.checkSubscriptionAuthority();
Tools.checkTenantId(tenantId);
// for future use, currently not supported by sidecar
InstanceCreationOptions instanceCreationOptions = null;
if (asyncCall) {
instanceCreationOptions = exits.getSubscribeExit().onBeforeAsyncSubscribe(tenantId, Cloner.clone(subscriptionPayload));
} else {
instanceCreationOptions = exits.getSubscribeExit().onBeforeSubscribe(tenantId, Cloner.clone(subscriptionPayload));
}
//get application URL for CIS from exit
String applicationUrl = exits.getSubscribeExit().uiURL() != null ? exits.getSubscribeExit().uiURL().toExternalForm() : null;
//build URL from properties if not set by exit
if (applicationUrl == null) applicationUrl = UiUrlCreator.createUrl(subscriptionPayload.subscribedSubdomain, baseUiUrl, urlSeparator);
if (asyncCall) {
long startTime = System.nanoTime();
try {
subscribeViaSidecar(tenantId, jwt, subscriptionPayload, instanceCreationOptions, applicationUrl, true, saasRegistryUrl,
asyncSubscribeCallBackUrl);
} catch (InternalError e) {
exits.getSubscribeExit().onAfterAsyncSubscribe(tenantId, Cloner.clone(subscriptionPayload), false);
waitSomeTime(startTime);
saasRegistry.callBackSaasRegistry(false, e.getMessage(), null, saasRegistryUrl);
throw e;
}
} else {
try {
subscribeViaSidecar(tenantId, jwt, subscriptionPayload, instanceCreationOptions, applicationUrl, false, null,
asyncSubscribeCallBackUrl);
exits.getSubscribeExit().onAfterSubscribe(tenantId, Cloner.clone(subscriptionPayload), true);
} catch (InternalError internalError) {
exits.getSubscribeExit().onAfterSubscribe(tenantId, Cloner.clone(subscriptionPayload), false);
throw internalError;
}
}
return applicationUrl;
}
@Override
public void subscribeCallback(String jwt, SidecarSubscribeCallBackPayload sidecarPayload) throws AuthorityError, InternalError {
securityChecker.checkSubscriptionAuthority();
boolean success = sidecarPayload.status.equals(SaasRegistry.SUCCEEDED);
exits.getSubscribeExit().onAfterAsyncSubscribe(sidecarPayload.tenantId, Cloner.clone(sidecarPayload.saasRequestPayload), success);
if (!success) {
logger.error(sidecarPayload.message);
}
//be careful: sidecarPayload.subscriptionUrl contains the url determined by sidecar and not the url determined by us. It will be empty.
//Our url is put into the subscription payload extended by us, which is returned as sidecarPayload.saasRequestPayload
saasRegistry.callBackSaasRegistry(success, sidecarPayload.message, sidecarPayload.saasRequestPayload._applicationUrlFromJava_,
sidecarPayload.saasCallbackUrl);
}
@Override
public void setupDbTables(List tenants, String jwt) throws InternalError, NotSupported, AuthorityError {
securityChecker.checkInitDbAuthority();
logger.debug("Synchronous init db is not supported with sidecar");
throw new NotSupported("Synchronous init db is not supported with sidecar");
}
@Override
public String setupDbTablesAsync(List tenants, String jwt) throws InternalError, AuthorityError {
securityChecker.checkInitDbAuthority();
SidecarUpgradePayload upgradePayload = new SidecarUpgradePayload();
upgradePayload.tenants = tenants.toArray(new String[tenants.size()]);
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpUriRequest postRequest = RequestBuilder.post(new URL(sidecarUrl + SIDECAR_UPGRADE_ENDPOINT).toURI())
.setHeader(HttpHeaders.AUTHORIZATION, jwt)
.setHeader(HttpHeaders.ACCEPT, APPLICATION_JSON)
.setHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.setEntity(new StringEntity(convertPayloadToString(upgradePayload)))
.build();
try (CloseableHttpResponse response = httpClient.execute(postRequest)) {
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode != HttpStatus.SC_OK) {
logger.error("Sidecar returned http status {} ", responseCode);
throw new InternalError("Sidecar returned http status " + responseCode);
}
try {
if (response.getEntity() == null) {
return "";
}
return EntityUtils.toString(response.getEntity());
} catch (IOException e) {
logger.error("Cannot access return value from sidecar. Error is {}", e.getMessage());
throw new InternalError("Cannot access return value from sidecar.", e);
}
}
} catch (URISyntaxException | MalformedURLException e) {
throw new InternalError(e);
} catch (IOException e) {
logger.error(e.getMessage());
throw new InternalError(e);
}
}
@Override
public String updateStatus(String jobId, String jwt) throws InternalError, ParameterError, NotFound, AuthorityError {
securityChecker.checkInitDbAuthority();
if (!jobId.matches(Tools.SECURE_CHARS)) {
throw new ParameterError("Job id contains illegal characters");
}
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpUriRequest getRequest = RequestBuilder.get(new URL(sidecarUrl + SIDECAR_STATUS_ENDPOINT + jobId).toURI())
.setHeader(HttpHeaders.AUTHORIZATION, jwt)
.setHeader(HttpHeaders.ACCEPT, APPLICATION_JSON)
.build();
try (CloseableHttpResponse response = httpClient.execute(getRequest)) {
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode == HttpStatus.SC_NOT_FOUND) {
throw new NotFound("Job id not known");
}
if (responseCode != HttpStatus.SC_OK) {
throw new InternalError("Sidecar returned http status " + responseCode);
}
if (response.getEntity() == null) {
return "";
}
try {
return EntityUtils.toString(response.getEntity());
} catch (IOException e) {
throw new InternalError("Could not access return message from sidecar", e);
}
}
} catch (URISyntaxException | MalformedURLException e) {
throw new InternalError(e);
} catch (IOException e) {
logger.error(e.getMessage());
throw new InternalError(e);
}
}
private String convertPayloadToString(Object payload) throws JsonProcessingException {
String body = "{}";
if (payload != null) {
body = Tools.getObjectMapper().writeValueAsString(payload);
}
return body;
}
private SidecarSubscriptionPayload getSubscriptionPayloadForSidecar(SubscriptionPayload subscriptionPayload, InstanceCreationOptions instanceCreationOptions) {
SidecarSubscriptionPayload payload = new SidecarSubscriptionPayload(subscriptionPayload);
boolean hasProvisioningParameters = false;
boolean hasBindingParameters = false;
if (instanceCreationOptions != null) {
if (instanceCreationOptions.getProvisioningParameters() != null && instanceCreationOptions.getProvisioningParameters().size() > 0) {
hasProvisioningParameters = true;
}
if (instanceCreationOptions.getBindingParameters() != null && instanceCreationOptions.getBindingParameters().size() > 0) {
hasBindingParameters = true;
}
}
if (hasProvisioningParameters || hasBindingParameters) {
payload._application_ = new ApplicationForSidecar();
payload._application_.sap = new SAPForSidecar();
payload._application_.sap.managedHana = new ManagedHanaForSidecar();
if (hasProvisioningParameters) {
payload._application_.sap.managedHana.provisioning_parameters = instanceCreationOptions.getProvisioningParameters();
}
if (hasBindingParameters) {
payload._application_.sap.managedHana.binding_parameters = instanceCreationOptions.getBindingParameters();
}
}
return payload;
}
private void subscribeViaSidecar(String tenantId, String jwt, SubscriptionPayload subscriptionPayload,
InstanceCreationOptions instanceCreationOptions, String applicationUrl,
boolean asyncCall, String saasRegistryUrl,
String asyncSubscribeCallBackUrl) throws InternalError {
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
SidecarSubscriptionPayload sidecarPayload = getSubscriptionPayloadForSidecar(subscriptionPayload, instanceCreationOptions);
sidecarPayload._applicationUrlFromJava_ = applicationUrl;
RequestBuilder buildPutRequest = RequestBuilder.put(new URL(sidecarUrl + SIDECAR_PROVISIONING_ENDPOINT + tenantId).toURI())
.setHeader(HttpHeaders.AUTHORIZATION, jwt)
.setHeader(HttpHeaders.ACCEPT, APPLICATION_JSON)
.setHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
.setEntity(new StringEntity(convertPayloadToString(sidecarPayload)));
if (asyncCall) {
buildPutRequest.setHeader(MT_LIB_CALLBACK_URL, asyncSubscribeCallBackUrl);
buildPutRequest.setHeader(STATUS_CALLBACK, saasRegistryUrl);
}
HttpUriRequest putRequest = buildPutRequest.build();
try (CloseableHttpResponse response = httpClient.execute(putRequest)) {
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode != HttpStatus.SC_CREATED && responseCode != HttpStatus.SC_OK && responseCode != HttpStatus.SC_ACCEPTED) {
logger.error("Sidecar returned http status {} ", responseCode);
throw new InternalError("Sidecar returned http status " + responseCode);
}
}
} catch (URISyntaxException | IOException e) {
logger.error(e.getMessage());
exits.getSubscribeExit().onAfterSubscribe(tenantId, Cloner.clone(subscriptionPayload), false);
throw new InternalError(e);
}
}
private void unsubscribeViaSidecar(String tenantId, String jwt, DeletePayload deletePayloadCopy, boolean asyncCall,
String saasRegistryUrl,
String asyncUnsubscribeCallBackUrl) throws InternalError {
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
SidecarUnSubscriptionPayload sidecarPayload = new SidecarUnSubscriptionPayload(deletePayloadCopy);
RequestBuilder buildDelRequest = RequestBuilder.delete(new URL(sidecarUrl + SIDECAR_PROVISIONING_ENDPOINT + tenantId).toURI())
.setHeader(HttpHeaders.AUTHORIZATION, jwt)
.setHeader(HttpHeaders.ACCEPT, APPLICATION_JSON)
.setHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON);
if (asyncCall) {
buildDelRequest.setHeader(MT_LIB_CALLBACK_URL, asyncUnsubscribeCallBackUrl);
buildDelRequest.setHeader(STATUS_CALLBACK, saasRegistryUrl);
buildDelRequest.setEntity(new StringEntity(convertPayloadToString(sidecarPayload)));
;
}
HttpUriRequest delRequest = buildDelRequest.build();
try (CloseableHttpResponse response = httpClient.execute(delRequest)) {
int responseCode = response.getStatusLine().getStatusCode();
if (responseCode != HttpStatus.SC_ACCEPTED && responseCode != HttpStatus.SC_NO_CONTENT) {
throw new InternalError("Sidecar returned http status " + responseCode);
}
}
} catch (URISyntaxException | MalformedURLException e) {
logger.error("Url cannot be constructed. Error is {}", e.getMessage());
throw new InternalError(e);
} catch (IOException e) {
logger.error(e.getMessage());
throw new InternalError(e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy