or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

base-validation.mddata-size-validation.mdduration-validation.mdindex.mdmethod-validation.mdself-validation.mdvalue-validation.md

self-validation.mddocs/

0

# Self-Validation Framework

1

2

A framework enabling objects to define custom validation logic through annotated methods, providing flexibility for complex business rules that cannot be expressed with standard validation annotations. This framework allows objects to validate themselves using custom logic while integrating seamlessly with Bean Validation.

3

4

## Capabilities

5

6

### Self-Validating Annotation

7

8

Marks a class as having self-validation methods that should be executed during validation.

9

10

```java { .api }

11

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})

12

@Retention(RetentionPolicy.RUNTIME)

13

@Constraint(validatedBy = SelfValidatingValidator.class)

14

public @interface SelfValidating {

15

/**

16

* The validation message for this constraint.

17

*

18

* @return the message

19

*/

20

String message() default "";

21

22

/**

23

* The groups the constraint belongs to.

24

*

25

* @return an array of classes representing the groups

26

*/

27

Class<?>[] groups() default {};

28

29

/**

30

* The payloads of this constraint.

31

*

32

* @return the array of payload classes

33

*/

34

Class<? extends Payload>[] payload() default {};

35

}

36

```

37

38

### Self-Validation Method Annotation

39

40

Marks a method as a self-validation method that will be executed to check if the object is valid.

41

42

```java { .api }

43

@Target(ElementType.METHOD)

44

@Retention(RetentionPolicy.RUNTIME)

45

@Inherited

46

public @interface SelfValidation {

47

}

48

```

49

50

**Method Signature Requirements:**

51

- Must be `public`

52

- Must return `void`

53

- Must accept exactly one parameter of type `ViolationCollector`

54

- Method name can be anything

55

56

### Violation Collector

57

58

Collects constraint violations during self-validation, providing methods to add violations at different scopes.

59

60

```java { .api }

61

public class ViolationCollector {

62

/**

63

* Constructs a new ViolationCollector with the given ConstraintValidatorContext.

64

*

65

* @param constraintValidatorContext the wrapped ConstraintValidatorContext

66

*/

67

public ViolationCollector(ConstraintValidatorContext constraintValidatorContext);

68

69

/**

70

* Adds a new violation to this collector. This also sets violationOccurred to true.

71

*

72

* @param message the message of the violation

73

*/

74

public void addViolation(String message);

75

76

/**

77

* Adds a new violation to this collector. This also sets violationOccurred to true.

78

*

79

* @param message the message of the violation

80

* @param messageParameters a map of message parameters which can be interpolated in the violation message

81

*/

82

public void addViolation(String message, Map<String, Object> messageParameters);

83

84

/**

85

* Adds a new violation to this collector. This also sets violationOccurred to true.

86

*

87

* @param propertyName the name of the property

88

* @param message the message of the violation

89

*/

90

public void addViolation(String propertyName, String message);

91

92

/**

93

* Adds a new violation to this collector. This also sets violationOccurred to true.

94

*

95

* @param propertyName the name of the property

96

* @param message the message of the violation

97

* @param messageParameters a map of message parameters which can be interpolated in the violation message

98

*/

99

public void addViolation(String propertyName, String message, Map<String, Object> messageParameters);

100

101

/**

102

* Adds a new violation to this collector. This also sets violationOccurred to true.

103

*

104

* @param propertyName the name of the property with the violation

105

* @param index the index of the element with the violation

106

* @param message the message of the violation

107

*/

108

public void addViolation(String propertyName, Integer index, String message);

109

110

/**

111

* Adds a new violation to this collector. This also sets violationOccurred to true.

112

*

113

* @param propertyName the name of the property with the violation

114

* @param index the index of the element with the violation

115

* @param message the message of the violation

116

* @param messageParameters a map of message parameters which can be interpolated in the violation message

117

*/

118

public void addViolation(String propertyName, Integer index, String message, Map<String, Object> messageParameters);

119

120

/**

121

* Adds a new violation to this collector. This also sets violationOccurred to true.

122

*

123

* @param propertyName the name of the property with the violation

124

* @param key the key of the element with the violation

125

* @param message the message of the violation

126

*/

127

public void addViolation(String propertyName, String key, String message);

128

129

/**

130

* Adds a new violation to this collector. This also sets violationOccurred to true.

131

*

132

* @param propertyName the name of the property with the violation

133

* @param key the key of the element with the violation

134

* @param message the message of the violation

135

* @param messageParameters a map of message parameters which can be interpolated in the violation message

136

*/

137

public void addViolation(String propertyName, String key, String message, Map<String, Object> messageParameters);

138

139

/**

140

* Returns, if a violation has previously occurred.

141

*

142

* @return if any violation was collected

143

*/

144

public boolean hasViolationOccurred();

145

146

/**

147

* Manually sets if a violation occurred. This is automatically set if addViolation is called.

148

*

149

* @param violationOccurred if any violation was collected

150

*/

151

public void setViolationOccurred(boolean violationOccurred);

152

153

/**

154

* This method returns the wrapped context for raw access to the validation framework.

155

* If you use the context to add violations make sure to call setViolationOccurred(true).

156

*

157

* @return the wrapped Hibernate ConstraintValidatorContext

158

*/

159

public ConstraintValidatorContext getContext();

160

}

161

```

