Java tutorial
/* 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); } } } } } }