or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdconstraint-validation.mdconstraints.mdindex.mdmessage-interpolation.mdpath-navigation.mdprogrammatic-config.mdresource-loading.mdspi.md

resource-loading.mddocs/

0

# Resource Bundle Loading

1

2

Multiple resource bundle locator implementations supporting platform resource bundles, aggregation of multiple bundles, caching, and delegation patterns for flexible message source configuration.

3

4

## Capabilities

5

6

### Platform Resource Bundle Locator

7

8

Default platform-based resource bundle locator using Java's standard ResourceBundle mechanism.

9

10

```java { .api }

11

package org.hibernate.validator.resourceloading;

12

13

import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;

14

import java.util.Locale;

15

import java.util.ResourceBundle;

16

17

/**

18

* Default implementation using platform's ResourceBundle mechanism.

19

* Loads resource bundles using standard Java resource bundle loading.

20

*/

21

class PlatformResourceBundleLocator implements ResourceBundleLocator {

22

/**

23

* Create locator for specified bundle name.

24

*

25

* @param bundleName resource bundle name (without .properties extension)

26

*/

27

PlatformResourceBundleLocator(String bundleName);

28

29

/**

30

* Create locator with custom class loader.

31

*

32

* @param bundleName resource bundle name

33

* @param classLoader class loader for loading bundles

34

*/

35

PlatformResourceBundleLocator(String bundleName, ClassLoader classLoader);

36

37

/**

38

* Create locator with class loader and control.

39

*

40

* @param bundleName resource bundle name

41

* @param classLoader class loader for loading bundles

42

* @param control resource bundle control for loading strategy

43

*/

44

PlatformResourceBundleLocator(

45

String bundleName,

46

ClassLoader classLoader,

47

ResourceBundle.Control control);

48

49

/**

50

* Get resource bundle for specified locale.

51

*

52

* @param locale locale for bundle

53

* @return resource bundle

54

*/

55

@Override

56

ResourceBundle getResourceBundle(Locale locale);

57

}

58

```

59

60

**Usage Example:**

61

62

```java

63

import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;

64

import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;

65

import jakarta.validation.*;

66

import java.util.*;

67

68

// Create locator for custom bundle

69

ResourceBundleLocator locator = new PlatformResourceBundleLocator("MyValidationMessages");

70

71

// Use with message interpolator

72

MessageInterpolator interpolator = new ResourceBundleMessageInterpolator(locator);

73

74

ValidatorFactory factory = Validation.byDefaultProvider()

75

.configure()

76

.messageInterpolator(interpolator)

77

.buildValidatorFactory();

78

79

// MyValidationMessages.properties:

80

// user.name.required=User name is required

81

// user.email.invalid=Invalid email format

82

// user.age.range=Age must be between {min} and {max}

83

84

// MyValidationMessages_fr.properties:

85

// user.name.required=Le nom d'utilisateur est requis

86

// user.email.invalid=Format d'email invalide

87

// user.age.range=L'âge doit être entre {min} et {max}

88

```

89

90

### Aggregate Resource Bundle Locator

91

92

Aggregates multiple resource bundle locators into a single combined bundle.

93

94

```java { .api }

95

package org.hibernate.validator.resourceloading;

96

97

import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;

98

import java.util.List;

99

import java.util.Locale;

100

import java.util.ResourceBundle;

101

102

/**

103

* Aggregates multiple ResourceBundleLocators.

104

* Combines resource bundles from multiple sources into single bundle.

105

* Messages are looked up in order of locators; first match wins.

106

*/

107

class AggregateResourceBundleLocator implements ResourceBundleLocator {

108

/**

109

* Create aggregating locator.

110

*

111

* @param resourceBundleLocators list of locators to aggregate

112

*/

113

AggregateResourceBundleLocator(List<ResourceBundleLocator> resourceBundleLocators);

114

115

/**

116

* Get aggregated resource bundle for specified locale.

117

* Returns AggregateResourceBundle combining all locators.

118

*

119

* @param locale locale for bundle

120

* @return aggregated resource bundle

121

*/

122

@Override

123

ResourceBundle getResourceBundle(Locale locale);

124

}

125

```

126

127

**Usage Example:**

128

129

