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
/*
 * *************************************************************************
 *  * (C) 2019-2021 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, 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, 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