-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
CDILiquibase.java
195 lines (177 loc) · 6.23 KB
/
CDILiquibase.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package liquibase.integration.cdi;
import liquibase.Contexts;
import liquibase.LabelExpression;
import liquibase.Liquibase;
import liquibase.Scope;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.integration.cdi.annotations.LiquibaseType;
import liquibase.integration.commandline.LiquibaseCommandLineConfiguration;
import liquibase.logging.Logger;
import liquibase.resource.ResourceAccessor;
import liquibase.util.LiquibaseUtil;
import liquibase.util.NetUtil;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.spi.Extension;
import javax.inject.Inject;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
/**
* A CDI wrapper for Liquibase.
* <p/>
* Example Configuration:
* <p/>
* <p/>
* This CDI configuration example will cause liquibase to run
* automatically when the CDI container is initialized. It will load
* <code>db-changelog.xml</code> from the classpath and apply it against
* <code>myDataSource</code>.
* <p/>
* Various producers methods are required to resolve the dependencies, i.e.
* <pre>
* {@code
*
* public class CDILiquibaseProducer {
*
* @literal @Produces @LiquibaseType
* public CDILiquibaseConfig createConfig() {
* CDILiquibaseConfig config = new CDILiquibaseConfig();
* config.setChangeLog("liquibase/parser/core/xml/simpleChangeLog.xml");
* return config;
* }
*
* @literal @Produces @LiquibaseType
* public DataSource createDataSource() throws SQLException {
* jdbcDataSource ds = new jdbcDataSource();
* ds.setDatabase("jdbc:hsqldb:mem:test");
* ds.setUser("sa");
* ds.setPassword("");
* return ds;
* }
*
* @literal @Produces @LiquibaseType
* public ResourceAccessor create() {
* return new ClassLoaderResourceAccessor(getClass().getClassLoader());
* }
*
* }
*
* }
* </p>
* @author Aaron Walker (http://github.com/aaronwalker)
*/
@ApplicationScoped
public class CDILiquibase implements Extension {
@Inject
@LiquibaseType
ResourceAccessor resourceAccessor;
@Inject
@LiquibaseType
private CDILiquibaseConfig config;
@Inject
@LiquibaseType
private DataSource dataSource;
private boolean initialized;
private boolean updateSuccessful;
public boolean isInitialized() {
return initialized;
}
public boolean isUpdateSuccessful() {
return updateSuccessful;
}
@PostConstruct
public void onStartup() {
try {
Logger log = Scope.getCurrentScope().getLog(getClass());
log.info("Booting Liquibase " + LiquibaseUtil.getBuildVersionInfo());
String hostName;
try {
hostName = NetUtil.getLocalHostName();
} catch (Exception e) {
log.warning("Cannot find hostname: " + e.getMessage());
log.fine("", e);
return;
}
if (!LiquibaseCommandLineConfiguration.SHOULD_RUN.getCurrentValue()) {
log.info(String.format("Liquibase did not run on %s because %s was set to false.",
hostName,
LiquibaseCommandLineConfiguration.SHOULD_RUN.getKey()
));
return;
}
if (!config.getShouldRun()) {
log.info(String.format("Liquibase did not run on %s because CDILiquibaseConfig.shouldRun was set to false.", hostName));
return;
}
initialized = true;
performUpdate();
} catch (Throwable e) {
Scope.getCurrentScope().getLog(getClass()).severe(e.getMessage(), e);
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new UnexpectedLiquibaseException(e);
}
}
private void performUpdate() throws LiquibaseException {
Connection c = null;
Liquibase liquibase = null;
try {
c = dataSource.getConnection();
liquibase = createLiquibase(c);
liquibase.update(new Contexts(config.getContexts()), new LabelExpression(config.getLabels()));
updateSuccessful = true;
} catch (SQLException e) {
throw new DatabaseException(e);
} catch (LiquibaseException ex) {
updateSuccessful = false;
throw ex;
} finally {
if ((liquibase != null) && (liquibase.getDatabase() != null)) {
liquibase.getDatabase().close();
} else if (c != null) {
try {
c.rollback();
c.close();
} catch (SQLException e) {
//nothing to do
}
}
}
}
@java.lang.SuppressWarnings("squid:S2095")
protected Liquibase createLiquibase(Connection c) throws LiquibaseException {
Liquibase liquibase = new Liquibase(config.getChangeLog(), resourceAccessor, createDatabase(c));
if (config.getParameters() != null) {
for (Map.Entry<String, String> entry : config.getParameters().entrySet()) {
liquibase.setChangeLogParameter(entry.getKey(), entry.getValue());
}
}
if (config.isDropFirst()) {
liquibase.dropAll();
}
return liquibase;
}
/**
* Subclasses may override this method add change some database settings such as
* default schema before returning the database object.
*
* @param c
* @return a Database implementation retrieved from the {@link liquibase.database.DatabaseFactory}.
* @throws DatabaseException
*/
protected Database createDatabase(Connection c) throws DatabaseException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(c));
if (config.getDefaultSchema() != null) {
database.setDefaultSchemaName(config.getDefaultSchema());
}
return database;
}
}