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

org.eclipse.dirigible.tests.DirigibleCleaner Maven / Gradle / Ivy

/*
 * Copyright (c) 2024 Eclipse Dirigible contributors
 *
 * All rights reserved. This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v2.0 which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * SPDX-FileCopyrightText: Eclipse Dirigible contributors SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.dirigible.tests;

import org.eclipse.dirigible.commons.config.DirigibleConfig;
import org.eclipse.dirigible.components.data.sources.manager.DataSourcesManager;
import org.eclipse.dirigible.components.database.DirigibleDataSource;
import org.eclipse.dirigible.database.sql.ISqlDialect;
import org.eclipse.dirigible.database.sql.dialects.SqlDialectFactory;
import org.eclipse.dirigible.repository.api.IRepository;
import org.eclipse.dirigible.repository.api.IRepositoryStructure;
import org.eclipse.dirigible.tests.util.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class DirigibleCleaner {

    private static final Logger LOGGER = LoggerFactory.getLogger(DirigibleCleaner.class);

    private final DataSourcesManager dataSourcesManager;
    private final IRepository dirigibleRepo;

    DirigibleCleaner(DataSourcesManager dataSourcesManager, IRepository dirigibleRepo) {
        this.dataSourcesManager = dataSourcesManager;
        this.dirigibleRepo = dirigibleRepo;
    }

    public void clean() {
        try {
            deleteDatabases();
            deleteCMSFolderFiles();
            unpublishResources();
        } catch (Throwable ex) {
            throw new IllegalStateException("Failed to cleanup resources", ex);
        }
    }

    private void deleteDatabases() {
        LOGGER.info("Deleting Dirigible databases...");

        deleteDirigibleDBData();
        deleteH2Folder();

        LOGGER.info("Dirigible databases have been deleted...");
    }

    /**
     * Execute this before H2 folder deletion because it is in memory DB. Otherwise, will remain data in
     * memory.
     */
    private void deleteDirigibleDBData() {
        DirigibleDataSource defaultDataSource = dataSourcesManager.getDefaultDataSource();
        dropAllTablesInSchema(defaultDataSource);
        dropAllSequencesInSchema(defaultDataSource);

        DirigibleDataSource systemDataSource = dataSourcesManager.getSystemDataSource();
        deleteAllTablesDataInSchema(systemDataSource);
        dropAllTablesInSchema(systemDataSource, "QRTZ_");

        deleteSchemas(defaultDataSource);
    }

    private void dropAllSequencesInSchema(DirigibleDataSource dataSource) {
        List sequences = getAllSequences(dataSource);
        LOGGER.info("Will drop [{}] sequences from data source [{}]. Sequences: {}", sequences.size(), dataSource, sequences);

        for (int idx = 0; idx < 4; idx++) {
            Iterator iterator = sequences.iterator();
            while (iterator.hasNext()) {
                String sequence = iterator.next();
                try (Connection connection = dataSource.getConnection()) {
                    String sql = SqlDialectFactory.getDialect(dataSource)
                                                  .drop()
                                                  .sequence(sequence)
                                                  .build();
                    try (PreparedStatement prepareStatement = connection.prepareStatement(sql)) {
                        prepareStatement.executeUpdate();
                        LOGGER.info("Dropped sequence [{}]", sequence);
                        iterator.remove();
                    }
                } catch (SQLException ex) {
                    LOGGER.warn("Failed to drop sequence [{}] in data source [{}]", sequence, dataSource, ex);
                }
            }
        }
    }

    private List getAllSequences(DataSource dataSource) {
        List sequences = new ArrayList<>();
        try (Connection connection = dataSource.getConnection();
                PreparedStatement prepareStatement = connection.prepareStatement(
                        "SELECT sequence_name FROM information_schema.sequences WHERE sequence_schema='public' OR sequence_schema='PUBLIC'")) {
            ResultSet resultSet = prepareStatement.executeQuery();
            while (resultSet.next()) {
                sequences.add(resultSet.getString(1));
            }
            return sequences;
        } catch (SQLException ex) {
            throw new IllegalStateException("Failed to get all sequences in data source:" + dataSource, ex);
        }
    }

    private void dropAllTablesInSchema(DirigibleDataSource dataSource, String... skipTablePrefixes) {
        Set tables = getAllTables(dataSource);
        for (String skipTablePrefix : skipTablePrefixes) {
            tables = tables.stream()
                           .filter(t -> !t.startsWith(skipTablePrefix))
                           .collect(Collectors.toSet());
        }

        LOGGER.info("Will drop [{}] tables from data source [{}]. Tables: {}", tables.size(), dataSource, tables);

        for (int idx = 0; idx < 4; idx++) { // execute it a few times due to constraint violations
            Iterator iterator = tables.iterator();
            while (iterator.hasNext()) {
                String tableName = iterator.next();
                try (Connection connection = dataSource.getConnection()) {
                    String sql = SqlDialectFactory.getDialect(dataSource)
                                                  .drop()
                                                  .table(tableName)
                                                  .cascade(true)
                                                  .build();
                    try (PreparedStatement prepareStatement = connection.prepareStatement(sql)) {
                        prepareStatement.executeUpdate();
                        LOGGER.info("Dropped table [{}]", tableName);
                        iterator.remove();
                    }
                } catch (SQLException ex) {
                    LOGGER.warn("Failed to drop table [{}] in data source [{}]", tableName, dataSource, ex);
                }
            }
        }
    }

    private Set getAllTables(DataSource dataSource) {
        Set tables = new HashSet<>();
        try (Connection connection = dataSource.getConnection();
                PreparedStatement prepareStatement = connection.prepareStatement(
                        "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='PUBLIC' OR TABLE_SCHEMA='public'")) {
            ResultSet resultSet = prepareStatement.executeQuery();
            while (resultSet.next()) {
                tables.add(resultSet.getString(1));
            }
            return tables;
        } catch (SQLException ex) {
            throw new IllegalStateException("Failed to get all tables in data source:" + dataSource, ex);
        }
    }

    private void deleteSchemas(DirigibleDataSource dataSource) {
        Set schemas = getSchemas(dataSource);
        schemas.remove("PUBLIC");
        schemas.remove("public");
        schemas.remove("INFORMATION_SCHEMA");
        schemas.remove("information_schema");
        schemas.removeIf(s -> s.startsWith("pg_"));

        LOGGER.info("Will drop schemas [{}] from data source [{}]", schemas, dataSource);
        schemas.forEach(schema -> deleteSchema(schema, dataSource));
    }

    private Set getSchemas(DataSource dataSource) {
        try {
            return getSchemas(dataSource, "SHOW SCHEMAS");
        } catch (SQLException ex) {
            try {
                return getSchemas(dataSource, "SELECT nspname FROM pg_catalog.pg_namespace");
            } catch (SQLException e) {
                IllegalStateException exc = new IllegalStateException("Failed to get all schemas from data source: " + dataSource, e);
                exc.addSuppressed(ex);
                throw exc;
            }
        }
    }

    private Set getSchemas(DataSource dataSource, String sql) throws SQLException {
        Set schemas = new HashSet<>();
        try (Connection connection = dataSource.getConnection();
                PreparedStatement preparedStatement = connection.prepareStatement(sql);
                ResultSet resultSet = preparedStatement.executeQuery()) {
            while (resultSet.next()) {
                schemas.add(resultSet.getString(1));
            }
            return schemas;
        }
    }

    private void deleteSchema(String schema, DirigibleDataSource dataSource) {
        LOGGER.info("Will drop schema [{}] from data source [{}]", schema, dataSource);
        try (Connection connection = dataSource.getConnection()) {
            ISqlDialect dialect = SqlDialectFactory.getDialect(dataSource);
            String sql = dialect.drop()
                                .schema(schema)
                                .cascade(true)
                                .generate();
            try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
                preparedStatement.executeUpdate();
            } catch (SQLException ex) {
                throw new IllegalStateException(
                        "Failed to drop schema [" + schema + "] from dataSource [" + dataSource + "] using sql: " + sql, ex);
            }
        } catch (SQLException ex) {
            throw new IllegalStateException("Failed to drop schema [" + schema + "] from dataSource [" + dataSource + "] ", ex);
        }
    }

    private void deleteAllTablesDataInSchema(DirigibleDataSource dataSource) {
        Set tables = getAllTables(dataSource);

        for (int idx = 0; idx < 4; idx++) { // execute it a few times due to constraint violations
            Iterator iterator = tables.iterator();
            while (iterator.hasNext()) {
                String table = iterator.next();
                try (Connection connection = dataSource.getConnection()) {
                    String sql = SqlDialectFactory.getDialect(dataSource)
                                                  .delete()
                                                  .from(table)
                                                  .build();
                    try (PreparedStatement prepareStatement = connection.prepareStatement(sql)) {
                        int rowsAffected = prepareStatement.executeUpdate();
                        LOGGER.info("Deleted [{}] from table [{}]", rowsAffected, table);
                        iterator.remove();
                    }
                } catch (SQLException ex) {
                    LOGGER.warn("Failed to delete data from table [{}] in data source [{}]", table, dataSource, ex);
                }
            }
        }
    }

    private void deleteH2Folder() {
        String h2Folder = getDirigibleSubfolder("h2");
        FileUtil.deleteFolder(h2Folder);
    }

    private String getDirigibleSubfolder(String folder) {
        return System.getProperty("user.dir") + File.separator + "target" + File.separator + "dirigible" + File.separator + folder;
    }

    private void unpublishResources() throws IOException {
        LOGGER.info("Deleting all Dirigible project resources from the repository...");

        Set userProjects = getUserProjects();
        deleteCurrentUserFolder();
        deleteDirigibleProjectsFromRegistry(userProjects);
        LOGGER.info("Dirigible project resources have been deleted.");
    }

    private Set getUserProjects() throws IOException {
        return new HashSet<>(getUserProjectsFromWorkingDir());
    }

    private List getUserProjectsFromWorkingDir() throws IOException {
        File usersRepoFolder = getUsersRepoFolder();
        if (usersRepoFolder.exists()) {
            List userProjectFiles = FileUtil.findFiles(usersRepoFolder, "project.json");
            return userProjectFiles.stream()
                                   .map(p -> p.toFile()
                                              .getParentFile()
                                              .getName())
                                   .toList();
        }
        LOGGER.info("Missing users repo folder [{}]", usersRepoFolder);
        return Collections.emptyList();
    }

    private File getUsersRepoFolder() {
        String repoBasePath = dirigibleRepo.getRepositoryPath();
        return new File(repoBasePath + File.separator + "users");
    }

    private void deleteCurrentUserFolder() {
        File usersFolder = getUsersRepoFolder();
        String currentUserFolder = usersFolder.getPath() + File.separator + DirigibleConfig.BASIC_ADMIN_USERNAME.getFromBase64Value();
        LOGGER.info("Will delete current user folder [{}]", currentUserFolder);
        FileUtil.deleteFolder(currentUserFolder);
    }

    private void deleteDirigibleProjectsFromRegistry(Set userProjects) {
        String repoBasePath = dirigibleRepo.getRepositoryPath() + IRepositoryStructure.PATH_REGISTRY_PUBLIC + File.separator;
        LOGGER.info("Will delete user projects [{}] from the registry [{}]", userProjects, repoBasePath);
        userProjects.forEach(projectName -> {
            String projectPath = repoBasePath + projectName;
            FileUtil.deleteFolder(projectPath);
        });
    }

    private void deleteCMSFolderFiles() throws IOException {
        String cmdFolder = getDirigibleSubfolder("cms");
        FileUtil.findFiles(cmdFolder)
                .stream()
                .map(Path::toFile)
                .forEach(File::delete);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy