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

liquibase.integration.servlet.LiquibaseServletListener Maven / Gradle / Ivy

package liquibase.integration.servlet;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Enumeration;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
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.resource.ClassLoaderResourceAccessor;
import liquibase.resource.CompositeResourceAccessor;
import liquibase.resource.FileSystemResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.util.NetUtil;
import liquibase.util.StringUtils;

/**
 * Servlet listener than can be added to web.xml to allow Liquibase to run on every application server startup.
 * Using this listener allows users to know that they always have the most up to date database, although it will
 * slow down application server startup slightly.
 * See the Liquibase documentation for
 * more information.
 */
public class LiquibaseServletListener implements ServletContextListener {

    private static final String JAVA_COMP_ENV = "java:comp/env";
    private static final String LIQUIBASE_CHANGELOG = "liquibase.changelog";
    private static final String LIQUIBASE_CONTEXTS = "liquibase.contexts";
    private static final String LIQUIBASE_DATASOURCE = "liquibase.datasource";
    private static final String LIQUIBASE_HOST_EXCLUDES = "liquibase.host.excludes";
    private static final String LIQUIBASE_HOST_INCLUDES = "liquibase.host.includes";
    private static final String LIQUIBASE_ONERROR_FAIL = "liquibase.onerror.fail";
    private static final String LIQUIBASE_PARAMETER = "liquibase.parameter";
    private static final String LIQUIBASE_SCHEMA_DEFAULT = "liquibase.schema.default";

    private String changeLogFile;
    private String dataSourceName;
    private String contexts;
    private String defaultSchema;
    private String hostName;

    public String getChangeLogFile() {
        return changeLogFile;
    }

    public void setContexts(String ctxt) {
        contexts = ctxt;
    }

    public String getContexts() {
        return contexts;
    }

    public void setChangeLogFile(String changeLogFile) {
        this.changeLogFile = changeLogFile;
    }

    public String getDataSource() {
        return dataSourceName;
    }

    public String getDefaultSchema() {
        return defaultSchema;
    }

    /**
     * Sets the name of the data source.
     */
    public void setDataSource(String dataSource) {
        this.dataSourceName = dataSource;
    }

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ServletContext servletContext = servletContextEvent.getServletContext();
        try {
            this.hostName = NetUtil.getLocalHostName();
        }
        catch (Exception e) {
            servletContext.log("Cannot find hostname: " + e.getMessage());
            return;
        }

