liquibase.integration.spring.SpringLiquibase Maven / Gradle / Ivy
package liquibase.integration.spring;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Vector;
import javax.sql.DataSource;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
import liquibase.logging.LogFactory;
import liquibase.logging.Logger;
import liquibase.resource.ResourceAccessor;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
/**
* A Spring-ified wrapper for Liquibase.
*
* Example Configuration:
*
*
* This Spring configuration example will cause liquibase to run
* automatically when the Spring context is initialized. It will load
* db-changelog.xml
from the classpath and apply it against
* myDataSource
.
*
*
*
* <bean id="myLiquibase"
* class="liquibase.spring.SpringLiquibase"
* >
*
* <property name="dataSource" ref="myDataSource" />
*
* <property name="changeLog" value="classpath:db-changelog.xml" />
*
* <!-- The following configuration options are optional -->
*
* <property name="executeEnabled" value="true" />
*
* <!--
* If set to true, writeSqlFileEnabled will write the generated
* SQL to a file before executing it.
* -->
* <property name="writeSqlFileEnabled" value="true" />
*
* <!--
* sqlOutputDir specifies the directory into which the SQL file
* will be written, if so configured.
* -->
* <property name="sqlOutputDir" value="c:\sql" />
*
* </bean>
*
*
*
* @author Rob Schoening
*/
public class SpringLiquibase implements InitializingBean, BeanNameAware, ResourceLoaderAware {
public class SpringResourceOpener implements ResourceAccessor {
private String parentFile;
public SpringResourceOpener(String parentFile) {
this.parentFile = parentFile;
}
public InputStream getResourceAsStream(String file) throws IOException {
try {
Resource resource = getResource(file);
return resource.getInputStream();
}
catch ( FileNotFoundException ex ) {
return null;
}
}
public Enumeration getResources(String packageName) throws IOException {
Vector tmp = new Vector();
tmp.add(getResource(packageName).getURL());
return tmp.elements();
}
public Resource getResource(String file) {
return getResourceLoader().getResource(adjustClasspath(file));
}
private String adjustClasspath(String file) {
return isClasspathPrefixPresent(parentFile) && !isClasspathPrefixPresent(file)
? ResourceLoader.CLASSPATH_URL_PREFIX + file
: file;
}
public boolean isClasspathPrefixPresent(String file) {
return file.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX);
}
public ClassLoader toClassLoader() {
return getResourceLoader().getClassLoader();
}
}
private String beanName;
private ResourceLoader resourceLoader;
private DataSource dataSource;
private Logger log = LogFactory.getLogger(SpringLiquibase.class.getName());
private String changeLog;
private String contexts;
public SpringLiquibase() {
super();
}
public String getDatabaseProductName() throws DatabaseException {
Connection connection = null;
String name = "unknown";
try {
connection = getDataSource().getConnection();
Database database =
DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(dataSource.getConnection()));
name = database.getDatabaseProductName();
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
if (connection != null) {
try {
if (!connection.getAutoCommit())
{
connection.rollback();
}
connection.close();
} catch (Exception e) {
log.warning("problem closing connection", e);
}
}
}
return name;
}
/**
* The DataSource that liquibase will use to perform the migration.
*
* @return
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* The DataSource that liquibase will use to perform the migration.
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* Returns a Resource that is able to resolve to a file or classpath resource.
*
* @return
*/
public String getChangeLog() {
return changeLog;
}
/**
* Sets a Spring Resource that is able to resolve to a file or classpath resource.
* An example might be classpath:db-changelog.xml
.
*/
public void setChangeLog(String dataModel) {
this.changeLog = dataModel;
}
public String getContexts() {
return contexts;
}
public void setContexts(String contexts) {
this.contexts = contexts;
}
/**
* Executed automatically when the bean is initialized.
*/
public void afterPropertiesSet() throws LiquibaseException {
String shouldRunProperty = System.getProperty(Liquibase.SHOULD_RUN_SYSTEM_PROPERTY);
if (shouldRunProperty != null && !Boolean.valueOf(shouldRunProperty)) {
System.out.println("Liquibase did not run because '" + Liquibase.SHOULD_RUN_SYSTEM_PROPERTY + "' system property was set to false");
return;
}
Connection c = null;
Liquibase liquibase = null;
try {
c = getDataSource().getConnection();
liquibase = createLiquibase(c);
liquibase.update(getContexts());
} catch (SQLException e) {
throw new DatabaseException(e);
} finally {
if (liquibase != null) {
liquibase.forceReleaseLocks();
}
if (c != null) {
try {
c.rollback();
c.close();
} catch (SQLException e) {
//nothing to do
}
}
}
}
protected Liquibase createLiquibase(Connection c) throws LiquibaseException {
return new Liquibase(getChangeLog(), createResourceOpener(), createDatabase(c));
}
/**
* Subclasses may override this method add change some database settings such as
* default schema before returning the database object.
* @param c
* @return a Database implementation retrieved from the {@link DatabaseFactory}.
* @throws DatabaseException
*/
protected Database createDatabase(Connection c) throws DatabaseException {
return DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(c) );
}
/**
* Create a new resourceOpener.
*/
protected SpringResourceOpener createResourceOpener() {
return new SpringResourceOpener(getChangeLog());
}
/**
* Spring sets this automatically to the instance's configured bean name.
*/
public void setBeanName(String name) {
this.beanName = name;
}
/**
* Gets the Spring-name of this instance.
*
* @return
*/
public String getBeanName() {
return beanName;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
@Override
public String toString() {
return getClass().getName()+"("+this.getResourceLoader().toString()+")";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy