All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cloud.mt.subscription.InstanceLifecycleManagerSqlDb Maven / Gradle / Ivy

There is a newer version: 3.3.1
Show newest version
/*******************************************************************************
 *   © 2019-2024 SAP SE or an SAP affiliate company. All rights reserved.
 ******************************************************************************/

package com.sap.cloud.mt.subscription;

import com.sap.cloud.mt.subscription.exceptions.InternalError;
import com.sap.cloud.mt.subscription.exceptions.UnknownTenant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

public class InstanceLifecycleManagerSqlDb implements InstanceLifecycleManager {
	protected static final String SCHEMA_PREFIX = "MT-";
	private static Logger logger = LoggerFactory.getLogger(InstanceLifecycleManagerSqlDb.class);
	private DbIdentifiersSql dbIdentifiers;
	private final SqlOperations sqlOperations;
	private static final ConcurrentHashMap tenantToMutex = new ConcurrentHashMap<>();
	private final DbIdentifiers.DB db;

	public InstanceLifecycleManagerSqlDb(DbIdentifiersSql dbIdentifiers) throws InternalError {
		if (dbIdentifiers == null || !dbIdentifiers.areSet()) {
			throw new InternalError("No databases specified");
		}
		this.dbIdentifiers = dbIdentifiers;
		this.sqlOperations = SqlOperations.build(dbIdentifiers.getDB());
		this.db = dbIdentifiers.getDB();
	}

	@Override
	public void createNewInstance(String tenantId, ProvisioningParameters provisioningParameters, BindingParameters bindingParameters) throws InternalError {
		synchronized (getMutex(tenantId)) {
			if (doesTenantExist(tenantId)) {
				return;
			}
			String databaseId = null;
			if (provisioningParameters != null) {
				databaseId = provisioningParameters.getDatabaseId();
			}
			Optional credentials;
			if (databaseId != null) {
				credentials = dbIdentifiers.getCredentials(databaseId);
			} else {
				credentials = dbIdentifiers.getLast();
			}
			if (!credentials.isPresent()) {
				throw new InternalError("No database credentials available for database ID " + databaseId);
			}
			try {
				DbCredentials cred = credentials.get();
				try (Connection connection = getConnection(cred)) {
					sqlOperations.createSchema(getSchemaName(tenantId), connection);
					if (!connection.getAutoCommit()) {
						connection.commit();
					}
				}
			} catch (SQLException e) {
				throw new InternalError(e);
			}
		}
	}

	@Override
	public void deleteInstance(String tenantId) throws InternalError {
		synchronized (getMutex(tenantId)) {
			Optional credentials = getCredentials(tenantId);
			if (!credentials.isPresent()) {
				logger.warn("No schema for tenant {} found", tenantId);
				return;
			}
			try (Connection connection = getConnection(credentials.get())) {
				sqlOperations.deleteSchema(getSchemaName(tenantId), connection);
				if (!connection.getAutoCommit()) {
					connection.commit();
				}
			} catch (SQLException e) {
				throw new InternalError(e);
			}
		}
	}

	@Override
	public DataSourceInfo getDataSourceInfo(String tenantId, boolean forceCacheUpdate) throws InternalError, UnknownTenant {
		Optional credentials = getCredentials(tenantId);
		if (!credentials.isPresent()) {
			throw new UnknownTenant("No schema found for tenant " + tenantId);
		}
		DbCredentials cred = credentials.get();
		return DataSourceInfoBuilder.createBuilder().driver(cred.getDriver()).host(cred.getHost())
				.port(cred.getPort()).user(cred.getUser()).password(cred.getPassword()).statusAsText("ok")
				.url(cred.getUrl()).schema(getSchemaName(tenantId)).tenantId(tenantId).id(tenantId)
				.dbKey(cred.getUrl())
				.databaseId(cred.getDatabaseId())
				.build();
	}

	@Override
	public ContainerStatus getContainerStatus(String tenantId) throws InternalError {
		if (!doesTenantExist(tenantId)) {
			return ContainerStatus.DOES_NOT_EXIST;
		}
		return ContainerStatus.OK;
	}

	@Override
	public boolean hasCredentials(String tenantId, boolean forceCacheUpdate) throws InternalError {
		return getContainerStatus(tenantId).equals(ContainerStatus.OK);
	}