162

163

## Usage Examples

164

165

### Basic Self-Validation

166

167

```java

168

import io.dropwizard.validation.selfvalidating.SelfValidating;

169

import io.dropwizard.validation.selfvalidating.SelfValidation;

170

import io.dropwizard.validation.selfvalidating.ViolationCollector;

171

172

@SelfValidating

173

public class UserRegistration {

174

private String username;

175

private String password;

176

private String confirmPassword;

177

private String email;

178

179

@SelfValidation

180

public void validatePasswords(ViolationCollector collector) {

181

if (password != null && !password.equals(confirmPassword)) {

182

collector.addViolation("confirmPassword", "Password confirmation does not match");

183

}

184

}

185

186

@SelfValidation

187

public void validateBusinessRules(ViolationCollector collector) {

188

if (username != null && username.length() < 3) {

189

collector.addViolation("username", "Username must be at least 3 characters long");

190

}

191

192

if (email != null && !email.contains("@")) {

193

collector.addViolation("email", "Email must be a valid email address");

194

}

195

}

196

197

// getters and setters...

198

}

199

```

200

201

### Advanced Self-Validation with Message Parameters

202

203

```java

204

import io.dropwizard.validation.selfvalidating.SelfValidating;

205

import io.dropwizard.validation.selfvalidating.SelfValidation;

206

import io.dropwizard.validation.selfvalidating.ViolationCollector;

207

import java.util.Map;

208

import java.util.HashMap;

209

210

@SelfValidating

211

public class ConfigurationSettings {

212

private int maxConnections;

213

private int maxConnectionsPerHost;

214

private List<String> allowedHosts;

215

216

@SelfValidation

217

public void validateConnectionSettings(ViolationCollector collector) {

218

if (maxConnectionsPerHost > maxConnections) {

219

Map<String, Object> params = new HashMap<>();

220

params.put("maxConnections", maxConnections);

221

params.put("maxConnectionsPerHost", maxConnectionsPerHost);

222

223

collector.addViolation(

224

"maxConnectionsPerHost",

225

"Connections per host ({maxConnectionsPerHost}) cannot exceed total connections ({maxConnections})",

226

params

227

);

228

}

229

}

230

231

@SelfValidation

232

public void validateHostList(ViolationCollector collector) {

233

if (allowedHosts != null) {

234

for (int i = 0; i < allowedHosts.size(); i++) {

235

String host = allowedHosts.get(i);

236

if (host == null || host.trim().isEmpty()) {

237

collector.addViolation("allowedHosts", i, "Host cannot be empty");

238

}

239

}

240

}

241

}

242

}

243

```

244

245

### Complex Business Logic Validation

246

247

```java

248

import io.dropwizard.validation.selfvalidating.SelfValidating;

249

import io.dropwizard.validation.selfvalidating.SelfValidation;

250

import io.dropwizard.validation.selfvalidating.ViolationCollector;

251

252

@SelfValidating

253

public class OrderConfiguration {

254

private BigDecimal minOrderAmount;

255

private BigDecimal maxOrderAmount;

256

private BigDecimal discountThreshold;

257

private BigDecimal discountPercentage;

258

private Map<String, BigDecimal> categoryLimits;

259

260

@SelfValidation

261

public void validateOrderAmounts(ViolationCollector collector) {

262

if (minOrderAmount != null && maxOrderAmount != null) {

263

if (minOrderAmount.compareTo(maxOrderAmount) > 0) {

264

collector.addViolation("minOrderAmount", "Minimum order amount cannot exceed maximum order amount");

265

}

266

}

267

268

if (discountThreshold != null && maxOrderAmount != null) {

269

if (discountThreshold.compareTo(maxOrderAmount) > 0) {

270

collector.addViolation("discountThreshold", "Discount threshold cannot exceed maximum order amount");

271

}

272

}

273

}

274

275

@SelfValidation

276

public void validateDiscountSettings(ViolationCollector collector) {

277

if (discountPercentage != null) {

278

if (discountPercentage.compareTo(BigDecimal.ZERO) < 0 ||

279

discountPercentage.compareTo(new BigDecimal("100")) > 0) {

280

collector.addViolation("discountPercentage", "Discount percentage must be between 0 and 100");

281

}

282

}

283

}

284

285

@SelfValidation

286

public void validateCategoryLimits(ViolationCollector collector) {

287

if (categoryLimits != null) {

288

for (Map.Entry<String, BigDecimal> entry : categoryLimits.entrySet()) {

289

if (entry.getValue().compareTo(BigDecimal.ZERO) <= 0) {

290

collector.addViolation("categoryLimits", entry.getKey(),

291

"Category limit must be positive");

292

}

293

294

if (maxOrderAmount != null && entry.getValue().compareTo(maxOrderAmount) > 0) {

295

collector.addViolation("categoryLimits", entry.getKey(),

296

"Category limit cannot exceed maximum order amount");

297

}

298

}

299

}

300

}

301

}

302

```