```java

130

import org.hibernate.validator.resourceloading.*;

131

import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;

132

import java.util.*;

133

134

// Create multiple resource bundle locators

135

ResourceBundleLocator userMessages =

136

new PlatformResourceBundleLocator("UserMessages");

137

ResourceBundleLocator productMessages =

138

new PlatformResourceBundleLocator("ProductMessages");

139

ResourceBundleLocator orderMessages =

140

new PlatformResourceBundleLocator("OrderMessages");

141

142

// Aggregate them

143

AggregateResourceBundleLocator aggregateLocator =

144

new AggregateResourceBundleLocator(Arrays.asList(

145

userMessages,

146

productMessages,

147

orderMessages

148

));

149

150

// Use aggregated locator

151

MessageInterpolator interpolator = new ResourceBundleMessageInterpolator(aggregateLocator);

152

153

// Message lookup order:

154

// 1. UserMessages.properties

155

// 2. ProductMessages.properties

156

// 3. OrderMessages.properties

157

// First matching key wins

158

```

159

160

### Aggregate Resource Bundle

161

162

Resource bundle that aggregates multiple resource bundles.

163

164

```java { .api }

165

package org.hibernate.validator.resourceloading;

166

167

import java.util.Enumeration;

168

import java.util.Locale;

169

import java.util.ResourceBundle;

170

171

/**

172

* ResourceBundle aggregating multiple resource bundles.

173

* Provides unified view over multiple resource bundle sources.

174

*/

175

class AggregateResourceBundle extends ResourceBundle {

176

/**

177

* Create aggregate bundle.

178

*

179

* @param locale locale for bundle

180

* @param bundlesToAggregate bundles to aggregate

181

*/

182

AggregateResourceBundle(Locale locale, Iterable<ResourceBundle> bundlesToAggregate);

183

184

/**

185

* Get object for key from aggregated bundles.

186

*

187

* @param key message key

188

* @return message value from first bundle containing key

189

*/

190

@Override

191

protected Object handleGetObject(String key);

192

193

/**

194

* Get enumeration of all keys from all aggregated bundles.

195

*

196

* @return enumeration of all keys

197

*/

198

@Override

199

Enumeration<String> getKeys();

200

}

201

```

202

203

### Caching Resource Bundle Locator

204

205

Wraps another resource bundle locator with caching capability.

206

207

```java { .api }

208

package org.hibernate.validator.resourceloading;

209

210

import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;

211

import java.util.Locale;

212

import java.util.ResourceBundle;

213

214

/**

215

* ResourceBundleLocator with caching support.

216

* Caches resource bundles by locale to avoid repeated loading.

217

*/

218

class CachingResourceBundleLocator implements ResourceBundleLocator {

219

/**

220

* Create caching locator wrapping delegate.

221

*

222

* @param delegate underlying locator to cache

223

*/

224

CachingResourceBundleLocator(ResourceBundleLocator delegate);

225

226

/**

227

* Get cached resource bundle for locale.

228

* Loads from delegate and caches on first request.

229

*

230

* @param locale locale for bundle

231

* @return cached resource bundle

232

*/

233

@Override

234

ResourceBundle getResourceBundle(Locale locale);

235

}

236

```

237

238

**Usage Example:**

239

240

```java

241

import org.hibernate.validator.resourceloading.*;

242

243

// Create base locator

244

ResourceBundleLocator baseLocator =

245

new PlatformResourceBundleLocator("ValidationMessages");

246

247

// Wrap with caching

248

CachingResourceBundleLocator cachingLocator =

249

new CachingResourceBundleLocator(baseLocator);

250

251

// Use caching locator

252

// Subsequent calls with same locale will return cached bundle

253

ResourceBundle bundle1 = cachingLocator.getResourceBundle(Locale.US);

254

ResourceBundle bundle2 = cachingLocator.getResourceBundle(Locale.US);

255

// bundle1 and bundle2 are the same instance (cached)

256

```

257

258

### Delegating Resource Bundle Locator

259

260

Delegates resource bundle loading to another locator.

261

262

