001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.liquibase; 018 019import java.util.function.Supplier; 020 021import javax.annotation.PostConstruct; 022import javax.persistence.EntityManagerFactory; 023import javax.sql.DataSource; 024 025import liquibase.change.DatabaseChange; 026import liquibase.integration.spring.SpringLiquibase; 027 028import org.springframework.beans.factory.ObjectProvider; 029import org.springframework.boot.autoconfigure.AutoConfigureAfter; 030import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 031import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 032import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 033import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 034import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 035import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor; 036import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; 037import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 038import org.springframework.boot.autoconfigure.jdbc.JdbcOperationsDependsOnPostProcessor; 039import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; 040import org.springframework.boot.context.properties.EnableConfigurationProperties; 041import org.springframework.boot.jdbc.DataSourceBuilder; 042import org.springframework.context.annotation.Bean; 043import org.springframework.context.annotation.Configuration; 044import org.springframework.context.annotation.Import; 045import org.springframework.core.io.Resource; 046import org.springframework.core.io.ResourceLoader; 047import org.springframework.jdbc.core.JdbcOperations; 048import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; 049import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; 050import org.springframework.util.Assert; 051 052/** 053 * {@link EnableAutoConfiguration Auto-configuration} for Liquibase. 054 * 055 * @author Marcel Overdijk 056 * @author Dave Syer 057 * @author Phillip Webb 058 * @author EddĂș MelĂ©ndez 059 * @author Andy Wilkinson 060 * @author Dominic Gunn 061 * @since 1.1.0 062 */ 063@Configuration 064@ConditionalOnClass({ SpringLiquibase.class, DatabaseChange.class }) 065@ConditionalOnBean(DataSource.class) 066@ConditionalOnProperty(prefix = "spring.liquibase", name = "enabled", matchIfMissing = true) 067@AutoConfigureAfter({ DataSourceAutoConfiguration.class, 068 HibernateJpaAutoConfiguration.class }) 069public class LiquibaseAutoConfiguration { 070 071 @Bean 072 public LiquibaseSchemaManagementProvider liquibaseDefaultDdlModeProvider( 073 ObjectProvider<SpringLiquibase> liquibases) { 074 return new LiquibaseSchemaManagementProvider(liquibases); 075 } 076 077 @Configuration 078 @ConditionalOnMissingBean(SpringLiquibase.class) 079 @EnableConfigurationProperties({ DataSourceProperties.class, 080 LiquibaseProperties.class }) 081 @Import(LiquibaseJpaDependencyConfiguration.class) 082 public static class LiquibaseConfiguration { 083 084 private final LiquibaseProperties properties; 085 086 private final DataSourceProperties dataSourceProperties; 087 088 private final ResourceLoader resourceLoader; 089 090 private final DataSource dataSource; 091 092 private final DataSource liquibaseDataSource; 093 094 public LiquibaseConfiguration(LiquibaseProperties properties, 095 DataSourceProperties dataSourceProperties, ResourceLoader resourceLoader, 096 ObjectProvider<DataSource> dataSource, 097 @LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource) { 098 this.properties = properties; 099 this.dataSourceProperties = dataSourceProperties; 100 this.resourceLoader = resourceLoader; 101 this.dataSource = dataSource.getIfUnique(); 102 this.liquibaseDataSource = liquibaseDataSource.getIfAvailable(); 103 } 104 105 @PostConstruct 106 public void checkChangelogExists() { 107 if (this.properties.isCheckChangeLogLocation()) { 108 Resource resource = this.resourceLoader 109 .getResource(this.properties.getChangeLog()); 110 Assert.state(resource.exists(), 111 () -> "Cannot find changelog location: " + resource 112 + " (please add changelog or check your Liquibase " 113 + "configuration)"); 114 } 115 } 116 117 @Bean 118 public SpringLiquibase liquibase() { 119 SpringLiquibase liquibase = createSpringLiquibase(); 120 liquibase.setChangeLog(this.properties.getChangeLog()); 121 liquibase.setContexts(this.properties.getContexts()); 122 liquibase.setDefaultSchema(this.properties.getDefaultSchema()); 123 liquibase.setLiquibaseSchema(this.properties.getLiquibaseSchema()); 124 liquibase.setLiquibaseTablespace(this.properties.getLiquibaseTablespace()); 125 liquibase.setDatabaseChangeLogTable( 126 this.properties.getDatabaseChangeLogTable()); 127 liquibase.setDatabaseChangeLogLockTable( 128 this.properties.getDatabaseChangeLogLockTable()); 129 liquibase.setDropFirst(this.properties.isDropFirst()); 130 liquibase.setShouldRun(this.properties.isEnabled()); 131 liquibase.setLabels(this.properties.getLabels()); 132 liquibase.setChangeLogParameters(this.properties.getParameters()); 133 liquibase.setRollbackFile(this.properties.getRollbackFile()); 134 liquibase.setTestRollbackOnUpdate(this.properties.isTestRollbackOnUpdate()); 135 return liquibase; 136 } 137 138 private SpringLiquibase createSpringLiquibase() { 139 DataSource liquibaseDataSource = getDataSource(); 140 if (liquibaseDataSource != null) { 141 SpringLiquibase liquibase = new SpringLiquibase(); 142 liquibase.setDataSource(liquibaseDataSource); 143 return liquibase; 144 } 145 SpringLiquibase liquibase = new DataSourceClosingSpringLiquibase(); 146 liquibase.setDataSource(createNewDataSource()); 147 return liquibase; 148 } 149 150 private DataSource getDataSource() { 151 if (this.liquibaseDataSource != null) { 152 return this.liquibaseDataSource; 153 } 154 if (this.properties.getUrl() == null && this.properties.getUser() == null) { 155 return this.dataSource; 156 } 157 return null; 158 } 159 160 private DataSource createNewDataSource() { 161 String url = getProperty(this.properties::getUrl, 162 this.dataSourceProperties::getUrl); 163 String user = getProperty(this.properties::getUser, 164 this.dataSourceProperties::getUsername); 165 String password = getProperty(this.properties::getPassword, 166 this.dataSourceProperties::getPassword); 167 return DataSourceBuilder.create().url(url).username(user).password(password) 168 .build(); 169 } 170 171 private String getProperty(Supplier<String> property, 172 Supplier<String> defaultValue) { 173 String value = property.get(); 174 return (value != null) ? value : defaultValue.get(); 175 } 176 177 } 178 179 /** 180 * Additional configuration to ensure that {@link EntityManagerFactory} beans depend 181 * on the liquibase bean. 182 */ 183 @Configuration 184 @ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class) 185 @ConditionalOnBean(AbstractEntityManagerFactoryBean.class) 186 protected static class LiquibaseJpaDependencyConfiguration 187 extends EntityManagerFactoryDependsOnPostProcessor { 188 189 public LiquibaseJpaDependencyConfiguration() { 190 super("liquibase"); 191 } 192 193 } 194 195 /** 196 * Additional configuration to ensure that {@link JdbcOperations} beans depend on the 197 * liquibase bean. 198 */ 199 @Configuration 200 @ConditionalOnClass(JdbcOperations.class) 201 @ConditionalOnBean(JdbcOperations.class) 202 protected static class LiquibaseJdbcOperationsDependencyConfiguration 203 extends JdbcOperationsDependsOnPostProcessor { 204 205 public LiquibaseJdbcOperationsDependencyConfiguration() { 206 super("liquibase"); 207 } 208 209 } 210 211}