-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
ChangeLogIterator.java
193 lines (171 loc) · 8.45 KB
/
ChangeLogIterator.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
package liquibase.changelog;
import liquibase.*;
import liquibase.changelog.filter.ChangeSetFilter;
import liquibase.changelog.filter.ChangeSetFilterResult;
import liquibase.changelog.visitor.ChangeSetVisitor;
import liquibase.changelog.visitor.SkippedChangeSetVisitor;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.exception.LiquibaseException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.exception.ValidationErrors;
import liquibase.executor.Executor;
import liquibase.executor.ExecutorService;
import liquibase.logging.Logger;
import liquibase.logging.core.BufferedLogService;
import liquibase.logging.core.CompositeLogService;
import liquibase.util.StringUtil;
import java.util.*;
import static java.util.ResourceBundle.getBundle;
public class ChangeLogIterator {
private DatabaseChangeLog databaseChangeLog;
private List<ChangeSetFilter> changeSetFilters;
private static ResourceBundle coreBundle = getBundle("liquibase/i18n/liquibase-core");
protected static final String MSG_COULD_NOT_FIND_EXECUTOR = coreBundle.getString("no.executor.found");
private Set<String> seenChangeSets = new HashSet<>();
public ChangeLogIterator(DatabaseChangeLog databaseChangeLog, ChangeSetFilter... changeSetFilters) {
this.databaseChangeLog = databaseChangeLog;
this.changeSetFilters = Arrays.asList(changeSetFilters);
}
public ChangeLogIterator(List<RanChangeSet> changeSetList, DatabaseChangeLog changeLog, ChangeSetFilter... changeSetFilters) {
final List<ChangeSet> changeSets = new ArrayList<>();
for (RanChangeSet ranChangeSet : changeSetList) {
ChangeSet changeSet = changeLog.getChangeSet(ranChangeSet);
if (changeSet != null) {
changeSet.setFilePath(DatabaseChangeLog.normalizePath(ranChangeSet.getChangeLog()));
changeSets.add(changeSet);
}
}
this.databaseChangeLog = (new DatabaseChangeLog() {
@Override
public List<ChangeSet> getChangeSets() {
return changeSets;
}
// Prevent NPE (CORE-3231)
@Override
public String toString() {
return "";
}
});
this.databaseChangeLog.setChangeLogId(changeLog.getChangeLogId());
this.changeSetFilters = Arrays.asList(changeSetFilters);
}
public void run(ChangeSetVisitor visitor, RuntimeEnvironment env) throws LiquibaseException {
Logger log = Scope.getCurrentScope().getLog(getClass());
databaseChangeLog.setRuntimeEnvironment(env);
try {
Scope.child(Scope.Attr.databaseChangeLog, databaseChangeLog, new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
List<ChangeSet> changeSetList = new ArrayList<>(databaseChangeLog.getChangeSets());
if (visitor.getDirection().equals(ChangeSetVisitor.Direction.REVERSE)) {
Collections.reverse(changeSetList);
}
for (ChangeSet changeSet : changeSetList) {
boolean shouldVisit = true;
Set<ChangeSetFilterResult> reasonsAccepted = new HashSet<>();
Set<ChangeSetFilterResult> reasonsDenied = new HashSet<>();
if (changeSetFilters != null) {
for (ChangeSetFilter filter : changeSetFilters) {
ChangeSetFilterResult acceptsResult = filter.accepts(changeSet);
if (acceptsResult.isAccepted()) {
reasonsAccepted.add(acceptsResult);
} else {
shouldVisit = false;
reasonsDenied.add(acceptsResult);
break;
}
}
}
boolean finalShouldVisit = shouldVisit;
BufferedLogService bufferLog = new BufferedLogService();
CompositeLogService compositeLogService = new CompositeLogService(true, bufferLog);
Scope.child(Scope.Attr.changeSet.name(), changeSet, () -> {
if (finalShouldVisit && !alreadySaw(changeSet)) {
//
// Go validate any change sets with an Executor
//
validateChangeSetExecutor(changeSet, env);
//
// Execute the visit call in its own scope with a new
// CompositeLogService and BufferLogService in order
// to capture the logging for just this change set. The
// log is sent to Hub if available
//
Map<String, Object> values = new HashMap<>();
values.put(Scope.Attr.logService.name(), compositeLogService);
values.put(BufferedLogService.class.getName(), bufferLog);
Scope.child(values, () -> {
visitor.visit(changeSet, databaseChangeLog, env.getTargetDatabase(), reasonsAccepted);
});
markSeen(changeSet);
} else {
if (visitor instanceof SkippedChangeSetVisitor) {
((SkippedChangeSetVisitor) visitor).skipped(changeSet, databaseChangeLog, env.getTargetDatabase(), reasonsDenied);
}
}
});
}
}
});
} catch (Exception e) {
throw new LiquibaseException(e);
} finally {
databaseChangeLog.setRuntimeEnvironment(null);
}
}
//
// Make sure that any change set which has a runWith=<executor> setting
// has a valid Executor, and that the changes in the change set
// are eligible for execution by this Executor
//
private void validateChangeSetExecutor(ChangeSet changeSet, RuntimeEnvironment env) throws LiquibaseException {
if (changeSet.getRunWith() == null) {
return;
}
String executorName = ChangeSet.lookupExecutor(changeSet.getRunWith());
Executor executor;
try {
executor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(executorName, env.getTargetDatabase());
}
catch (UnexpectedLiquibaseException ule) {
String message = String.format(MSG_COULD_NOT_FIND_EXECUTOR, executorName, changeSet.toString());
Scope.getCurrentScope().getLog(getClass()).severe(message);
throw new LiquibaseException(message);
}
//
// ASSERT: the Executor is valid
// allow the Executor to make changes to the object model
// if needed
//
executor.modifyChangeSet(changeSet);
ValidationErrors errors = executor.validate(changeSet);
if (errors.hasErrors()) {
String message = errors.toString();
Scope.getCurrentScope().getLog(getClass()).severe(message);
throw new LiquibaseException(message);
}
}
protected void markSeen(ChangeSet changeSet) {
if (changeSet.key == null) {
changeSet.key = createKey(changeSet);
}
seenChangeSets.add(changeSet.key);
}
protected String createKey(ChangeSet changeSet) {
Labels labels = changeSet.getLabels();
ContextExpression contexts = changeSet.getContexts();
return changeSet.toString(true)
+ ":" + (labels == null ? null : labels.toString())
+ ":" + (contexts == null ? null : contexts.toString())
+ ":" + StringUtil.join(changeSet.getDbmsSet(), ",");
}
protected boolean alreadySaw(ChangeSet changeSet) {
if (changeSet.key == null) {
changeSet.key = createKey(changeSet);
}
return seenChangeSets.contains(changeSet.key);
}
public List<ChangeSetFilter> getChangeSetFilters() {
return Collections.unmodifiableList(changeSetFilters);
}
}