001/* 002 * Copyright 2011 Atteo. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License 010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 011 * or implied. See the License for the specific language governing permissions and limitations under 012 * the License. 013 */ 014package org.atteo.moonshine.liquibase; 015 016import java.sql.SQLException; 017import java.util.Map; 018import java.util.Map.Entry; 019 020import javax.inject.Inject; 021import javax.sql.DataSource; 022 023import liquibase.Liquibase; 024import liquibase.database.DatabaseConnection; 025import liquibase.database.jvm.JdbcConnection; 026import liquibase.exception.DatabaseException; 027import liquibase.exception.LiquibaseException; 028import liquibase.resource.ClassLoaderResourceAccessor; 029import liquibase.resource.ResourceAccessor; 030 031/** 032 * Liquibase facade for migrations execution. 033 * <p> 034 * Usage: 035 * <pre> 036 * @ImportService 037 * private DatabaseService database; 038 * 039 * ... 040 * 041 * database.registerMigration(new DatabaseMigration() { 042 * @Override 043 * public void execute(DataSource dataSource) { 044 * new LiquibaseFacade(datasource).migrate("/migrations/migration01.xml"); 045 * } 046 * } 047 * </pre> 048 * </p> 049 */ 050public class LiquibaseFacade { 051 private static final String BEFORE_LAST_UPDATE = "BEFORE_LAST_UPDATE"; 052 053 private final DataSource dataSource; 054 055 @Inject 056 public LiquibaseFacade(DataSource dataSource) { 057 this.dataSource = dataSource; 058 } 059 060 /** 061 * Migrate using given migration. 062 * @param changelog resource with the migration 063 */ 064 public void migrate(String changelog) { 065 migrate(changelog, null); 066 } 067 068 /** 069 * Migrate using given migration. 070 * @param changelog resource with the migration 071 * @param contexts contexts, see {@link Liquibase#update(String)}. 072 */ 073 public void migrate(String changelog, String contexts) { 074 migrate(changelog, contexts, null); 075 } 076 077 /** 078 * Migrate using given migration. 079 * @param changelog resource with the migration 080 * @param contexts contexts, see {@link Liquibase#update(String)}. 081 * @param changelogParameters changelog parameters, can be null 082 */ 083 public void migrate(String changelog, String contexts, Map<String, Object> changelogParameters) { 084 ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(); 085 086 DatabaseConnection databaseConnection = null; 087 changelog = normalizeName(changelog); 088 089 try { 090 databaseConnection = new JdbcConnection(dataSource.getConnection()); 091 Liquibase liquibase = new Liquibase(changelog, resourceAccessor, databaseConnection); 092 liquibase.tag(BEFORE_LAST_UPDATE); 093 094 if (changelogParameters != null) { 095 for (Entry<String, Object> entry : changelogParameters.entrySet()) { 096 liquibase.setChangeLogParameter(entry.getKey(), entry.getValue()); 097 } 098 } 099 100 liquibase.update(contexts); 101 } catch (LiquibaseException | SQLException e) { 102 throw new RuntimeException(e); 103 } finally { 104 try { 105 if (databaseConnection != null && !databaseConnection.isClosed()) 106 databaseConnection.close(); 107 } catch (DatabaseException e) { 108 throw new RuntimeException(e); 109 } 110 } 111 } 112 113 /** 114 * Rollbacks last update. 115 * @param changelog resource with the migration 116 */ 117 public void rollbackLastUpdate(String changelog) { 118 rollbackLastUpdate(changelog, null); 119 } 120 121 /** 122 * Rollbacks last update. 123 * @param changelog resource with the migration 124 * @param contexts contexts, see {@link Liquibase#update(String)}. 125 */ 126 public void rollbackLastUpdate(String changelog, String contexts) { 127 rollbackLastUpdate(changelog, contexts, null) ; 128 } 129 130 /** 131 * Rollbacks given changelog to given tag. 132 * @param changelog resource with the migration 133 * @param contexts contexts, see {@link Liquibase#update(String)}. 134 * @param changelogParameters changelogParameters 135 */ 136 public void rollbackLastUpdate(String changelog, String contexts, Map<String, Object> changelogParameters) { 137 rollback(changelog, contexts, changelogParameters, BEFORE_LAST_UPDATE); 138 } 139 140 /** 141 * Rollbacks given changelog to given tag. 142 * @param changelog resource with the migration 143 * @param contexts contexts, see {@link Liquibase#update(String)}. 144 * @param changelogParameters changelogParameters 145 * @param tag tag to rollback to 146 */ 147 private void rollback(String changelog, String contexts, Map<String, Object> changelogParameters, String tag) { 148 changelog = normalizeName(changelog); 149 150 ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(); 151 152 DatabaseConnection databaseConnection = null; 153 154 try { 155 databaseConnection = new JdbcConnection(dataSource.getConnection()); 156 Liquibase liquibase = new Liquibase(changelog, resourceAccessor, databaseConnection); 157 158 if (changelogParameters != null) { 159 for (Entry<String, Object> entry : changelogParameters.entrySet()) { 160 liquibase.setChangeLogParameter(entry.getKey(), entry.getValue()); 161 } 162 } 163 164 liquibase.rollback(tag, contexts); 165 } catch (LiquibaseException | SQLException e) { 166 throw new RuntimeException(e); 167 } finally { 168 try { 169 if (databaseConnection != null && !databaseConnection.isClosed()) { 170 databaseConnection.close(); 171 } 172 } catch (DatabaseException e) { 173 throw new RuntimeException(e); 174 } 175 } 176 } 177 178 public void dropAll() { 179 ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(); 180 181 DatabaseConnection databaseConnection = null; 182 183 try { 184 databaseConnection = new JdbcConnection(dataSource.getConnection()); 185 Liquibase liquibase = new Liquibase(null, resourceAccessor, databaseConnection); 186 liquibase.dropAll(); 187 } catch (LiquibaseException | SQLException e) { 188 throw new RuntimeException(e); 189 } finally { 190 try { 191 if (databaseConnection != null && !databaseConnection.isClosed()) 192 databaseConnection.close(); 193 } catch (DatabaseException e) { 194 throw new RuntimeException(e); 195 } 196 } 197 } 198 199 private String normalizeName(String changelog) { 200 if (changelog.startsWith("/") ){ 201 changelog = changelog.substring(1); 202 } 203 return changelog; 204 } 205}