or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdconnection-pooling.mdhibernate-integration.mdindex.mdjmx-management.mdmetrics-integration.mdutilities.md

utilities.mddocs/

0

# Utilities

1

2

Supporting utility classes for credentials management, JNDI integration, and advanced connection handling.

3

4

## Capabilities

5

6

### Credentials Management

7

8

Immutable credentials holder for secure username/password management.

9

10

```java { .api }

11

/**

12

* Immutable credentials holder for database authentication

13

* Thread-safe and designed for use with atomic updates

14

*/

15

public final class Credentials {

16

17

/**

18

* Factory method to create credentials instance

19

* @param username Database username (can be null)

20

* @param password Database password (can be null)

21

* @return New Credentials instance

22

*/

23

public static Credentials of(String username, String password);

24

25

/**

26

* Constructor for credentials

27

* @param username Database username

28

* @param password Database password

29

*/

30

@ConstructorParameters({"username", "password"})

31

public Credentials(String username, String password);

32

33

/**

34

* Get the username

35

* @return Username string or null

36

*/

37

public String getUsername();

38

39

/**

40

* Get the password

41

* @return Password string or null

42

*/

43

public String getPassword();

44

}

45

```

46

47

**Credentials Usage Examples:**

48

49

```java

50

import com.zaxxer.hikari.util.Credentials;

51

import java.util.concurrent.atomic.AtomicReference;

52

53

// Basic credential creation

54

Credentials creds = Credentials.of("dbuser", "dbpass");

55

Credentials nullCreds = Credentials.of(null, null);

56

57

// Thread-safe credential updates (used internally by HikariConfig)

58

AtomicReference<Credentials> credentialsRef = new AtomicReference<>(

59

Credentials.of("olduser", "oldpass"));

60

61

// Atomic username update

62

credentialsRef.updateAndGet(current ->

63

Credentials.of("newuser", current.getPassword()));

64

65

// Atomic password update

66

credentialsRef.updateAndGet(current ->

67

Credentials.of(current.getUsername(), "newpass"));

68

69

// Atomic full update

70

credentialsRef.set(Credentials.of("admin", "secretpass"));

71

72

// Usage in configuration

73

HikariConfig config = new HikariConfig();

74

config.setCredentials(Credentials.of("appuser", "apppass"));

75

76

// Runtime credential updates (DataSource-based connections only)

77

HikariDataSource dataSource = new HikariDataSource(config);

78

dataSource.setCredentials(Credentials.of("newuser", "newpass"));

79

```

80

81

### JNDI Integration

82

83

JNDI ObjectFactory for creating HikariDataSource instances in application servers.

84

85

```java { .api }

86

/**

87

* JNDI ObjectFactory for creating HikariDataSource instances

88

* Enables deployment in Java EE application servers with JNDI configuration

89

*/

90

public class HikariJNDIFactory implements ObjectFactory {

91

92

/**

93

* Create HikariDataSource instance from JNDI Reference

94

* @param obj Reference object (must be javax.sql.DataSource Reference)

95

* @param name JNDI name being created

96

* @param nameCtx Naming context

97

* @param environment JNDI environment

98

* @return HikariDataSource instance or null if not applicable

99

* @throws Exception if creation fails

100

*/

101

@Override

102

public synchronized Object getObjectInstance(Object obj, Name name, Context nameCtx,

103

Hashtable<?, ?> environment) throws Exception;

104

}

105

```

106

107

**JNDI Configuration Examples:**

108

109

**Tomcat context.xml:**

110

111

```xml

112

<Context>

113

<Resource name="jdbc/MyDB"

114

factory="com.zaxxer.hikari.HikariJNDIFactory"

115

type="javax.sql.DataSource"

116

jdbcUrl="jdbc:mysql://localhost:3306/mydb"

117

username="dbuser"

118

password="dbpass"

119

maximumPoolSize="20"

120

minimumIdle="5"

121

connectionTimeout="30000"

122

idleTimeout="600000"

123

maxLifetime="1800000"

124

poolName="JNDI-Pool"

125

dataSource.cachePrepStmts="true"

126

dataSource.prepStmtCacheSize="250"

127

dataSource.useServerPrepStmts="true" />

128

</Context>

129

```

130

131

**WildFly/JBoss standalone.xml:**

132

133

```xml

134

<subsystem xmlns="urn:jboss:domain:naming:2.0">

135

<bindings>

136

<object-factory name="java:jboss/datasources/MyDB"

137

module="com.zaxxer.hikari"

138

class="com.zaxxer.hikari.HikariJNDIFactory">

139

<environment>

140

<property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/mydb"/>

141

<property name="username" value="dbuser"/>

142

<property name="password" value="dbpass"/>

143

<property name="maximumPoolSize" value="25"/>

144

<property name="minimumIdle" value="5"/>

145

<property name="poolName" value="WildFly-Pool"/>

146

<property name="registerMbeans" value="true"/>

147

</environment>

148

</object-factory>

149

</bindings>

150

</subsystem>

151

```

