package liquibase.integration.servlet; import liquibase.Liquibase; import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.database.jvm.JdbcConnection; 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; import javax.naming.Context; import javax.naming.InitialContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.sql.DataSource; import java.sql.Connection; import java.util.Enumeration; /** * 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 <a href="http://www.liquibase.org/manual/latest/servlet_listener_migrator.html">Liquibase documentation</a> for * more information. */ public class LiquibaseServletListener implements ServletContextListener { private String changeLogFile; private String dataSource; private String contexts; private String defaultSchema; 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 dataSource; } public void setDataSource(String dataSource) { this.dataSource = dataSource; } public void contextInitialized(ServletContextEvent servletContextEvent) { String hostName; try { hostName = NetUtil.getLocalHost().getHostName(); } catch (Exception e) { servletContextEvent.getServletContext().log("Cannot find hostname: " + e.getMessage()); return; } String shouldRunProperty = System.getProperty(Liquibase.SHOULD_RUN_SYSTEM_PROPERTY); 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; } String machineIncludes = servletContextEvent.getServletContext().getInitParameter("liquibase.host.includes"); String machineExcludes = servletContextEvent.getServletContext().getInitParameter("liquibase.host.excludes"); String failOnError = servletContextEvent.getServletContext().getInitParameter("liquibase.onerror.fail"); 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 (!shouldRun) { servletContextEvent.getServletContext().log("LiquibaseServletListener did not run due to liquibase.host.includes and/or liquibase.host.excludes"); return; } setDataSource(servletContextEvent.getServletContext().getInitParameter("liquibase.datasource")); setChangeLogFile(servletContextEvent.getServletContext().getInitParameter("liquibase.changelog")); setContexts(servletContextEvent.getServletContext().getInitParameter("liquibase.contexts")); this.defaultSchema = StringUtils.trimToNull(servletContextEvent.getServletContext().getInitParameter("liquibase.schema.default")); if (getChangeLogFile() == null) { throw new RuntimeException("Cannot run Liquibase, liquibase.changelog is not set"); } if (getDataSource() == null) { throw new RuntimeException("Cannot run Liquibase, liquibase.datasource is not set"); } try { Context ic = null; Connection connection = null; try { ic = new InitialContext(); DataSource dataSource = (DataSource) ic.lookup(this.dataSource); 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(this.defaultSchema); Liquibase liquibase = new Liquibase(getChangeLogFile(), new CompositeResourceAccessor(clFO,fsFO, threadClFO), database); Enumeration<String> initParameters = servletContextEvent.getServletContext().getInitParameterNames(); while (initParameters.hasMoreElements()) { String name = initParameters.nextElement().trim(); if (name.startsWith("liquibase.parameter.")) { liquibase.setChangeLogParameter(name.substring("liquibase.parameter".length()), servletContextEvent.getServletContext().getInitParameter(name)); } } liquibase.update(getContexts()); } finally { if (ic != null) { ic.close(); } if (connection != null) { connection.close(); } } } catch (Exception e) { if (!"false".equals(failOnError)) { throw new RuntimeException(e); } } } public void contextDestroyed(ServletContextEvent servletContextEvent) { } }