303

304

### Integration with Bean Validation

305

306

```java

307

import io.dropwizard.validation.selfvalidating.SelfValidating;

308

import io.dropwizard.validation.selfvalidating.SelfValidation;

309

import io.dropwizard.validation.selfvalidating.ViolationCollector;

310

import javax.validation.constraints.NotNull;

311

import javax.validation.constraints.Email;

312

import javax.validation.constraints.Size;

313

314

@SelfValidating

315

public class UserProfile {

316

@NotNull

317

@Size(min = 3, max = 50)

318

private String username;

319

320

@NotNull

321

@Email

322

private String email;

323

324

@NotNull

325

@Size(min = 8)

326

private String password;

327

328

private String confirmPassword;

329

private boolean termsAccepted;

330

331

@SelfValidation

332

public void validateCustomRules(ViolationCollector collector) {

333

// Password confirmation (not covered by standard annotations)

334

if (password != null && !password.equals(confirmPassword)) {

335

collector.addViolation("confirmPassword", "Password confirmation must match password");

336

}

337

338

// Business rule validation

339

if (!termsAccepted) {

340

collector.addViolation("termsAccepted", "Terms and conditions must be accepted");

341

}

342

343

// Complex username validation

344

if (username != null && username.contains("admin")) {

345

collector.addViolation("username", "Username cannot contain 'admin'");

346

}

347

}

348

}

349

350

// Usage

351

Validator validator = BaseValidator.newValidator();

352

UserProfile profile = new UserProfile();

353

// ... set properties

354

Set<ConstraintViolation<UserProfile>> violations = validator.validate(profile);

355

// Both standard Bean Validation and self-validation will be executed

356

```

357

358

## Advanced Features

359

360

### Validation Groups

361

362

```java

363

import io.dropwizard.validation.selfvalidating.SelfValidating;

364

import io.dropwizard.validation.selfvalidating.SelfValidation;

365

366

public interface CreateUser {}

367

public interface UpdateUser {}

368

369

@SelfValidating(groups = {CreateUser.class, UpdateUser.class})

370

public class UserData {

371

private String username;

372

private String password;

373

374

@SelfValidation

375

public void validateForCreation(ViolationCollector collector) {

376

// This validation runs for both CreateUser and UpdateUser groups

377

if (username != null && username.length() < 3) {

378

collector.addViolation("username", "Username too short");

379

}

380

}

381

}

382

```

383

384

### Multiple Self-Validation Methods

385

386

```java

387

@SelfValidating

388

public class ComplexConfiguration {

389

private DatabaseConfig database;

390

private CacheConfig cache;

391

private SecurityConfig security;

392

393

@SelfValidation

394

public void validateDatabaseConfig(ViolationCollector collector) {

395

// Database-specific validation logic

396

}

397

398

@SelfValidation

399

public void validateCacheConfig(ViolationCollector collector) {

400

// Cache-specific validation logic

401

}

402

403

@SelfValidation

404

public void validateSecurityConfig(ViolationCollector collector) {

405

// Security-specific validation logic

406

}

407

408

@SelfValidation

409

public void validateCrossComponentCompatibility(ViolationCollector collector) {

410

// Cross-component validation logic

411

}

412

}

413

```

414

415

## Integration Notes

416

417

- Self-validation methods are executed in addition to standard Bean Validation annotations

418

- All `@SelfValidation` methods on a class are executed when validation occurs

419

- Violations are collected and reported alongside standard constraint violations

420

- Self-validation integrates seamlessly with Dropwizard's configuration validation

421

- Use `ViolationCollector` methods to ensure proper violation reporting and message interpolation

422

- Self-validation is particularly useful for cross-field validation and complex business rules