152

153

**JNDI Lookup Usage:**

154

155

```java

156

import javax.naming.InitialContext;

157

import javax.sql.DataSource;

158

159

public class DatabaseService {

160

161

private DataSource dataSource;

162

163

public void init() {

164

try {

165

InitialContext ctx = new InitialContext();

166

dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/MyDB");

167

System.out.println("HikariDataSource obtained from JNDI");

168

} catch (Exception e) {

169

throw new RuntimeException("Failed to lookup DataSource", e);

170

}

171

}

172

173

public Connection getConnection() throws SQLException {

174

return dataSource.getConnection();

175

}

176

}

177

```

178

179

**JNDI with DataSource Reference:**

180

181

```xml

182

<!-- Alternative JNDI configuration using existing DataSource -->

183

<Resource name="jdbc/MyDB"

184

factory="com.zaxxer.hikari.HikariJNDIFactory"

185

type="javax.sql.DataSource"

186

dataSourceJNDI="java:comp/env/jdbc/UnderlyingDS"

187

maximumPoolSize="15"

188

minimumIdle="3"

189

poolName="Wrapped-Pool" />

190

```

191

192

### SQL Exception Override

193

194

Interface for customizing SQLException handling and connection eviction behavior.

195

196

```java { .api }

197

/**

198

* Interface for custom SQLException handling in HikariCP

199

* Allows overriding default connection eviction logic

200

*/

201

public interface SQLExceptionOverride {

202

203

/**

204

* Enumeration of eviction override decisions

205

*/

206

enum Override {

207

/** Continue with default HikariCP eviction logic */

208

CONTINUE_EVICT,

209

/** Do not evict the connection regardless of exception */

210

DO_NOT_EVICT,

211

/** Force eviction regardless of exception type */

212

MUST_EVICT

213

}

214

215

/**

216

* Adjudicate whether a connection should be evicted based on SQLException

217

* @param sqlException The SQLException that was thrown

218

* @return Override decision for connection eviction

219

*/

220

default Override adjudicate(SQLException sqlException);

221

}

222

```

223

224

**SQL Exception Override Examples:**

225

226

```java

227

import com.zaxxer.hikari.SQLExceptionOverride;

228

import java.sql.SQLException;

229

230

/**

231

* Custom exception override for database-specific error handling

232

*/

233

public class CustomSQLExceptionOverride implements SQLExceptionOverride {

234

235

@Override

236

public Override adjudicate(SQLException sqlException) {

237

String sqlState = sqlException.getSQLState();

238

int errorCode = sqlException.getErrorCode();

239

240

// MySQL-specific error handling

241

if (sqlState != null) {

242

switch (sqlState) {

243

case "08S01": // Communication link failure

244

case "08007": // Connection failure during transaction

245

case "08006": // Connection failure

246

return Override.MUST_EVICT;

247

248

case "40001": // Serialization failure (deadlock)

249

case "25006": // Read-only transaction

250

return Override.DO_NOT_EVICT; // Temporary condition

251

252

case "42000": // Syntax error - don't evict for application errors

253

case "23000": // Integrity constraint violation

254

return Override.DO_NOT_EVICT;

255

}

256

}

257

258

// MySQL error codes

259

switch (errorCode) {

260

case 1205: // Lock wait timeout - temporary condition

261

case 1213: // Deadlock - temporary condition

262

return Override.DO_NOT_EVICT;

263

264

case 2006: // MySQL server has gone away

265

case 2013: // Lost connection to MySQL server

266

return Override.MUST_EVICT;

267

}

268

269

// PostgreSQL-specific handling

270

if (sqlException.getMessage() != null) {

271

String message = sqlException.getMessage().toLowerCase();

272

if (message.contains("connection refused") ||

273

message.contains("connection reset") ||

274

message.contains("broken pipe")) {

275

return Override.MUST_EVICT;

276

}

277

}

278

279

// Default to HikariCP's built-in logic

280

return Override.CONTINUE_EVICT;

281

}

282

}

283

284

// Configuration usage

285

HikariConfig config = new HikariConfig();

286

config.setExceptionOverrideClassName("com.myapp.CustomSQLExceptionOverride");

287

288

// Or set instance directly

289

config.setExceptionOverride(new CustomSQLExceptionOverride());

290

```

291

292

**Advanced Exception Override:**

293

294

