0
# Exception Handling
1
2
Groovy Contracts provides specialized exception classes for different types of contract violations and utilities for tracking multiple violations during contract evaluation.
3
4
## Exception Hierarchy
5
6
### AssertionViolation (Abstract Base)
7
8
```groovy { .api }
9
abstract class AssertionViolation extends AssertionError {
10
protected AssertionViolation()
11
protected AssertionViolation(Object o)
12
protected AssertionViolation(boolean b)
13
protected AssertionViolation(char c)
14
protected AssertionViolation(int i)
15
protected AssertionViolation(long l)
16
protected AssertionViolation(float v)
17
protected AssertionViolation(double v)
18
}
19
```
20
21
Abstract base class for all contract assertion violations. Automatically registers with `ViolationTracker` upon instantiation to support chronological violation tracking.
22
23
**Features:**
24
- Extends `AssertionError` for integration with Java assertion system
25
- Automatic registration with violation tracker
26
- Protected constructors for all primitive types and Object
27
28
## Specific Violation Types
29
30
### PreconditionViolation
31
32
```groovy { .api }
33
class PreconditionViolation extends AssertionViolation {
34
PreconditionViolation()
35
PreconditionViolation(Object o)
36
PreconditionViolation(boolean b)
37
PreconditionViolation(char c)
38
PreconditionViolation(int i)
39
PreconditionViolation(long l)
40
PreconditionViolation(float v)
41
PreconditionViolation(double v)
42
}
43
```
44
45
Thrown when a `@Requires` annotation condition fails. Indicates that a method was called with invalid arguments or in an invalid state.
46
47
**Usage Example:**
48
49
```groovy
50
@Requires({ amount > 0 })
51
void withdraw(BigDecimal amount) {
52
balance -= amount
53
}
54
55
// Calling withdraw(-100) will throw PreconditionViolation
56
```
57
58
**Catching Precondition Violations:**
59
60
```groovy
61
try {
62
account.withdraw(new BigDecimal("-100"))
63
} catch (PreconditionViolation e) {
64
println "Invalid withdrawal amount: ${e.message}"
65
}
66
```
67
68
### PostconditionViolation
69
70
```groovy { .api }
71
class PostconditionViolation extends AssertionViolation {
72
PostconditionViolation()
73
PostconditionViolation(Object o)
74
PostconditionViolation(boolean b)
75
PostconditionViolation(char c)
76
PostconditionViolation(int i)
77
PostconditionViolation(long l)
78
PostconditionViolation(float v)
79
PostconditionViolation(double v)
80
}
81
```
82
83
Thrown when an `@Ensures` annotation condition fails. Indicates that a method did not fulfill its guaranteed postcondition.
84
85
**Usage Example:**
86
87
```groovy
88
@Ensures({ result > 0 })
89
int calculateAge(Date birthDate) {
90
// Bug: might return negative value for future dates
91
return (new Date().time - birthDate.time) / (365 * 24 * 60 * 60 * 1000)
92
}
93
94
// Method will throw PostconditionViolation if it returns negative age
95
```
96
97
**Catching Postcondition Violations:**
98
99
```groovy
100
try {
101
int age = person.calculateAge(futureDate)
102
} catch (PostconditionViolation e) {
103
println "Method failed to satisfy postcondition: ${e.message}"
104
}
105
```
106
107
### ClassInvariantViolation
108
109
```groovy { .api }
110
class ClassInvariantViolation extends AssertionViolation {
111
ClassInvariantViolation()
112
ClassInvariantViolation(Object o)
113
ClassInvariantViolation(boolean b)
114
ClassInvariantViolation(char c)
115
ClassInvariantViolation(int i)
116
ClassInvariantViolation(long l)
117
ClassInvariantViolation(float v)
118
ClassInvariantViolation(double v)
119
}
120
```
121
122
Thrown when an `@Invariant` annotation condition fails. Indicates that an object's state became invalid during construction or method execution.
123
124
**Usage Example:**
125
126
```groovy
127
@Invariant({ balance >= 0 })
128
class BankAccount {
129
private BigDecimal balance = BigDecimal.ZERO
130
131
void withdraw(BigDecimal amount) {
132
balance -= amount // Might violate invariant
133
}
134
}
135
136
// Creating account or calling withdraw might throw ClassInvariantViolation
137
```
138
139
**Catching Invariant Violations:**
140
141
```groovy
142
try {
143
BankAccount account = new BankAccount()
144
account.withdraw(new BigDecimal("1000"))
145
} catch (ClassInvariantViolation e) {
146
println "Object state became invalid: ${e.message}"
147
}
148
```
149
150
### CircularAssertionCallException
151
152
```groovy { .api }
153
class CircularAssertionCallException extends RuntimeException {
154
CircularAssertionCallException()
155
CircularAssertionCallException(String s)
156
CircularAssertionCallException(String s, Throwable throwable)
157
CircularAssertionCallException(Throwable throwable)
158
}
159
```
160
161
Thrown when contract evaluation results in circular method calls, preventing infinite recursion. Unlike other contract violations, this extends `RuntimeException` rather than `AssertionViolation`.
162
163
**Usage Example:**
164
165
```groovy
166
@Invariant({ isValid() })
167
class CircularExample {
168
boolean isValid() {
169
return someCondition() // If this triggers invariant check, causes circular call
170
}
171
172
void doSomething() {
173
// Method call will trigger invariant check
174
}
175
}
176
```
177
178
**Preventing Circular Calls:**
179
180
```groovy
181
@Invariant({ balance >= 0 }) // Simple field check, no method calls
182
class SafeAccount {
183
private BigDecimal balance = BigDecimal.ZERO
184
185
boolean isValid() {
186
return true // Don't call methods in contracts
187
}
188
}
189
```
190
191
## Violation Tracking
192
193
### ViolationTracker
194
195
```groovy { .api }
196
class ViolationTracker {
197
static final ThreadLocal<ViolationTracker> INSTANCE
198
199
static void init()
200
static void deinit()
201
static boolean violationsOccurred()
202
static void rethrowFirst()
203
static void rethrowLast()
204
205
void track(AssertionViolation assertionViolation)
206
boolean hasViolations()
207
AssertionViolation first()
208
AssertionViolation last()
209
}
210
```
211
212
Thread-local utility for tracking multiple contract violations in chronological order. Used internally by the contract system to manage violation reporting.
213
214
**Important**: The `INSTANCE.get()` method can return `null` if no tracker has been initialized for the current thread. Most static methods include null checking, but direct access requires manual null checking.
215
216
**Static Methods:**
217
218
**`init()`**: Initialize violation tracker for current thread
219
```groovy
220
ViolationTracker.init()
221
```
222
223
**`deinit()`**: Remove violation tracker from current thread
224
```groovy
225
ViolationTracker.deinit()
226
```
227
228
**`violationsOccurred()`**: Check if any violations occurred
229
```groovy
230
// Safe to call - includes null checking
231
if (ViolationTracker.violationsOccurred()) {
232
// Handle violations
233
}
234
```
235
236
**`rethrowFirst()`**: Rethrow the first violation that occurred
237
```groovy
238
ViolationTracker.rethrowFirst()
239
```
240
241
**`rethrowLast()`**: Rethrow the most recent violation
242
```groovy
243
ViolationTracker.rethrowLast()
244
```
245
246
**Instance Methods:**
247
248
**`track(AssertionViolation)`**: Track a violation (called automatically)
249
**`hasViolations()`**: Check if tracker has recorded violations
250
**`first()`**: Get the first violation
251
**`last()`**: Get the most recent violation
252
253
## Exception Handling Patterns
254
255
### Comprehensive Exception Handling
256
257
```groovy
258
try {
259
performContractedOperation()
260
} catch (PreconditionViolation e) {
261
// Handle invalid input or state
262
log.error("Invalid precondition: ${e.message}")
263
return errorResponse("Invalid parameters provided")
264
} catch (PostconditionViolation e) {
265
// Handle implementation bugs
266
log.error("Method failed postcondition: ${e.message}")
267
return errorResponse("Internal processing error")
268
} catch (ClassInvariantViolation e) {
269
// Handle object state corruption
270
log.error("Object invariant violated: ${e.message}")
271
return errorResponse("Object state became invalid")
272
} catch (CircularAssertionCallException e) {
273
// Handle contract design issues
274
log.error("Circular contract evaluation: ${e.message}")
275
return errorResponse("Contract evaluation error")
276
}
277
```
278
279
### Assertion-Based Exception Handling
280
281
```groovy
282
// Contracts work with Java assertion system
283
try {
284
contractedMethod()
285
} catch (AssertionError e) {
286
if (e instanceof AssertionViolation) {
287
// Handle contract violations specifically
288
handleContractViolation((AssertionViolation) e)
289
} else {
290
// Handle other assertion failures
291
handleGeneralAssertion(e)
292
}
293
}
294
```
295
296
### Production Error Handling
297
298
```groovy
299
// Disable assertions in production to avoid contract exceptions
300
// Use JVM flags: -da (disable assertions)
301
// Or package-specific: -da:com.example.contracts.*
302
303
class ProductionService {
304
@Requires({ input != null })
305
@Ensures({ result != null })
306
String processInput(String input) {
307
// In production with -da, contracts won't throw exceptions
308
// But defensive programming is still recommended
309
if (input == null) {
310
throw new IllegalArgumentException("Input cannot be null")
311
}
312
313
String result = doProcessing(input)
314
315
if (result == null) {
316
throw new IllegalStateException("Processing failed")
317
}
318
319
return result
320
}
321
}
322
```
323
324
## Best Practices
325
326
### Exception Handling Guidelines
327
328
1. **Catch specific exceptions**: Handle each violation type appropriately
329
2. **Log contract violations**: Include detailed information for debugging
330
3. **Use assertions for development**: Enable during testing and development
331
4. **Control production behavior**: Use JVM assertion flags to control contract checking
332
5. **Combine with defensive programming**: Don't rely solely on contracts for validation
333
334
### Debugging Contract Violations
335
336
1. **Enable assertion messages**: Use descriptive expressions in contracts
337
2. **Use violation tracker**: Access chronological violation information
338
3. **Test contract scenarios**: Verify both success and failure cases
339
4. **Review inheritance hierarchies**: Check combined contract behavior