```java { .api }

263

package org.hibernate.validator.resourceloading;

264

265

import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;

266

import java.util.Locale;

267

import java.util.ResourceBundle;

268

269

/**

270

* ResourceBundleLocator that delegates to another locator.

271

* Base class for decorating locators with additional behavior.

272

*/

273

class DelegatingResourceBundleLocator implements ResourceBundleLocator {

274

/**

275

* Create delegating locator.

276

*

277

* @param delegate locator to delegate to

278

*/

279

DelegatingResourceBundleLocator(ResourceBundleLocator delegate);

280

281

/**

282

* Get resource bundle by delegating to wrapped locator.

283

*

284

* @param locale locale for bundle

285

* @return resource bundle from delegate

286

*/

287

@Override

288

ResourceBundle getResourceBundle(Locale locale);

289

}

290

```

291

292

## Complete Resource Bundle Configuration Example

293

294

```java

295

import org.hibernate.validator.resourceloading.*;

296

import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;

297

import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;

298

import jakarta.validation.*;

299

import java.util.*;

300

301

/**

302

* Configure complex resource bundle hierarchy:

303

* 1. Application-specific messages

304

* 2. Module-specific messages

305

* 3. Contributor messages

306

* 4. Default ValidationMessages

307

*/

308

class ResourceBundleConfiguration {

309

310

public ValidatorFactory createValidatorFactory() {

311

// Create individual locators

312

ResourceBundleLocator appMessages =

313

new PlatformResourceBundleLocator("ApplicationMessages");

314

315

ResourceBundleLocator userModuleMessages =

316

new PlatformResourceBundleLocator("UserModuleMessages");

317

318

ResourceBundleLocator productModuleMessages =

319

new PlatformResourceBundleLocator("ProductModuleMessages");

320

321

ResourceBundleLocator contributorMessages =

322

new PlatformResourceBundleLocator("ContributorMessages");

323

324

ResourceBundleLocator defaultMessages =

325

new PlatformResourceBundleLocator("ValidationMessages");

326

327

// Aggregate module messages

328

AggregateResourceBundleLocator moduleMessages =

329

new AggregateResourceBundleLocator(Arrays.asList(

330

userModuleMessages,

331

productModuleMessages

332

));

333

334

// Create final hierarchy

335

AggregateResourceBundleLocator allMessages =

336

new AggregateResourceBundleLocator(Arrays.asList(

337

appMessages, // Highest priority

338

moduleMessages,

339

contributorMessages,

340

defaultMessages // Lowest priority

341

));

342

343

// Add caching for performance

344

CachingResourceBundleLocator cachedMessages =

345

new CachingResourceBundleLocator(allMessages);

346

347

// Create message interpolator

348

ResourceBundleMessageInterpolator interpolator =

349

new ResourceBundleMessageInterpolator(cachedMessages);

350

351

// Build validator factory

352

return Validation.byDefaultProvider()

353

.configure()

354

.messageInterpolator(interpolator)

355

.buildValidatorFactory();

356

}

357

}

358

359

// Message lookup order:

360

// 1. ApplicationMessages.properties (app-specific overrides)

361

// 2. UserModuleMessages.properties (user module messages)

362

// 3. ProductModuleMessages.properties (product module messages)

363

// 4. ContributorMessages.properties (contributor messages)

364

// 5. ValidationMessages.properties (defaults)

365

```

366

367

## Custom Resource Bundle Locator

368

369

```java

370

import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;

371

import java.util.*;

372

373

/**

374

* Custom resource bundle locator loading messages from database.

375

*/

376

class DatabaseResourceBundleLocator implements ResourceBundleLocator {

377

378

private final MessageRepository messageRepository;

379

380

public DatabaseResourceBundleLocator(MessageRepository messageRepository) {

381

this.messageRepository = messageRepository;

382

}

383

384

@Override

385

public ResourceBundle getResourceBundle(Locale locale) {

386

// Load messages from database

387

Map<String, String> messages = messageRepository.findMessagesByLocale(locale);

388

389

// Create resource bundle from database messages

390

return new DatabaseResourceBundle(messages);

391

}

392

}

393

394

/**

395

* ResourceBundle backed by database messages.

396

*/

397

class DatabaseResourceBundle extends ResourceBundle {

398

399

private final Map<String, String> messages;

400

401

public DatabaseResourceBundle(Map<String, String> messages) {

402

this.messages = messages;

403

}

404

405

@Override

406

protected Object handleGetObject(String key) {

407

return messages.get(key);

408

}

409

410

@Override

411

public Enumeration<String> getKeys() {

412

return Collections.enumeration(messages.keySet());

413

}

414

}

415

416

// Usage

417

interface MessageRepository {

418

Map<String, String> findMessagesByLocale(Locale locale);

419

}

420

421

class MessageRepositoryImpl implements MessageRepository {

422

@Override

423

public Map<String, String> findMessagesByLocale(Locale locale) {

424

// Query database for messages

425

Map<String, String> messages = new HashMap<>();

426

// ... load from database ...

427

return messages;

428

}

429

}

430

431

// Configure validator with database messages

432

MessageRepository repository = new MessageRepositoryImpl();

433

ResourceBundleLocator dbLocator = new DatabaseResourceBundleLocator(repository);

434

435

// Combine database messages with file-based messages

436

ResourceBundleLocator fileLocator =

437

new PlatformResourceBundleLocator("ValidationMessages");

438

439

AggregateResourceBundleLocator combinedLocator =

440

new AggregateResourceBundleLocator(Arrays.asList(

441

dbLocator, // Database messages (higher priority)

442

fileLocator // File messages (fallback)

443

));

444

445

MessageInterpolator interpolator =

446

new ResourceBundleMessageInterpolator(combinedLocator);

447

448

ValidatorFactory factory = Validation.byDefaultProvider()

449

.configure()

450

.messageInterpolator(interpolator)

451

.buildValidatorFactory();

452

```

