or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access.mdexceptions.mdindex.mdloading.mdoptions.mdresolution.mdvalues.md

resolution.mddocs/

0

# Resolution and Substitution

1

2

Resolution and substitution provide dynamic configuration capabilities through variable substitution using `${path}` syntax. The system supports environment variables, system properties, and custom resolvers with comprehensive fallback and override mechanisms.

3

4

## Resolution Process

5

6

### Basic Resolution

7

8

Resolve all substitutions in a configuration to create a fully resolved Config.

9

10

```java { .api }

11

public Config resolve();

12

public Config resolve(ConfigResolveOptions options);

13

public boolean isResolved();

14

```

15

16

**Usage Examples:**

17

18

```java

19

// Basic resolution (resolves all ${...} substitutions)

20

Config unresolved = ConfigFactory.parseString("""

21

app {

22

name = "MyApp"

23

version = "1.0.0"

24

database {

25

url = "jdbc:postgresql://"${database.host}":"${database.port}"/"${database.name}

26

host = ${?DB_HOST}

27

port = ${?DB_PORT}

28

name = ${?DB_NAME}

29

}

30

}

31

""");

32

33

Config resolved = unresolved.resolve();

34

35

// Check if configuration is fully resolved

36

if (resolved.isResolved()) {

37

String dbUrl = resolved.getString("app.database.url");

38

}

39

```

40

41

### Resolution with External Source

42

43

Resolve substitutions using values from another configuration.

44

45

```java { .api }

46

public Config resolveWith(Config source);

47

public Config resolveWith(Config source, ConfigResolveOptions options);

48

```

49

50

**Usage Examples:**

51

52

```java

53

// Configuration with substitutions

54

Config template = ConfigFactory.parseString("""

55

server {

56

host = ${server.host}

57

port = ${server.port}

58

ssl = ${server.ssl.enabled}

59

}

60

""");

61

62

// Source configuration with actual values

63

Config values = ConfigFactory.parseString("""

64

server {

65

host = "production.example.com"

66

port = 8443

67

ssl.enabled = true

68

}

69

""");

70

71

// Resolve template using values

72

Config resolved = template.resolveWith(values);

73

```

74

75

## Substitution Syntax

76

77

### Basic Substitutions

78

79

Standard path-based substitutions reference other configuration values.

80

81

**Syntax Examples:**

82

83

```hocon

84

# Reference another path

85

app.name = "MyApp"

86

app.display-name = ${app.name}

87

88

# Nested path references

89

database {

90

host = "localhost"

91

port = 5432

92

url = "jdbc:postgresql://"${database.host}":"${database.port}"/mydb"

93

}

94

95

# Self-references and concatenation

96

base-path = "/api/v1"

97

endpoints {

98

users = ${base-path}"/users"

99

orders = ${base-path}"/orders"

100

}

101

```

102

103

### Optional Substitutions

104

105

Optional substitutions use `${?path}` syntax and don't fail if the path is missing.

106

107

**Syntax Examples:**

108

109

```hocon

110

# Optional environment variable substitution

111

database {

112

host = "localhost"

113

host = ${?DATABASE_HOST} # Override with env var if present

114

115

port = 5432

116

port = ${?DATABASE_PORT}

117

118

# Optional with fallback chain

119

url = ${?DATABASE_URL}

120

url = "jdbc:postgresql://"${database.host}":"${database.port}"/mydb"

121

}

122

123

# Optional system property

124

jvm {

125

max-heap = "512M"

126

max-heap = ${?JAVA_MAX_HEAP}

127

}

128

```

129

130

### Self-Substitution

131

132

Self-substitution allows a path to reference its own previous value.

133

134

**Syntax Examples:**

135

136

```hocon

137

# Append to existing value

138

path = "/base"

139

path = ${path}"/extended" # Results in "/base/extended"

140

141

# Prepend environment-specific prefix

142

config-file = "app.conf"

143

config-file = ${?ENV}"."${config-file} # Could become "prod.app.conf"

144

```

145

146

## Resolution Options

147

148

### ConfigResolveOptions Class

149

150

Control resolution behavior with comprehensive options.