```java

295

import org.slf4j.Logger;

296

import org.slf4j.LoggerFactory;

297

298

public class ProductionSQLExceptionOverride implements SQLExceptionOverride {

299

300

private static final Logger logger = LoggerFactory.getLogger(ProductionSQLExceptionOverride.class);

301

302

@Override

303

public Override adjudicate(SQLException sqlException) {

304

// Log all exceptions for monitoring

305

logger.warn("SQLException in connection pool - State: {}, Code: {}, Message: {}",

306

sqlException.getSQLState(), sqlException.getErrorCode(), sqlException.getMessage());

307

308

// Check for specific patterns that indicate connection issues

309

if (isConnectionFailure(sqlException)) {

310

logger.error("Connection failure detected, forcing eviction", sqlException);

311

return Override.MUST_EVICT;

312

}

313

314

// Check for temporary conditions that shouldn't cause eviction

315

if (isTemporaryCondition(sqlException)) {

316

logger.info("Temporary condition detected, preserving connection");

317

return Override.DO_NOT_EVICT;

318

}

319

320

// Use default HikariCP logic for other cases

321

return Override.CONTINUE_EVICT;

322

}

323

324

private boolean isConnectionFailure(SQLException e) {

325

String sqlState = e.getSQLState();

326

int errorCode = e.getErrorCode();

327

String message = e.getMessage();

328

329

// SQLSTATE classes indicating connection problems

330

if (sqlState != null && (sqlState.startsWith("08") || sqlState.startsWith("57"))) {

331

return true;

332

}

333

334

// Common database error codes for connection issues

335

if (errorCode == 2006 || errorCode == 2013 || // MySQL

336

errorCode == 17002 || errorCode == 17008 || // Oracle

337

errorCode == -4499 || errorCode == -30108) { // DB2

338

return true;

339

}

340

341

// Message-based detection

342

if (message != null) {

343

String lowerMessage = message.toLowerCase();

344

return lowerMessage.contains("connection") &&

345

(lowerMessage.contains("closed") || lowerMessage.contains("reset") ||

346

lowerMessage.contains("refused") || lowerMessage.contains("timeout"));

347

}

348

349

return false;

350

}

351

352

private boolean isTemporaryCondition(SQLException e) {

353

String sqlState = e.getSQLState();

354

int errorCode = e.getErrorCode();

355

356

// Serialization failures, deadlocks - retry without eviction

357

if ("40001".equals(sqlState) || "40P01".equals(sqlState)) {

358

return true;

359

}

360

361

// Lock timeouts

362

if (errorCode == 1205 || errorCode == 1213) { // MySQL

363

return true;

364

}

365

366

return false;

367

}

368

}

369

```

370

371

### Utility Integration

372

373

Integrate utility classes with HikariCP configuration and monitoring.

374

375

```java

376

import com.zaxxer.hikari.HikariConfig;

377

import com.zaxxer.hikari.HikariDataSource;

378

import com.zaxxer.hikari.util.Credentials;

379

380

public class DatabaseConnectionManager {

381

382

private HikariDataSource primaryDataSource;

383

private HikariDataSource readOnlyDataSource;

384

385

public void initialize() {

386

// Primary database with custom exception handling

387

HikariConfig primaryConfig = createBaseConfig();

388

primaryConfig.setJdbcUrl("jdbc:mysql://primary-db:3306/myapp");

389

primaryConfig.setCredentials(Credentials.of("app_user", "app_pass"));

390

primaryConfig.setExceptionOverride(new ProductionSQLExceptionOverride());

391

primaryConfig.setPoolName("Primary-DB");

392

primaryDataSource = new HikariDataSource(primaryConfig);

393

394

// Read-only replica

395

HikariConfig readOnlyConfig = createBaseConfig();

396

readOnlyConfig.setJdbcUrl("jdbc:mysql://readonly-db:3306/myapp");

397

readOnlyConfig.setCredentials(Credentials.of("readonly_user", "readonly_pass"));

398

readOnlyConfig.setReadOnly(true);

399

readOnlyConfig.setPoolName("ReadOnly-DB");

400

readOnlyDataSource = new HikariDataSource(readOnlyConfig);

401

}

402

403

private HikariConfig createBaseConfig() {

404

HikariConfig config = new HikariConfig();

405

config.setMaximumPoolSize(20);

406

config.setMinimumIdle(5);

407

config.setConnectionTimeout(30000);

408

config.setIdleTimeout(600000);

409

config.setMaxLifetime(1800000);

410

config.setLeakDetectionThreshold(60000);

411

config.setRegisterMbeans(true);

412

413

// MySQL optimizations

414

config.addDataSourceProperty("cachePrepStmts", "true");

415

config.addDataSourceProperty("prepStmtCacheSize", "250");

416

config.addDataSourceProperty("useServerPrepStmts", "true");

417

418

return config;

419

}

420

421

public Connection getPrimaryConnection() throws SQLException {

422

return primaryDataSource.getConnection();

423

}

424

425

public Connection getReadOnlyConnection() throws SQLException {

426

return readOnlyDataSource.getConnection();

427

}

428

429

public void updateCredentials(String username, String password) {

430

Credentials newCreds = Credentials.of(username, password);

431

primaryDataSource.setCredentials(newCreds);

432

// Note: credential updates only work with DataSource-based connections

433

}

434

435

public void shutdown() {

436

if (primaryDataSource != null) {

437

primaryDataSource.close();

438

}

439

if (readOnlyDataSource != null) {

440

readOnlyDataSource.close();

441

}

442

}

443

}

444

```

