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;
017
018import javax.inject.Inject;
019import javax.sql.DataSource;
020
021import liquibase.Liquibase;
022import liquibase.database.DatabaseConnection;
023import liquibase.database.jvm.JdbcConnection;
024import liquibase.exception.DatabaseException;
025import liquibase.exception.LiquibaseException;
026import liquibase.resource.ClassLoaderResourceAccessor;
027import liquibase.resource.ResourceAccessor;
028
029/**
030 * Liquibase facade for migrations execution.
031 * <p>
032 * Usage:
033 * <pre>
034 * &#64;ImportService
035 * private DatabaseService database;
036 *
037 * ...
038 *
039 * database.registerMigration(new DatabaseMigration() {
040 *     &#64;Override
041 *     public void execute(DataSource dataSource) {
042 *         new LiquibaseFacade(datasource).migrate("/migrations/migration01.xml");
043 *     }
044 * }
045 * </pre>
046 * </p>
047 */
048public class LiquibaseFacade {
049    private static final String BEFORE_LAST_UPDATE = "BEFORE_LAST_UPDATE";
050
051    private final DataSource dataSource;
052
053    @Inject
054    public LiquibaseFacade(DataSource dataSource) {
055        this.dataSource = dataSource;
056    }
057
058    /**
059     * Migrate using given migration.
060     * @param changelog resource with the migration
061     */
062    public void migrate(String changelog) {
063        migrate(changelog, null);
064    }
065
066    /**
067     * Migrate using given migration.
068     * @param changelog resource with the migration
069     * @param contexts contexts, see {@link Liquibase#update(String)}.
070     */
071    public void migrate(String changelog, String contexts) {
072        ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor();
073
074        DatabaseConnection databaseConnection = null;
075        changelog = normalizeName(changelog);
076
077        try {
078            databaseConnection = new JdbcConnection(dataSource.getConnection());
079            Liquibase liquibase = new Liquibase(changelog, resourceAccessor, databaseConnection);
080            liquibase.tag(BEFORE_LAST_UPDATE);
081            liquibase.update(contexts);
082        } catch (LiquibaseException | SQLException e) {
083            throw new RuntimeException(e);
084        } finally {
085            try {
086                if (databaseConnection != null && !databaseConnection.isClosed())
087                    databaseConnection.close();
088            } catch (DatabaseException e) {
089                throw new RuntimeException(e);
090            }
091        }
092    }
093
094    /**
095     * Rollbacks last update.
096     * @param changelog resource with the migration
097     */
098    public void rollbackLastUpdate(String changelog) {
099        rollback(changelog, null, BEFORE_LAST_UPDATE);
100    }
101
102    /**
103     * Rollbacks last update.
104     * @param changelog resource with the migration
105     * @param contexts contexts, see {@link Liquibase#update(String)}.
106     */
107    public void rollbackLastUpdate(String changelog, String contexts) {
108        rollback(changelog, contexts, BEFORE_LAST_UPDATE) ;
109    }
110
111    /**
112     * Rollbacks given changelog to given tag.
113     * @param changelog resource with the migration
114     * @param contexts contexts, see {@link Liquibase#update(String)}.
115     * @param tag tag to rollback to
116     */
117    private void rollback(String changelog, String contexts, String tag) {
118        changelog = normalizeName(changelog);
119
120        ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor();
121
122        DatabaseConnection databaseConnection = null;
123
124        try {
125            databaseConnection = new JdbcConnection(dataSource.getConnection());
126            Liquibase liquibase = new Liquibase(changelog, resourceAccessor, databaseConnection);
127            liquibase.rollback(tag, contexts);
128        } catch (LiquibaseException | SQLException e) {
129            throw new RuntimeException(e);
130        } finally {
131            try {
132                if (databaseConnection != null && !databaseConnection.isClosed()) {
133                    databaseConnection.close();
134                }
135            } catch (DatabaseException e) {
136                throw new RuntimeException(e);
137            }
138        }
139    }
140
141    public void dropAll() {
142        ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor();
143
144        DatabaseConnection databaseConnection = null;
145
146        try {
147            databaseConnection = new JdbcConnection(dataSource.getConnection());
148            Liquibase liquibase = new Liquibase(null, resourceAccessor, databaseConnection);
149            liquibase.dropAll();
150        } catch (LiquibaseException | SQLException e) {
151            throw new RuntimeException(e);
152        } finally {
153            try {
154                if (databaseConnection != null && !databaseConnection.isClosed())
155                    databaseConnection.close();
156            } catch (DatabaseException e) {
157                throw new RuntimeException(e);
158            }
159        }
160    }
161
162    private String normalizeName(String changelog) {
163        if (changelog.startsWith("/") ){
164            changelog = changelog.substring(1);
165        }
166        return changelog;
167    }
168}