Skip to content

Commit eba776e

Browse files
authored
Merge pull request #12 from binlecode/issue/011-log-changeSet-exception
Issue/011 log change set exception
2 parents dbeb543 + e6a24b9 commit eba776e

File tree

6 files changed

+85
-22
lines changed

6 files changed

+85
-22
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,22 @@ class BootStrap {
101101
}
102102
```
103103

104+
## CHANGE LOG
105+
106+
#### v 0.9.1
107+
- issue-11: change entry log can intercept and save exception during change set invocation, and then bubble back to main execution flow
108+
109+
#### v 0.9
110+
- issue-9: simplify hosting app to save the manual line adding in host app bootstrap.groovy
111+
112+
#### v 0.8
113+
- issue-4: add run-count support in changeEntry persistence for those repeatable changeSets
114+
115+
#### v 0.7 and under
116+
- issue-3: add loop/lock-retry ability to application start-up
117+
118+
119+
104120
## CONTRIBUTORS
105121

106122

mongogee/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ buildscript {
88
}
99
}
1010

11-
version "0.9"
11+
version "0.9.1"
1212
group "org.grails.plugins"
1313

1414
apply plugin:"eclipse"

mongogee/grails-app/domain/grails/plugin/mongogee/ChangeEntryLog.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,13 @@ class ChangeEntryLog {
1919
String author
2020
String host = 'localhost'
2121

22+
String status
23+
String message
24+
2225
static constraints = {
2326
author nullable: true
27+
status nullable: true, inList: ['success', 'fail']
28+
message nullable: true
2429
}
2530

2631
static mapWith = 'mongo'

mongogee/grails-app/services/grails/plugin/mongogee/MongogeeService.groovy

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -135,45 +135,68 @@ class MongogeeService {
135135
}
136136
}
137137

138+
/**
139+
* Apply change with given changeSet method and changeLog object.
140+
*
141+
* @param changeSetMethod {@link java.lang.reflect.Method} instance whose definition is {@link ChangeSet} annotated
142+
* @param changeLogInstance changeLog instance whose definition is {@link ChangeLog} annotated
143+
* @param changeAgent {@link ChangeAgent} instance
144+
*/
138145
protected applyChangeSet(Method changeSetMethod, changeLogInstance, ChangeAgent changeAgent) {
139146
ChangeEntry changeEntry = buildChangeEntry(changeSetMethod)
140147
if (!changeEntry) {
141-
return // return from current iteration
148+
return
142149
}
143150

144151
ChangeEntry existingChangeEntry = ChangeEntry.findByChangeSetId(changeEntry.changeSetId)
145152

146-
//todo: try-catch the invocation so the error/exp msg can be saved to changeEntryLog domain record
147-
148153
if (!existingChangeEntry) {
149154
log.info "applying new changeSet: $changeEntry"
150-
invokeChangeSetMethod(changeSetMethod, changeLogInstance)
151-
152-
ChangeEntry.withNewTransaction {
153-
changeEntry.save(failOnError: true, flush: true)
154-
buildChangeEntryLog(changeEntry).save(failOnError: true, flush: true)
155-
}
156-
log.info " ... changeSet applied"
155+
invokeChangeSetWithEntry(changeSetMethod, changeEntry, changeLogInstance)
157156

158157
} else if (changeAgent.isRunAlwaysChangeSet(changeSetMethod)) {
159-
log.info "reapplying changeSet: $changeEntry"
160-
invokeChangeSetMethod(changeSetMethod, changeLogInstance)
161158

162-
ChangeEntry.withNewTransaction {
163-
if (changeEntry.author) {
164-
existingChangeEntry.author = changeEntry.author
165-
}
166-
existingChangeEntry.runCount += 1
167-
existingChangeEntry.save(failOnError: true, flush: true)
168-
buildChangeEntryLog(existingChangeEntry).save(failOnError: true, flush: true)
159+
// merge changeEntry into existing changeEntry and update other attributes
160+
if (changeEntry.author) {
161+
existingChangeEntry.author = changeEntry.author
169162
}
170-
log.info " ... changeSet reapplied"
163+
existingChangeEntry.runCount += 1
164+
changeEntry = existingChangeEntry
165+
166+
log.info "reapplying existing changeSet: $changeEntry"
167+
invokeChangeSetWithEntry(changeSetMethod, changeEntry, changeLogInstance)
171168

172169
} else {
173170
log.info "changeSet skipped: $changeEntry"
174171
}
175172
}
176173

174+
/**
175+
* Invoke the chagneSet, save changeEntry info, and log success or failure, when there's exception, the exception
176+
* is saved to change entry log, and then bubbled up to main execution flow.
177+
*/
178+
protected invokeChangeSetWithEntry(Method changeSetMethod, ChangeEntry changeEntry, changeLogInstance) {
179+
try {
180+
invokeChangeSetMethod(changeSetMethod, changeLogInstance)
181+
log.info "changeSet invoked"
182+
183+
ChangeEntry.withNewTransaction {
184+
changeEntry.save(failOnError: true, flush: true)
185+
def changeEntryLog = buildChangeEntryLog(changeEntry).save(failOnError: true)
186+
log.debug "changeEntry saved [id: ${changeEntry.id}], and logged [id: ${changeEntryLog.id}]"
187+
}
188+
} catch (ex) {
189+
log.error "changeSet invocation error: ${ex.message ?: ex.toString().take(255)}"
190+
// save error information to change entry log
191+
ChangeEntryLog.withNewTransaction {
192+
def changeEntryLog = buildChangeEntryErrorLog(changeEntry, ex).save(failOnError: true)
193+
log.debug "changeSet invocation error logged [id: ${changeEntryLog.id}]"
194+
}
195+
// bubble exp to main flow
196+
throw ex
197+
}
198+
}
199+
177200
/**
178201
* Invoke changeSet method based on method argument specification.
179202
*/
@@ -202,6 +225,13 @@ class MongogeeService {
202225
}
203226
}
204227

228+
/**
229+
* Builds a change entry object from the input annotated {@link ChangeSet} method.
230+
* If the input method is not valid, return null.
231+
*
232+
* @param changeSetMethod {@link ChangeSet} annotated {@link java.lang.reflect.Method} object
233+
* @return {@link ChangeEntry} object, or null
234+
*/
205235
protected ChangeEntry buildChangeEntry(Method changeSetMethod) {
206236
if (changeSetMethod.isAnnotationPresent(ChangeSet.class)) {
207237
ChangeSet annotation = changeSetMethod.getAnnotation(ChangeSet.class)
@@ -227,4 +257,11 @@ class MongogeeService {
227257
return cel
228258
}
229259

260+
protected ChangeEntryLog buildChangeEntryErrorLog(ChangeEntry changeEntry, Exception exp) {
261+
ChangeEntryLog cel = buildChangeEntryLog(changeEntry)
262+
cel.status = 'fail'
263+
cel.message = exp.message ?: exp.toString().take(255)
264+
return cel
265+
}
266+
230267
}

test-g3-mongodb/grails-app/conf/application.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ environments:
139139
mongogee:
140140
changeLogsScanPackage: 'test.g3.mongodb.datamigration'
141141
changeEnabled: true # default to true
142-
continueWithError: false # default to false
142+
continueWithError: true # default to false
143143
# lockingRetryEnabled: false # default to true
144144
lockingRetryIntervalMillis: 3000 # default to 5s
145145
lockingRetryMax: 3 # default to 120, aka 10min

test-g3-mongodb/src/main/groovy/test/g3/mongodb/datamigration/Dm001.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,9 @@ class Dm001 {
2929
Document.collection.insert(name: "document at ${new Date()}")
3030
}
3131

32+
@ChangeSet(order = '003', id = '001.003.loadDocumentDataWithException', author = 'test-mongogee-app', runAlways = true)
33+
def loadDocumentDataWithException() {
34+
throw new RuntimeException('Test exception thrown from changeSet: 001.003.loadDocumentDataWithException')
35+
}
36+
3237
}

0 commit comments

Comments
 (0)