445

446

### Advanced Utility Patterns

447

448

Advanced patterns using HikariCP utilities for enterprise scenarios.

449

450

```java

451

import javax.naming.InitialContext;

452

import java.util.concurrent.ConcurrentHashMap;

453

import java.util.Map;

454

455

/**

456

* Multi-tenant database connection manager using HikariCP utilities

457

*/

458

public class MultiTenantConnectionManager {

459

460

private final Map<String, HikariDataSource> tenantDataSources = new ConcurrentHashMap<>();

461

private final SQLExceptionOverride commonExceptionOverride = new ProductionSQLExceptionOverride();

462

463

/**

464

* Get or create DataSource for specific tenant

465

*/

466

public DataSource getDataSource(String tenantId) {

467

return tenantDataSources.computeIfAbsent(tenantId, this::createTenantDataSource);

468

}

469

470

private HikariDataSource createTenantDataSource(String tenantId) {

471

try {

472

// Try JNDI lookup first

473

InitialContext ctx = new InitialContext();

474

String jndiName = "java:comp/env/jdbc/" + tenantId;

475

476

try {

477

return (HikariDataSource) ctx.lookup(jndiName);

478

} catch (Exception e) {

479

// JNDI lookup failed, create programmatically

480

return createProgrammaticDataSource(tenantId);

481

}

482

483

} catch (Exception e) {

484

throw new RuntimeException("Failed to create DataSource for tenant: " + tenantId, e);

485

}

486

}

487

488

private HikariDataSource createProgrammaticDataSource(String tenantId) {

489

HikariConfig config = new HikariConfig();

490

491

// Tenant-specific configuration

492

TenantConfig tenantConfig = getTenantConfig(tenantId);

493

config.setJdbcUrl(tenantConfig.getJdbcUrl());

494

config.setCredentials(Credentials.of(tenantConfig.getUsername(), tenantConfig.getPassword()));

495

496

// Common pool settings

497

config.setMaximumPoolSize(tenantConfig.getMaxPoolSize());

498

config.setMinimumIdle(tenantConfig.getMinIdle());

499

config.setConnectionTimeout(30000);

500

config.setIdleTimeout(600000);

501

config.setMaxLifetime(1800000);

502

config.setPoolName("Tenant-" + tenantId);

503

config.setRegisterMbeans(true);

504

505

// Common exception handling

506

config.setExceptionOverride(commonExceptionOverride);

507

508

return new HikariDataSource(config);

509

}

510

511

/**

512

* Update credentials for all tenants (rolling update)

513

*/

514

public void updateCredentials(Map<String, Credentials> tenantCredentials) {

515

tenantCredentials.forEach((tenantId, credentials) -> {

516

HikariDataSource ds = tenantDataSources.get(tenantId);

517

if (ds != null) {

518

ds.setCredentials(credentials);

519

System.out.printf("Updated credentials for tenant: %s%n", tenantId);

520

}

521

});

522

}

523

524

/**

525

* Shutdown all tenant connections

526

*/

527

public void shutdown() {

528

tenantDataSources.values().forEach(HikariDataSource::close);

529

tenantDataSources.clear();

530

}

531

532

private TenantConfig getTenantConfig(String tenantId) {

533

// Load tenant-specific configuration from external source

534

// This is implementation-specific

535

return new TenantConfig(tenantId);

536

}

537

538

private static class TenantConfig {

539

private final String tenantId;

540

541

public TenantConfig(String tenantId) {

542

this.tenantId = tenantId;

543

}

544

545

public String getJdbcUrl() {

546

return "jdbc:mysql://db-" + tenantId + ":3306/" + tenantId;

547

}

548

549

public String getUsername() {

550

return tenantId + "_user";

551

}

552

553

public String getPassword() {

554

return loadPasswordFromVault(tenantId);

555

}

556

557

public int getMaxPoolSize() {

558

return "premium".equals(getTenantTier()) ? 20 : 10;

559

}

560

561

public int getMinIdle() {

562

return "premium".equals(getTenantTier()) ? 5 : 2;

563

}

564

565

// Implementation-specific methods

566

private String loadPasswordFromVault(String tenantId) { return "password"; }

567

private String getTenantTier() { return "standard"; }

568

}

569

}