        InitialContext ic = null;
        String failOnError = null;
        try {
            ic = new InitialContext();
            failOnError = getValue(LIQUIBASE_ONERROR_FAIL, servletContext, ic);
            if (checkPreconditions(servletContext, ic)) {
                executeUpdate(servletContext, ic);
            }

        } catch (Exception e) {
            if (!"false".equals(failOnError)) {
                throw new RuntimeException(e);
            }
        } finally {
            if (ic != null) {
                try {
                    ic.close();
                }
                catch (NamingException e) {
                    // ignore
                }
            }
        }
    }

    /**
     * Checks if the update is supposed to be executed. That depends on several conditions:
     * 
    *
  1. if {@value Liquibase#SHOULD_RUN_SYSTEM_PROPERTY} is false the update will not be executed.
  2. *
  3. if {@value LiquibaseServletListener#LIQUIBASE_HOST_INCLUDES} contains the current hostname, the the update will be executed.
  4. *
  5. if {@value LiquibaseServletListener#LIQUIBASE_HOST_EXCLUDES} contains the current hostname, the the update will not be executed.
  6. *
*/ private boolean checkPreconditions(ServletContext servletContext, InitialContext ic) { String shouldRunProperty = getValue(Liquibase.SHOULD_RUN_SYSTEM_PROPERTY, servletContext, ic); if (shouldRunProperty != null && !Boolean.valueOf(shouldRunProperty)) { LogFactory.getLogger().info( "Liquibase did not run on " + hostName + " because '" + Liquibase.SHOULD_RUN_SYSTEM_PROPERTY + "' system property was set to false"); return false; } String machineIncludes = getValue(LIQUIBASE_HOST_INCLUDES, servletContext, ic); String machineExcludes = getValue(LIQUIBASE_HOST_EXCLUDES, servletContext, ic); boolean shouldRun = false; if (machineIncludes == null && machineExcludes == null) { shouldRun = true; } else if (machineIncludes != null) { for (String machine : machineIncludes.split(",")) { machine = machine.trim(); if (hostName.equalsIgnoreCase(machine)) { shouldRun = true; } } } else if (machineExcludes != null) { shouldRun = true; for (String machine : machineExcludes.split(",")) { machine = machine.trim(); if (hostName.equalsIgnoreCase(machine)) { shouldRun = false; } } } if (shouldRunProperty != null && Boolean.valueOf(shouldRunProperty)) { shouldRun = true; servletContext.log("ignoring " + LIQUIBASE_HOST_INCLUDES + " and " + LIQUIBASE_HOST_EXCLUDES + ", since " + Liquibase.SHOULD_RUN_SYSTEM_PROPERTY + "=true"); } if (!shouldRun) { servletContext.log("LiquibaseServletListener did not run due to " + LIQUIBASE_HOST_INCLUDES + " and/or " + LIQUIBASE_HOST_EXCLUDES + ""); return false; } return true; } /** * Executes the Liquibase update. */ private void executeUpdate(ServletContext servletContext, InitialContext ic) throws NamingException, SQLException, LiquibaseException { setDataSource(getValue(LIQUIBASE_DATASOURCE, servletContext, ic)); if (getDataSource() == null) { throw new RuntimeException("Cannot run Liquibase, " + LIQUIBASE_DATASOURCE + " is not set"); } setChangeLogFile(getValue(LIQUIBASE_CHANGELOG, servletContext, ic)); if (getChangeLogFile() == null) { throw new RuntimeException("Cannot run Liquibase, " + LIQUIBASE_CHANGELOG + " is not set"); } setContexts(getValue(LIQUIBASE_CONTEXTS, servletContext, ic)); this.defaultSchema = StringUtils.trimToNull(getValue(LIQUIBASE_SCHEMA_DEFAULT, servletContext, ic)); Connection connection = null; try { DataSource dataSource = (DataSource) ic.lookup(this.dataSourceName); connection = dataSource.getConnection(); Thread currentThread = Thread.currentThread(); ClassLoader contextClassLoader = currentThread.getContextClassLoader(); ResourceAccessor threadClFO = new ClassLoaderResourceAccessor(contextClassLoader); ResourceAccessor clFO = new ClassLoaderResourceAccessor(); ResourceAccessor fsFO = new FileSystemResourceAccessor(); Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection)); database.setDefaultSchemaName(getDefaultSchema()); Liquibase liquibase = new Liquibase(getChangeLogFile(), new CompositeResourceAccessor(clFO, fsFO, threadClFO), database); @SuppressWarnings("unchecked") Enumeration initParameters = servletContext.getInitParameterNames(); while (initParameters.hasMoreElements()) { String name = initParameters.nextElement().trim(); if (name.startsWith(LIQUIBASE_PARAMETER + ".")) { liquibase.setChangeLogParameter(name.substring(LIQUIBASE_PARAMETER.length()), getValue(name, servletContext, ic)); } } liquibase.update(getContexts()); } finally { if (connection != null) { connection.close(); } } } /** * Try to read the value that is stored by the given key from *
    *
  • JNDI
  • *
  • the servlet context's init parameters
  • *
  • system properties
  • *
*/ public String getValue(String key, ServletContext servletContext, InitialContext initialContext) { // Try to get value from JNDI try { Context envCtx = (Context) initialContext.lookup(JAVA_COMP_ENV); String valueFromJndi = (String) envCtx.lookup(key); return valueFromJndi; } catch (NamingException e) { // Ignore } // Return the value from the servlet context String valueFromServletContext = servletContext.getInitParameter(key); if (valueFromServletContext != null) { return valueFromServletContext; } // Otherwise: Return system property return System.getProperty(key); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy