0
# Validation Utilities
1
2
Apache Commons Lang provides comprehensive validation utilities through the Validate class, offering 51 static methods for argument validation, precondition checking, and defensive programming. These utilities throw clear, informative exceptions with consistent error messages.
3
4
## Core Validation Class
5
6
### Validate - Comprehensive Argument Validation
7
8
The Validate class provides fluent, null-safe validation methods that throw IllegalArgumentException with descriptive messages:
9
10
```java { .api }
11
import org.apache.commons.lang3.Validate;
12
```
13
14
#### Null Validation
15
16
```java { .api }
17
// Null checking methods
18
public static <T> T notNull(T object)
19
public static <T> T notNull(T object, String message, Object... values)
20
21
// Object validation
22
public static void validIndex(Object[] array, int index)
23
public static void validIndex(Object[] array, int index, String message, Object... values)
24
public static void validIndex(Collection<?> collection, int index)
25
public static void validIndex(Collection<?> collection, int index, String message, Object... values)
26
public static void validIndex(CharSequence chars, int index)
27
public static void validIndex(CharSequence chars, int index, String message, Object... values)
28
```
29
30
**Usage Examples:**
31
```java { .api }
32
public class UserService {
33
34
public User createUser(String name, String email, Integer age) {
35
// Basic null validation
36
Validate.notNull(name, "Name cannot be null");
37
Validate.notNull(email, "Email cannot be null");
38
Validate.notNull(age, "Age cannot be null");
39
40
// Validation with formatted message
41
Validate.notNull(name, "User %s field cannot be null", "name");
42
43
return new User(name, email, age);
44
}
45
46
public String getCharacterAt(String text, int index) {
47
Validate.notNull(text, "Text cannot be null");
48
Validate.validIndex(text, index, "Invalid index %d for text of length %d", index, text.length());
49
50
return String.valueOf(text.charAt(index));
51
}
52
53
public <T> T getElementAt(List<T> list, int index) {
54
Validate.notNull(list, "List cannot be null");
55
Validate.validIndex(list, index, "Index %d is out of bounds for list of size %d", index, list.size());
56
57
return list.get(index);
58
}
59
}
60
```
61
62
#### Empty and Blank Validation
63
64
```java { .api }
65
// Collection and array validation
66
public static <T extends Collection<?>> T notEmpty(T collection)
67
public static <T extends Collection<?>> T notEmpty(T collection, String message, Object... values)
68
public static <T> T[] notEmpty(T[] array)
69
public static <T> T[] notEmpty(T[] array, String message, Object... values)
70
71
// Map validation
72
public static <T extends Map<?, ?>> T notEmpty(T map)
73
public static <T extends Map<?, ?>> T notEmpty(T map, String message, Object... values)
74
75
// String validation
76
public static <T extends CharSequence> T notEmpty(T chars)
77
public static <T extends CharSequence> T notEmpty(T chars, String message, Object... values)
78
public static <T extends CharSequence> T notBlank(T chars)
79
public static <T extends CharSequence> T notBlank(T chars, String message, Object... values)
80
```
81
82
**Usage Examples:**
83
```java { .api }
84
public class ValidationExamples {
85
86
public void processData(List<String> items, String[] categories, Map<String, Object> config) {
87
// Collection validation
88
Validate.notEmpty(items, "Items list cannot be empty");
89
Validate.notEmpty(categories, "Categories array cannot be empty");
90
Validate.notEmpty(config, "Configuration map cannot be empty");
91
92
// Process data...
93
}
94
95
public User authenticateUser(String username, String password) {
96
// String validation (notEmpty allows whitespace-only strings)
97
Validate.notEmpty(username, "Username cannot be empty");
98
Validate.notEmpty(password, "Password cannot be empty");
99
100
// Blank validation (rejects whitespace-only strings)
101
Validate.notBlank(username, "Username cannot be blank");
102
Validate.notBlank(password, "Password cannot be blank");
103
104
return authenticate(username.trim(), password);
105
}
106
107
public void saveConfiguration(Map<String, String> settings) {
108
Validate.notEmpty(settings, "Settings cannot be empty");
109
110
// Validate individual entries
111
for (Map.Entry<String, String> entry : settings.entrySet()) {
112
Validate.notBlank(entry.getKey(), "Setting key cannot be blank");
113
Validate.notBlank(entry.getValue(), "Setting value for '%s' cannot be blank", entry.getKey());
114
}
115
}
116
}
117
```
118
119
#### Numeric Range Validation
120
121
```java { .api }
122
// Inclusive range validation
123
public static void inclusiveBetween(double start, double end, double value)
124
public static void inclusiveBetween(double start, double end, double value, String message, Object... values)
125
public static void inclusiveBetween(long start, long end, long value)
126
public static void inclusiveBetween(long start, long end, long value, String message, Object... values)
127
public static <T> void inclusiveBetween(T start, T end, Comparable<T> value)
128
public static <T> void inclusiveBetween(T start, T end, Comparable<T> value, String message, Object... values)
129
130
// Exclusive range validation
131
public static void exclusiveBetween(double start, double end, double value)
132
public static void exclusiveBetween(double start, double end, double value, String message)
133
public static void exclusiveBetween(long start, long end, long value)
134
public static void exclusiveBetween(long start, long end, long value, String message)
135
public static <T> void exclusiveBetween(T start, T end, Comparable<T> value)
136
public static <T> void exclusiveBetween(T start, T end, Comparable<T> value, String message, Object... values)
137
```
138
139
**Usage Examples:**
140
```java { .api }
141
public class RangeValidationExamples {
142
143
public void setAge(int age) {
144
Validate.inclusiveBetween(0, 150, age, "Age must be between 0 and 150, got: %d", age);
145
this.age = age;
146
}
147
148
public void setPercentage(double percentage) {
149
Validate.inclusiveBetween(0.0, 100.0, percentage, "Percentage must be between 0 and 100");
150
this.percentage = percentage;
151
}
152
153
public void setTemperature(double celsius) {
154
Validate.inclusiveBetween(-273.15, 1000.0, celsius, "Temperature must be above absolute zero");
155
this.temperature = celsius;
156
}
157
158
public void setScore(int score) {
159
// Exclusive range: score must be > 0 and < 100
160
Validate.exclusiveBetween(0, 100, score, "Score must be greater than 0 and less than 100");
161
this.score = score;
162
}
163
164
public void setGrade(String grade) {
165
Validate.notBlank(grade, "Grade cannot be blank");
166
167
// Using Comparable validation with strings
168
Validate.inclusiveBetween("A", "F", grade, "Grade must be between A and F");
169
this.grade = grade;
170
}
171
172
// Date range validation
173
public void setEventDate(Date eventDate) {
174
Validate.notNull(eventDate, "Event date cannot be null");
175
176
Date now = new Date();
177
Date maxFutureDate = DateUtils.addYears(now, 5);
178
179
Validate.inclusiveBetween(now, maxFutureDate, eventDate,
180
"Event date must be between now and 5 years in the future");
181
182
this.eventDate = eventDate;
183
}
184
}
185
```
186
187
#### Finite Number Validation
188
189
```java { .api }
190
// Finite number validation (not NaN or Infinity)
191
public static void finite(double value)
192
public static void finite(double value, String message, Object... values)
193
public static void finite(float value)
194
public static void finite(float value, String message, Object... values)
195
```
196
197
**Usage Examples:**
198
```java { .api }
199
public class FiniteValidationExamples {
200
201
public void calculateDistance(double x1, double y1, double x2, double y2) {
202
// Ensure all coordinates are finite numbers
203
Validate.finite(x1, "x1 coordinate must be finite");
204
Validate.finite(y1, "y1 coordinate must be finite");
205
Validate.finite(x2, "x2 coordinate must be finite");
206
Validate.finite(y2, "y2 coordinate must be finite");
207
208
double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
209
Validate.finite(distance, "Calculated distance is not finite");
210
211
return distance;
212
}
213
214
public void setPrice(double price) {
215
Validate.finite(price, "Price cannot be NaN or infinite: %f", price);
216
Validate.inclusiveBetween(0.0, Double.MAX_VALUE, price, "Price must be non-negative");
217
218
this.price = price;
219
}
220
221
public Money convertCurrency(Money amount, double exchangeRate) {
222
Validate.notNull(amount, "Amount cannot be null");
223
Validate.finite(exchangeRate, "Exchange rate must be finite: %f", exchangeRate);
224
Validate.exclusiveBetween(0.0, Double.MAX_VALUE, exchangeRate, "Exchange rate must be positive");
225
226
double convertedValue = amount.getValue() * exchangeRate;
227
Validate.finite(convertedValue, "Converted amount is not finite");
228
229
return new Money(convertedValue, targetCurrency);
230
}
231
}
232
```
233
234
#### Boolean State Validation
235
236
```java { .api }
237
// Boolean state validation
238
public static void isTrue(boolean expression)
239
public static void isTrue(boolean expression, String message, Object... values)
240
```
241
242
**Usage Examples:**
243
```java { .api }
244
public class StateValidationExamples {
245
246
public void transferFunds(Account from, Account to, BigDecimal amount) {
247
Validate.notNull(from, "Source account cannot be null");
248
Validate.notNull(to, "Destination account cannot be null");
249
Validate.notNull(amount, "Transfer amount cannot be null");
250
251
// Boolean state validation
252
Validate.isTrue(amount.compareTo(BigDecimal.ZERO) > 0,
253
"Transfer amount must be positive: %s", amount);
254
255
Validate.isTrue(from.getBalance().compareTo(amount) >= 0,
256
"Insufficient funds: balance=%s, requested=%s", from.getBalance(), amount);
257
258
Validate.isTrue(!from.equals(to),
259
"Cannot transfer to the same account");
260
261
Validate.isTrue(from.isActive(),
262
"Source account %s is not active", from.getAccountNumber());
263
264
Validate.isTrue(to.isActive(),
265
"Destination account %s is not active", to.getAccountNumber());
266
267
// Perform transfer...
268
}
269
270
public void processOrder(Order order) {
271
Validate.notNull(order, "Order cannot be null");
272
273
// Validate order state
274
Validate.isTrue(order.hasItems(), "Order must contain at least one item");
275
Validate.isTrue(order.getCustomer() != null, "Order must have a customer");
276
Validate.isTrue(order.getTotalAmount().compareTo(BigDecimal.ZERO) > 0,
277
"Order total must be positive");
278
279
// Validate business rules
280
Validate.isTrue(order.getItems().size() <= 100,
281
"Order cannot contain more than 100 items");
282
283
Validate.isTrue(order.getTotalAmount().compareTo(new BigDecimal("10000")) <= 0,
284
"Order total cannot exceed $10,000");
285
}
286
287
public void setUserPermissions(User user, Set<Permission> permissions) {
288
Validate.notNull(user, "User cannot be null");
289
Validate.notEmpty(permissions, "Permissions cannot be empty");
290
291
// Validate user state
292
Validate.isTrue(user.isActive(), "Cannot set permissions for inactive user: %s", user.getUsername());
293
Validate.isTrue(!user.isLocked(), "Cannot set permissions for locked user: %s", user.getUsername());
294
295
// Validate permission constraints
296
boolean hasAdminPermission = permissions.contains(Permission.ADMIN);
297
boolean hasUserPermissions = permissions.stream().anyMatch(p -> p.getType() == PermissionType.USER);
298
299
Validate.isTrue(!hasAdminPermission || user.getRole() == Role.ADMINISTRATOR,
300
"Only administrators can have admin permissions");
301
302
Validate.isTrue(permissions.size() <= 20,
303
"User cannot have more than 20 permissions");
304
}
305
}
306
```
307
308
## Advanced Validation Patterns
309
310
### Custom Validation Methods
311
312
```java { .api }
313
public final class CustomValidators {
314
315
// Email validation
316
public static String validateEmail(String email) {
317
Validate.notBlank(email, "Email cannot be blank");
318
Validate.isTrue(email.contains("@"), "Email must contain @ symbol: %s", email);
319
Validate.isTrue(email.indexOf("@") != 0, "Email cannot start with @: %s", email);
320
Validate.isTrue(email.lastIndexOf("@") != email.length() - 1, "Email cannot end with @: %s", email);
321
Validate.isTrue(email.indexOf("@") == email.lastIndexOf("@"), "Email cannot contain multiple @ symbols: %s", email);
322
323
return email.toLowerCase().trim();
324
}
325
326
// Phone number validation
327
public static String validatePhoneNumber(String phone) {
328
Validate.notBlank(phone, "Phone number cannot be blank");
329
330
// Remove common formatting
331
String cleaned = phone.replaceAll("[\\s\\-\\(\\)\\.\\+]", "");
332
Validate.isTrue(cleaned.matches("\\d+"), "Phone number can only contain digits and formatting: %s", phone);
333
Validate.inclusiveBetween(7, 15, cleaned.length(),
334
"Phone number must be between 7 and 15 digits: %s", phone);
335
336
return cleaned;
337
}
338
339
// URL validation
340
public static String validateUrl(String url) {
341
Validate.notBlank(url, "URL cannot be blank");
342
343
String lowerUrl = url.toLowerCase();
344
Validate.isTrue(lowerUrl.startsWith("http://") || lowerUrl.startsWith("https://"),
345
"URL must start with http:// or https://: %s", url);
346
347
try {
348
new URL(url); // Additional validation
349
return url;
350
} catch (MalformedURLException e) {
351
throw new IllegalArgumentException("Invalid URL format: " + url, e);
352
}
353
}
354
355
// Credit card validation (simplified)
356
public static String validateCreditCard(String cardNumber) {
357
Validate.notBlank(cardNumber, "Credit card number cannot be blank");
358
359
String cleaned = cardNumber.replaceAll("[\\s\\-]", "");
360
Validate.isTrue(cleaned.matches("\\d+"), "Credit card number can only contain digits: %s", cardNumber);
361
Validate.inclusiveBetween(13, 19, cleaned.length(),
362
"Credit card number must be between 13 and 19 digits: %s", cardNumber);
363
364
// Luhn algorithm validation
365
Validate.isTrue(isValidLuhn(cleaned), "Invalid credit card number: %s", cardNumber);
366
367
return cleaned;
368
}
369
370
private static boolean isValidLuhn(String cardNumber) {
371
int sum = 0;
372
boolean alternate = false;
373
374
for (int i = cardNumber.length() - 1; i >= 0; i--) {
375
int digit = Character.getNumericValue(cardNumber.charAt(i));
376
377
if (alternate) {
378
digit *= 2;
379
if (digit > 9) {
380
digit = (digit % 10) + 1;
381
}
382
}
383
384
sum += digit;
385
alternate = !alternate;
386
}
387
388
return (sum % 10) == 0;
389
}
390
391
// Password strength validation
392
public static String validatePassword(String password) {
393
Validate.notNull(password, "Password cannot be null");
394
Validate.inclusiveBetween(8, 128, password.length(),
395
"Password must be between 8 and 128 characters");
396
397
Validate.isTrue(password.matches(".*[a-z].*"), "Password must contain at least one lowercase letter");
398
Validate.isTrue(password.matches(".*[A-Z].*"), "Password must contain at least one uppercase letter");
399
Validate.isTrue(password.matches(".*\\d.*"), "Password must contain at least one digit");
400
Validate.isTrue(password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':\"\\\\|,.<>\\/?].*"),
401
"Password must contain at least one special character");
402
403
return password;
404
}
405
}
406
```
407
408
### Fluent Validation Builder
409
410
```java { .api }
411
public class FluentValidator<T> {
412
413
private final T value;
414
private final String fieldName;
415
416
private FluentValidator(T value, String fieldName) {
417
this.value = value;
418
this.fieldName = fieldName;
419
}
420
421
public static <T> FluentValidator<T> validate(T value, String fieldName) {
422
return new FluentValidator<>(value, fieldName);
423
}
424
425
public FluentValidator<T> notNull() {
426
Validate.notNull(value, "%s cannot be null", fieldName);
427
return this;
428
}
429
430
public FluentValidator<T> notEmpty() {
431
if (value instanceof Collection) {
432
Validate.notEmpty((Collection<?>) value, "%s cannot be empty", fieldName);
433
} else if (value instanceof CharSequence) {
434
Validate.notEmpty((CharSequence) value, "%s cannot be empty", fieldName);
435
} else if (value instanceof Object[]) {
436
Validate.notEmpty((Object[]) value, "%s cannot be empty", fieldName);
437
}
438
return this;
439
}
440
441
public FluentValidator<T> notBlank() {
442
if (value instanceof CharSequence) {
443
Validate.notBlank((CharSequence) value, "%s cannot be blank", fieldName);
444
}
445
return this;
446
}
447
448
public FluentValidator<T> inclusiveBetween(Comparable<T> start, Comparable<T> end) {
449
if (value instanceof Comparable) {
450
Validate.inclusiveBetween(start, end, (Comparable<T>) value,
451
"%s must be between %s and %s", fieldName, start, end);
452
}
453
return this;
454
}
455
456
public FluentValidator<T> isTrue(boolean condition, String message, Object... args) {
457
Validate.isTrue(condition, message, args);
458
return this;
459
}
460
461
public FluentValidator<T> custom(Predicate<T> validator, String message, Object... args) {
462
Validate.isTrue(validator.test(value), message, args);
463
return this;
464
}
465
466
public T get() {
467
return value;
468
}
469
}
470
471
// Usage examples
472
public class FluentValidationExamples {
473
474
public User createUser(String name, String email, Integer age) {
475
String validatedName = FluentValidator.validate(name, "name")
476
.notNull()
477
.notBlank()
478
.isTrue(name.length() <= 100, "Name cannot exceed 100 characters")
479
.get();
480
481
String validatedEmail = FluentValidator.validate(email, "email")
482
.notNull()
483
.notBlank()
484
.custom(e -> e.contains("@"), "Email must contain @ symbol")
485
.get();
486
487
Integer validatedAge = FluentValidator.validate(age, "age")
488
.notNull()
489
.inclusiveBetween(0, 150)
490
.get();
491
492
return new User(validatedName, validatedEmail, validatedAge);
493
}
494
}
495
```
496
497
### Validation Exception Handling
498
499
```java { .api }
500
@ControllerAdvice
501
public class ValidationExceptionHandler {
502
503
private static final Logger log = LoggerFactory.getLogger(ValidationExceptionHandler.class);
504
505
@ExceptionHandler(IllegalArgumentException.class)
506
public ResponseEntity<ErrorResponse> handleValidationError(IllegalArgumentException ex) {
507
log.warn("Validation error: {}", ex.getMessage());
508
509
ErrorResponse error = new ErrorResponse(
510
"VALIDATION_ERROR",
511
ex.getMessage(),
512
System.currentTimeMillis()
513
);
514
515
return ResponseEntity.badRequest().body(error);
516
}
517
518
@ExceptionHandler(NullPointerException.class)
519
public ResponseEntity<ErrorResponse> handleNullPointerError(NullPointerException ex) {
520
log.error("Null pointer error: {}", ex.getMessage(), ex);
521
522
ErrorResponse error = new ErrorResponse(
523
"NULL_VALUE_ERROR",
524
"A required value was null",
525
System.currentTimeMillis()
526
);
527
528
return ResponseEntity.badRequest().body(error);
529
}
530
}
531
532
// Custom validation service
533
@Service
534
public class ValidationService {
535
536
public <T> T validateAndReturn(T value, Consumer<T> validator, String context) {
537
try {
538
validator.accept(value);
539
return value;
540
} catch (IllegalArgumentException e) {
541
throw new ValidationException(context + ": " + e.getMessage(), e);
542
}
543
}
544
545
public void validateBusinessRules(Object entity) {
546
if (entity instanceof User) {
547
validateUser((User) entity);
548
} else if (entity instanceof Order) {
549
validateOrder((Order) entity);
550
} else {
551
throw new UnsupportedOperationException("Validation not supported for: " + entity.getClass());
552
}
553
}
554
555
private void validateUser(User user) {
556
Validate.notNull(user, "User cannot be null");
557
558
FluentValidator.validate(user.getEmail(), "email")
559
.notBlank()
560
.custom(email -> email.length() <= 255, "Email cannot exceed 255 characters")
561
.custom(this::isValidEmailFormat, "Invalid email format");
562
563
FluentValidator.validate(user.getAge(), "age")
564
.notNull()
565
.inclusiveBetween(13, 120); // Business rule: minimum age 13
566
}
567
568
private void validateOrder(Order order) {
569
Validate.notNull(order, "Order cannot be null");
570
Validate.notEmpty(order.getItems(), "Order must contain items");
571
572
// Validate total amount matches item sum
573
BigDecimal calculatedTotal = order.getItems().stream()
574
.map(OrderItem::getPrice)
575
.reduce(BigDecimal.ZERO, BigDecimal::add);
576
577
Validate.isTrue(order.getTotal().equals(calculatedTotal),
578
"Order total %s does not match calculated total %s",
579
order.getTotal(), calculatedTotal);
580
}
581
}
582
```
583
584
## Integration Examples
585
586
### Spring Boot Integration
587
588
```java { .api }
589
@RestController
590
public class UserController {
591
592
@PostMapping("/users")
593
public ResponseEntity<User> createUser(@RequestBody CreateUserRequest request) {
594
// Validate request
595
Validate.notNull(request, "Request body cannot be null");
596
597
String name = FluentValidator.validate(request.getName(), "name")
598
.notBlank()
599
.isTrue(request.getName().length() <= 100, "Name cannot exceed 100 characters")
600
.get();
601
602
String email = FluentValidator.validate(request.getEmail(), "email")
603
.notBlank()
604
.custom(CustomValidators::isValidEmail, "Invalid email format")
605
.get();
606
607
User user = userService.createUser(name, email);
608
return ResponseEntity.ok(user);
609
}
610
}
611
612
@Component
613
public class ConfigurationValidator {
614
615
@EventListener
616
public void validateConfiguration(ApplicationReadyEvent event) {
617
validateDatabaseConfiguration();
618
validateCacheConfiguration();
619
validateSecurityConfiguration();
620
}
621
622
private void validateDatabaseConfiguration() {
623
String url = environment.getProperty("spring.datasource.url");
624
Validate.notBlank(url, "Database URL must be configured");
625
626
String username = environment.getProperty("spring.datasource.username");
627
Validate.notBlank(username, "Database username must be configured");
628
}
629
}
630
```
631
632
The validation utilities in Apache Commons Lang provide a comprehensive foundation for defensive programming, argument validation, and precondition checking that helps create robust, reliable applications with clear error messages and consistent validation patterns.