151

152

```java { .api }

153

public final class ConfigResolveOptions {

154

public static ConfigResolveOptions defaults();

155

public static ConfigResolveOptions noSystem();

156

public ConfigResolveOptions setUseSystemEnvironment(boolean value);

157

public ConfigResolveOptions setAllowUnresolved(boolean value);

158

public ConfigResolveOptions appendResolver(ConfigResolver resolver);

159

public List<ConfigResolver> getResolvers();

160

}

161

```

162

163

**Usage Examples:**

164

165

```java

166

// Default resolution (includes system environment and properties)

167

Config resolved = config.resolve();

168

169

// Resolution without system environment

170

Config resolved = config.resolve(ConfigResolveOptions.noSystem());

171

172

// Custom resolution options

173

ConfigResolveOptions options = ConfigResolveOptions.defaults()

174

.setUseSystemEnvironment(false) // Disable environment variables

175

.setAllowUnresolved(true); // Allow unresolved substitutions

176

177

Config partiallyResolved = config.resolve(options);

178

179

// Add custom resolver

180

ConfigResolver customResolver = new MyCustomResolver();

181

ConfigResolveOptions withCustom = ConfigResolveOptions.defaults()

182

.appendResolver(customResolver);

183

184

Config resolved = config.resolve(withCustom);

185

```

186

187

### System Integration

188

189

Automatic integration with system properties and environment variables.

190

191

**Environment Variables:**

192

```hocon

193

# Environment variables are available automatically

194

database.host = ${?DATABASE_HOST}

195

database.port = ${?DATABASE_PORT}

196

app.debug = ${?DEBUG_MODE}

197

```

198

199

**System Properties:**

200

```hocon

201

# System properties are available automatically

202

jvm.max-heap = ${?java.max.heap}

203

user.home = ${user.home}

204

temp.dir = ${java.io.tmpdir}

205

```

206

207

**Usage Examples:**

208

209

```java

210

// Set environment variables (in shell or process builder)

211

// export DATABASE_HOST=prod.db.example.com

212

// export DATABASE_PORT=5432

213

214

// Set system properties

215

System.setProperty("app.environment", "production");

216

System.setProperty("log.level", "INFO");

217

218

// Configuration automatically picks up system values

219

Config config = ConfigFactory.load();

220

```

221

222

## Custom Resolvers

223

224

### ConfigResolver Interface

225

226

Create custom substitution resolvers for specialized value sources.

227

228

```java { .api }

229

public interface ConfigResolver {

230

ConfigValue lookup(String path);

231

ConfigResolver withFallback(ConfigResolver fallback);

232

}

233

```

234

235

**Implementation Examples:**

236

237

```java

238

// Custom resolver for external service configuration

239

public class ServiceConfigResolver implements ConfigResolver {

240

private final ConfigService configService;

241

242

public ServiceConfigResolver(ConfigService service) {

243

this.configService = service;

244

}

245

246

@Override

247

public ConfigValue lookup(String path) {

248

try {

249

String value = configService.getValue(path);

250

if (value != null) {

251

return ConfigValueFactory.fromAnyRef(value, "service:" + path);

252

}

253

} catch (Exception e) {

254

// Log error but don't fail resolution

255

}

256

return null; // Path not found in this resolver

257

}

258

259

@Override

260

public ConfigResolver withFallback(ConfigResolver fallback) {

261

return new FallbackConfigResolver(this, fallback);

262

}

263

}

264

265

// Usage

266

ConfigResolver serviceResolver = new ServiceConfigResolver(myConfigService);

267

ConfigResolveOptions options = ConfigResolveOptions.defaults()

268

.appendResolver(serviceResolver);

269

270

Config resolved = config.resolve(options);

271

```

272

273

### Resolver Chaining

274

275

Chain multiple resolvers with fallback behavior.

276

277

```java

278

// Chain resolvers in priority order

279

ConfigResolver primary = new DatabaseConfigResolver();

280

ConfigResolver secondary = new FileConfigResolver();

281

ConfigResolver tertiary = new DefaultConfigResolver();

282

283

ConfigResolver chain = primary

284

.withFallback(secondary)

285

.withFallback(tertiary);

286

287

ConfigResolveOptions options = ConfigResolveOptions.defaults()

288

.appendResolver(chain);

289

```