453

454

## Resource Bundle Organization Best Practices

455

456

```java

457

/**

458

* Best practices for organizing validation messages:

459

*

460

* 1. Hierarchical structure:

461

* - ValidationMessages.properties (defaults from Jakarta Validation & Hibernate Validator)

462

* - ApplicationMessages.properties (application-wide overrides)

463

* - ModuleMessages.properties (module-specific messages)

464

* - CustomMessages.properties (custom constraint messages)

465

*

466

* 2. Message key conventions:

467

* - Standard constraints: Use default keys (e.g., jakarta.validation.constraints.NotNull.message)

468

* - Custom constraints: Use fully qualified names (e.g., com.example.constraints.CustomConstraint.message)

469

* - Domain-specific: Use domain prefixes (e.g., user.email.invalid, product.price.negative)

470

*

471

* 3. Localization:

472

* - Create locale-specific files: ValidationMessages_fr.properties, ValidationMessages_de.properties

473

* - Keep keys consistent across all locales

474

* - Provide fallback messages in default bundle

475

*

476

* 4. Parameter naming:

477

* - Use descriptive parameter names: {min}, {max}, {value}, {regexp}

478

* - Document expected parameters in comments

479

*

480

* 5. Performance:

481

* - Use caching locators for frequently accessed bundles

482

* - Preload bundles for known locales at startup

483

* - Avoid complex bundle hierarchies for simple applications

484

*/

485

486

// Example bundle structure:

487

488

// ValidationMessages.properties (defaults)

489

// jakarta.validation.constraints.NotNull.message=must not be null

490

// jakarta.validation.constraints.Size.message=size must be between {min} and {max}

491

492

// ApplicationMessages.properties (overrides)

493

// jakarta.validation.constraints.NotNull.message=This field is required

494

// user.email.invalid=Please provide a valid email address

495

// user.password.weak=Password must contain at least 8 characters with letters and numbers

496

497

// ModuleMessages.properties (module-specific)

498

// order.total.negative=Order total cannot be negative

499

// order.items.empty=Order must contain at least one item

500

// order.discount.invalid=Discount percentage must be between 0 and 100

501

502

// ValidationMessages_fr.properties (French)

503

// jakarta.validation.constraints.NotNull.message=ne doit pas être null

504

// user.email.invalid=Veuillez fournir une adresse email valide

505

506

// Usage

507

ResourceBundleLocator appLocator = new PlatformResourceBundleLocator("ApplicationMessages");

508

ResourceBundleLocator moduleLocator = new PlatformResourceBundleLocator("ModuleMessages");

509

ResourceBundleLocator defaultLocator = new PlatformResourceBundleLocator("ValidationMessages");

510

511

AggregateResourceBundleLocator aggregateLocator = new AggregateResourceBundleLocator(

512

Arrays.asList(appLocator, moduleLocator, defaultLocator)

513

);

514

515

CachingResourceBundleLocator cachingLocator = new CachingResourceBundleLocator(aggregateLocator);

516

517

MessageInterpolator interpolator = new ResourceBundleMessageInterpolator(cachingLocator);

518

```

519