/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.flowable.common.engine.impl.db;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.AbstractEngineConfiguration;
import org.flowable.common.engine.impl.context.Context;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.resource.ClassLoaderResourceAccessor;

/**
 * @author Filip Hrisafov
 */
public abstract class LiquibaseBasedSchemaManager implements SchemaManager {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    protected final String context;
    protected final String changeLogFile;
    protected final String changeLogPrefix;

    public LiquibaseBasedSchemaManager(String context, String changeLogFile, String changeLogPrefix) {
        this.context = context;
        this.changeLogFile = changeLogFile;
        this.changeLogPrefix = changeLogPrefix;
    }

    public void initSchema(String databaseSchemaUpdate) {
        try {
            if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_CREATE_DROP.equals(databaseSchemaUpdate)) {
                schemaCreate();

            } else if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_DROP_CREATE.equals(databaseSchemaUpdate)) {
                schemaDrop();
                schemaCreate();

            } else if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_TRUE.equals(databaseSchemaUpdate)) {
                schemaUpdate();

            } else if (AbstractEngineConfiguration.DB_SCHEMA_UPDATE_FALSE.equals(databaseSchemaUpdate)) {
                schemaCheckVersion();

            }
        } catch (Exception e) {
            throw new FlowableException("Error initialising " + context + " data model", e);
        }
    }

    @Override
    public void schemaCreate() {
        Liquibase liquibase = null;
        try {
            liquibase = createLiquibaseInstance(getDatabaseConfiguration());
            liquibase.update(context);
        } catch (Exception e) {
            throw new FlowableException("Error creating " + context + " engine tables", e);
        } finally {
            closeDatabase(liquibase);
        }
    }

    @Override
    public void schemaDrop() {
        Liquibase liquibase = null;
        try {
            liquibase = createLiquibaseInstance(getDatabaseConfiguration());
            liquibase.dropAll();
        } catch (Exception e) {
            throw new FlowableException("Error dropping " + context + " engine tables", e);
        } finally {
            closeDatabase(liquibase);
        }
    }

    @Override
    public String schemaUpdate() {
        Liquibase liquibase = null;
        try {
            liquibase = createLiquibaseInstance(getDatabaseConfiguration());
            liquibase.update(context);
        } catch (Exception e) {
            throw new FlowableException("Error updating " + context + " engine tables", e);
        } finally {
            closeDatabase(liquibase);
        }
        return null;
    }

    @Override
    public void schemaCheckVersion() {
        Liquibase liquibase = null;
        try {
            liquibase = createLiquibaseInstance(getDatabaseConfiguration());
            liquibase.validate();
        } catch (Exception e) {
            throw new FlowableException("Error validating " + context + " engine schema", e);
        } finally {
            closeDatabase(liquibase);
        }
    }

    protected abstract LiquibaseDatabaseConfiguration getDatabaseConfiguration();

    protected Liquibase createLiquibaseInstance(LiquibaseDatabaseConfiguration databaseConfiguration) throws SQLException {
        Connection jdbcConnection = null;
        boolean closeConnection = false;
        try {
            CommandContext commandContext = Context.getCommandContext();
            if (commandContext == null) {
                jdbcConnection = databaseConfiguration.getDataSource().getConnection();
                closeConnection = true;
            } else {
                jdbcConnection = commandContext.getSession(DbSqlSession.class).getSqlSession().getConnection();
            }

            // A commit is needed here, because one of the things that Liquibase does when acquiring its lock
            // is doing a rollback, which removes all changes done so far.
            // For most databases, this is not a problem as DDL statements are not transactional.
            // However for some (e.g. sql server), this would remove all previous statements, which is not wanted,
            // hence the extra commit here.
            if (!jdbcConnection.getAutoCommit()) {
                jdbcConnection.commit();
            }

            DatabaseConnection connection = new JdbcConnection(jdbcConnection);
            Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);
            database.setDatabaseChangeLogTableName(changeLogPrefix + database.getDatabaseChangeLogTableName());
            database.setDatabaseChangeLogLockTableName(changeLogPrefix + database.getDatabaseChangeLogLockTableName());

            String databaseSchema = databaseConfiguration.getDatabaseSchema();
            if (StringUtils.isNotEmpty(databaseSchema)) {
                database.setDefaultSchemaName(databaseSchema);
                database.setLiquibaseSchemaName(databaseSchema);
            }

            String databaseCatalog = databaseConfiguration.getDatabaseCatalog();
            if (StringUtils.isNotEmpty(databaseCatalog)) {
                database.setDefaultCatalogName(databaseCatalog);
                database.setLiquibaseCatalogName(databaseCatalog);
            }

            return new Liquibase(changeLogFile, new ClassLoaderResourceAccessor(), database);

        } catch (Exception e) {
            // We only close the connection if an exception occurred, otherwise the Liquibase instance cannot be used
            if (jdbcConnection != null && closeConnection) {
                jdbcConnection.close();
            }
            throw new FlowableException("Error creating " + context + "liquibase instance", e);
        }
    }

    protected void closeDatabase(Liquibase liquibase) {
        if (liquibase != null) {
            Database database = liquibase.getDatabase();
            if (database != null) {
                // do not close the shared connection if a command context is currently active
                if (Context.getCommandContext() == null) {
                    try {
                        database.close();
                    } catch (DatabaseException e) {
                        logger.warn("Error closing database for " + context, e);
                    }
                }
            }
        }
    }

}

@@ -7006,16 +7006,17 @@
 text + %22
+ 
 liquibas

Add space in exception in LiquibaseBasedSchemaManager for better readability