	@Override
	public Map getAllTenantInfos(boolean forceCacheUpdate) throws InternalError {
		Map tenants = new HashMap<>();
		List credentialsWithError = new ArrayList<>();
		List sqlExceptions = new ArrayList<>();
		dbIdentifiers.asStream().forEach(cred -> {
			try (Connection connection = getConnection(cred)) {
				sqlOperations.getAllSchemas(connection).stream()
						.filter(s -> s.startsWith(SCHEMA_PREFIX))
						.map(s -> {
							TenantMetadata tenantInfo = new TenantMetadata(s.replace(SCHEMA_PREFIX, ""));
							if (cred.getDatabaseId() != null) {
								tenantInfo.putAdditionalProperty(DATABASE_ID, cred.getDatabaseId());
							}
							return tenantInfo;
						})
						.filter(tenantInfo -> FilterTenants.realTenants().test(tenantInfo.getTenantId()))
						.forEach(t -> tenants.put(t.getTenantId(), t));
			} catch (SQLException e) {
				logger.error("Cannot access database {} because of {}", cred.getUrl(), e.getMessage());
				sqlExceptions.add(e);
				credentialsWithError.add(cred);
			}
		});
		if (!sqlExceptions.isEmpty()) {
			throw new InternalError("Cannot access database " + credentialsWithError.get(0).getUrl(), sqlExceptions.get(0));
		}
		return tenants;
	}

	@Override
	public void checkThatTenantExists(String tenantId) throws UnknownTenant {
		try {
			if (!doesTenantExist(tenantId)) {
				throw new UnknownTenant("No schema for tenant " + tenantId);
			}
		} catch (InternalError internalError) {
			throw new UnknownTenant(internalError, "Could not access DB.");
		}
	}

	@Override
	public List createAndGetLibContainers(DataSourceInfo dataSourceInfo) throws InternalError {
		List internalErrors = new ArrayList<>();
		dbIdentifiers.asStream().forEach(cred -> {
			String mtLibTenantId = getMtLibContainerName(cred.getDatabaseId());
			try {
				if (!doesTenantExist(mtLibTenantId)) {
					synchronized (getMutex(mtLibTenantId)) {
						createNewInstance(mtLibTenantId, createProvisioningParameters(cred.getDatabaseId()), new BindingParameters());
					}
				}
			} catch (InternalError internalError) {
				logger.error("Could not access DB with {}, error is {}", cred.getUrl(), internalError.getMessage());
				internalErrors.add(internalError);
			}
		});
		if (!internalErrors.isEmpty()) {
			throw internalErrors.get(0);
		}
		return getLibContainers();
	}

	@Override
	public List getLibContainers() throws InternalError {
		List internalErrors = new ArrayList<>();
		List dsInfo = new ArrayList<>();
		dbIdentifiers.asStream().forEach(cred -> {
			String mtLibTenantId = getMtLibContainerName(cred.getDatabaseId());
			try {
				if (!doesTenantExist(mtLibTenantId)) {
					return;
				}
				try {
					dsInfo.add(getDataSourceInfo(mtLibTenantId, true));
				} catch (InternalError | UnknownTenant error) {
					logger.error("Could not retrieve credentials for schema {}", mtLibTenantId);
					internalErrors.add(new InternalError(error));
				}
			} catch (InternalError internalError) {
				logger.error("Could not access DB with {}, error is {}", cred.getUrl(), internalError.getMessage());
				internalErrors.add(internalError);
			}
		});
		if (!internalErrors.isEmpty()) {
			throw internalErrors.get(0);
		}
		return dsInfo;
	}

	private Connection getConnection(DbCredentials cred) throws SQLException {
		return DriverManager.getConnection(cred.getUrl(), cred.getUser(), cred.getPassword());
	}

	private boolean doesTenantExist(String tenantId) throws InternalError {
		Optional credentials = getCredentials(tenantId);
		return credentials.isPresent();
	}

	private Optional getCredentials(String tenantId) throws InternalError {
		List errors = new ArrayList<>();
		Optional credentials = dbIdentifiers.asStream().filter(cred -> {
			try (Connection connection = getConnection(cred)) {
				return sqlOperations.doesSchemaExist(getSchemaName(tenantId), connection);
			} catch (SQLException e) {
				logger.error("Could not access DB {}", cred.getUrl());
				errors.add(new InternalError(e));
				return false;
			}
		}).findFirst();
		if (!errors.isEmpty()) {
			throw new InternalError(errors.get(0));
		}
		return credentials;
	}

	private String getSchemaName(String tenantId) {
		return SCHEMA_PREFIX + tenantId;
	}

	private static Mutex getMutex(String tenantId) {
		Mutex mutex = tenantToMutex.get(tenantId);
		if (mutex != null) {
			return mutex;
		}
		Mutex newMutex = new Mutex();
		Mutex storedMutex = tenantToMutex.putIfAbsent(tenantId, newMutex);
		if (storedMutex != null) {
			return storedMutex;
		} else {
			return newMutex;
		}
	}

	@Override
	public void insertDbIdentifiers(DbIdentifiers dbIdentifiers) {
		this.dbIdentifiers = (DbIdentifiersSql) dbIdentifiers;
	}

	protected DbIdentifiers getDbIdentifiers() {
		return dbIdentifiers.createCopy();
	}

	@Override
	public boolean hasDbIdentifiers() {
		return dbIdentifiers != null && dbIdentifiers.areSet();
	}

	@Override
	public DbIdentifiers.DB getDbType() {
		return db;
	}

	private static class Mutex {//NOSONAR
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy