
com.sap.cloud.sdk.frameworks.liquibase.LiquibaseProvider Maven / Gradle / Ivy
Show all versions of liquibase-javaee Show documentation
/*
* Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved.
*/
package com.sap.cloud.sdk.frameworks.liquibase;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.sql.DataSource;
import org.slf4j.Logger;
import com.sap.cloud.sdk.cloudplatform.exception.ShouldNotHappenException;
import com.sap.cloud.sdk.cloudplatform.logging.CloudLoggerFactory;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.LiquibaseException;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.CompositeResourceAccessor;
import liquibase.resource.FileSystemResourceAccessor;
import liquibase.resource.ResourceAccessor;
/**
* Updates the database to the state given by the liquibase configuration file.
*
* The default liquibase configuration file is located at {@code liquibase/changelog_master.xml}.
*/
@Singleton
@Startup
public class LiquibaseProvider
{
public static final String DB_MIGRATE_UP_TO_CHANGESET = "db.migrate.changeset";
private static final Logger logger = CloudLoggerFactory.getLogger(LiquibaseProvider.class);
@PersistenceContext
private EntityManager em;
@Resource
private DataSource dataSource;
/**
* Updates the database given by the {@code dataSource} to the current state represented by the liquibase
* configuration file in {@code liquibase/changelog_master.xml}.
*/
@TransactionAttribute( TransactionAttributeType.NEVER )
@PostConstruct
public void createDatabase()
{
try( final Connection connection = dataSource.getConnection() ) {
final DatabaseMetaData metaData = connection.getMetaData();
logger.info(
"DB driver "
+ metaData.getDriverName()
+ " "
+ metaData.getDriverVersion()
+ ", major "
+ metaData.getDriverMajorVersion()
+ ", minor "
+ metaData.getDriverMinorVersion());
logger.info(
"DB version "
+ metaData.getDatabaseProductVersion()
+ ", major "
+ metaData.getDatabaseMajorVersion()
+ ", minor "
+ metaData.getDatabaseMinorVersion());
logger.info(
"JDBC version major " + metaData.getJDBCMajorVersion() + ", minor " + metaData.getJDBCMinorVersion());
logger.info("Starting Liquibase migration");
final Thread currentThread = Thread.currentThread();
final ClassLoader contextClassLoader = currentThread.getContextClassLoader();
final ResourceAccessor threadClFO = new ClassLoaderResourceAccessor(contextClassLoader);
final ResourceAccessor clFO = new ClassLoaderResourceAccessor();
final ResourceAccessor fsFO = new FileSystemResourceAccessor();
final Database database =
DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
try {
final CustomLiquibase liquibase =
new CustomLiquibase(
"liquibase/changelog_master.xml",
new CompositeResourceAccessor(clFO, fsFO, threadClFO),
database);
liquibase.setUpperBound(getUpperBound());
final String sqlString = simulateMigration(liquibase);
logger.info("Will execute:\n " + sqlString);
// Do migration
liquibase.update("");
}
finally {
logger.info("Finished Liquibase routine.");
try {
if( database != null ) {
database.close();
}
}
catch( final Exception e ) {
logger.warn("Failed to close connection.", e);
}
}
}
catch( final SQLException | LiquibaseException e ) {
throw new ShouldNotHappenException(e);
}
}
private String simulateMigration( final CustomLiquibase liquibase )
throws LiquibaseException
{
final StringWriter sqlToExecute = new StringWriter();
liquibase.update("", sqlToExecute);
return sqlToExecute.toString();
}
private int getUpperBound()
{
final String propVal = System.getProperty(DB_MIGRATE_UP_TO_CHANGESET);
if( propVal == null ) {
throw new IllegalArgumentException("System property " + DB_MIGRATE_UP_TO_CHANGESET + " is not defined.");
}
try {
return Integer.parseInt(propVal);
}
catch( final NumberFormatException e ) {
throw new IllegalArgumentException(
"Failed to parse the value " + propVal + " of system property " + DB_MIGRATE_UP_TO_CHANGESET + ".",
e);
}
}
}