290

291

## Error Handling

292

293

### Resolution Exceptions

294

295

Handle various resolution error conditions.

296

297

```java { .api }

298

public static class ConfigException.UnresolvedSubstitution extends ConfigException {

299

public String path();

300

public String detail();

301

}

302

303

public static class ConfigException.NotResolved extends ConfigException {

304

public String getMessage();

305

}

306

```

307

308

**Error Handling Examples:**

309

310

```java

311

try {

312

Config resolved = config.resolve();

313

} catch (ConfigException.UnresolvedSubstitution e) {

314

// Handle unresolved substitution

315

String problematicPath = e.path();

316

String details = e.detail();

317

System.err.println("Cannot resolve ${" + problematicPath + "}: " + details);

318

}

319

320

// Check resolution status before accessing values

321

if (!config.isResolved()) {

322

try {

323

config.resolve();

324

} catch (ConfigException.UnresolvedSubstitution e) {

325

// Handle or allow partial resolution

326

Config partial = config.resolve(

327

ConfigResolveOptions.defaults().setAllowUnresolved(true)

328

);

329

}

330

}

331

```

332

333

## Advanced Resolution Patterns

334

335

### Conditional Resolution

336

337

Use substitutions for conditional configuration.

338

339

```hocon

340

# Environment-specific configuration

341

environment = "development"

342

environment = ${?APP_ENVIRONMENT}

343

344

# Conditional database settings

345

database = {

346

development {

347

host = "localhost"

348

port = 5432

349

}

350

production {

351

host = "prod.db.example.com"

352

port = 5432

353

}

354

}

355

356

# Select configuration based on environment

357

current-db = ${database.${environment}}

358

```

359

360

### Template Resolution

361

362

Create configuration templates with substitution placeholders.

363

364

```hocon

365

# Template configuration

366

service-template = {

367

host = ${service.host}

368

port = ${service.port}

369

health-check = "http://"${service.host}":"${service.port}"/health"

370

metrics = "http://"${service.host}":"${service.port}"/metrics"

371

}

372

373

# Specific service configurations

374

services {

375

user-service = ${service-template} {

376

service.host = "user.service.internal"

377

service.port = 8080

378

}

379

380

order-service = ${service-template} {

381

service.host = "order.service.internal"

382

service.port = 8081

383

}

384

}

385

```

386

387

### Cross-Configuration Resolution

388

389

Resolve substitutions across multiple configuration sources.

390

391

```java

392

// Base configuration with substitutions

393

Config baseConfig = ConfigFactory.parseString("""

394

app {

395

name = ${app.metadata.name}

396

version = ${app.metadata.version}

397

database.url = ${database.connection.url}

398

}

399

""");

400

401

// Metadata configuration

402

Config metadata = ConfigFactory.parseString("""

403

app.metadata {

404

name = "MyApplication"

405

version = "2.1.0"

406

}

407

""");

408

409

// Database configuration

410

Config dbConfig = ConfigFactory.parseString("""

411

database.connection {

412

url = "jdbc:postgresql://localhost:5432/myapp"

413

}

414

""");

415

416

// Combine and resolve

417

Config combined = baseConfig

418

.withFallback(metadata)

419

.withFallback(dbConfig)

420

.resolve();

421

```

422

423

## Best Practices

424

425

1. **Use optional substitutions**: Prefer `${?path}` for environment variables and optional settings

426

2. **Provide fallback values**: Always include default values for optional substitutions

427

3. **Validate resolution**: Check `isResolved()` before using configuration in production

428

4. **Handle resolution errors**: Catch and handle `ConfigException.UnresolvedSubstitution` appropriately

429

5. **Order resolver priority**: Place most specific resolvers first in the chain

430

6. **Cache resolved configs**: Resolution is expensive, cache the results when possible

431

7. **Use meaningful origins**: Provide descriptive origin information in custom resolvers

432

8. **Test substitution scenarios**: Verify behavior with missing, null, and circular references