or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

build-integration.mdimage-packaging.mdindex.mdjar-writing.mdlaunch-scripts.mdlayer-support.mdlayout-management.mdlibrary-management.mdmain-class-detection.mdrepackaging.md

layer-support.mddocs/

0

# Layer Support for Docker Optimization

1

2

Advanced layering system for optimizing Docker image builds by strategically separating application code, dependencies, and Spring Boot loader components into distinct layers. This enables efficient caching and faster container image builds.

3

4

## Capabilities

5

6

### Layer Definition

7

8

Core class representing a named layer for organizing archive content.

9

10

```java { .api }

11

public class Layer {

12

/**

13

* Create a layer with the specified name.

14

*

15

* @param name the layer name

16

*/

17

public Layer(String name);

18

19

/**

20

* Check if this layer equals another object.

21

*

22

* @param obj the object to compare

23

* @return true if equal

24

*/

25

@Override

26

public boolean equals(Object obj);

27

28

/**

29

* Get the hash code for this layer.

30

*

31

* @return the hash code

32

*/

33

@Override

34

public int hashCode();

35

36

/**

37

* Get the string representation of this layer.

38

*

39

* @return the layer name

40

*/

41

@Override

42

public String toString();

43

}

44

```

45

46

### Layer Interface

47

48

Primary interface for providing layer information during the packaging process.

49

50

```java { .api }

51

public interface Layers extends Iterable<Layer> {

52

/**

53

* Get an iterator over all layers.

54

*

55

* @return iterator for layers

56

*/

57

@Override

58

Iterator<Layer> iterator();

59

60

/**

61

* Get a stream of all layers.

62

*

63

* @return stream of layers

64

*/

65

default Stream<Layer> stream() {

66

return StreamSupport.stream(spliterator(), false);

67

}

68

69

/**

70

* Get the layer for an application resource.

71

*

72

* @param applicationResource the application resource path

73

* @return the layer for this resource

74

*/

75

Layer getLayer(String applicationResource);

76

77

/**

78

* Get the layer for a library.

79

*

80

* @param library the library

81

* @return the layer for this library

82

*/

83

Layer getLayer(Library library);

84

85

/**

86

* Implicit layers implementation that uses default layer assignment.

87

*/

88

Layers IMPLICIT = /* default implementation */;

89

}

90

```

91

92

### Layer Index

93

94

Index file that describes which layer each entry belongs to, used by Docker image builders.

95

96

```java { .api }

97

public class LayersIndex {

98

/**

99

* Create a layers index with the specified layers.

100

*

101

* @param layers the layers to include in the index

102

*/

103

public LayersIndex(Layer... layers);

104

105

/**

106

* Create a layers index from an iterable of layers.

107

*

108

* @param layers the layers to include

109

*/

110

public LayersIndex(Iterable<Layer> layers);

111

112

/**

113

* Add an entry to the specified layer.

114

*

115

* @param layer the layer to add the entry to

116

* @param name the entry name/path

117

*/

118

public void add(Layer layer, String name);

119

120

/**

121

* Write the index to an output stream.

122

* The index format is compatible with Spring Boot's layered JAR structure.

123

*

124

* @param out the output stream to write to

125

* @throws IOException if writing fails

126

*/

127

public void writeTo(OutputStream out) throws IOException;

128

}

129

```

130

131

### Standard Layers

132

133

Pre-defined layer implementation following Spring Boot conventions for optimal Docker caching.

134

135

```java { .api }

136

public abstract class StandardLayers implements Layers {

137

/**

138

* Layer for external dependencies.

139

* Changes infrequently, provides good caching.

140

*/

141

public static final Layer DEPENDENCIES = new Layer("dependencies");

142

143

/**

144

* Layer for Spring Boot loader classes.

145

* Changes only with Spring Boot version updates.

146

*/

147

public static final Layer SPRING_BOOT_LOADER = new Layer("spring-boot-loader");

148

149

/**

150

* Layer for snapshot dependencies.

151

* Separated from stable dependencies for better cache efficiency.

152

*/

153

public static final Layer SNAPSHOT_DEPENDENCIES = new Layer("snapshot-dependencies");

154

155

/**

156

* Layer for application code.

157

* Changes frequently, placed in top layer.

158

*/

159

public static final Layer APPLICATION = new Layer("application");

160

}

161

```

162

163

### Implicit Layer Resolver

164

165

Default implementation that automatically assigns layers based on content analysis.

166

167

```java { .api }

168

public class ImplicitLayerResolver extends StandardLayers {

169

// Automatically determines appropriate layer for each component

170

// Uses heuristics to optimize Docker image layer efficiency

171

}

172

```

173

174

### Custom Layers

175

176

User-defined layer configuration with custom selection logic for fine-grained control over layer assignment.

177

178

```java { .api }

179

public class CustomLayers implements Layers {

180

/**

181

* Create custom layers with specified selectors.

182

*

183

* @param layers the ordered list of layers

184

* @param applicationSelectors selectors for application resources

185

* @param librarySelectors selectors for library resources

186

*/

187

public CustomLayers(List<Layer> layers,

188

List<ContentSelector<String>> applicationSelectors,

189

List<ContentSelector<Library>> librarySelectors);

190

}

191

```

192

193

### Content Selection

194

195

Strategy interface for determining which layer content should be assigned to.

196

197

```java { .api }

198

public interface ContentSelector<T> {

199

/**

200

* Get the target layer for selected content.

201

*

202

* @return the layer

203

*/

204

Layer getLayer();

205

206

/**

207

* Check if this selector contains the specified item.

208

*

209

* @param item the item to check

210

* @return true if the item belongs to this selector's layer

211

*/

212

boolean contains(T item);

213

}

214

```

215

216

### Include/Exclude Content Selector

217

218

Pattern-based content selector using include and exclude rules.

219

220

```java { .api }

221

public class IncludeExcludeContentSelector<T> implements ContentSelector<T> {

222

// Implementation uses pattern matching for flexible content selection

223

// Supports wildcards and regular expressions

224

}

225

```

226

227

### Content Filters

228

229

Filter interfaces for different types of content.

230

231

```java { .api }

232

public interface ContentFilter<T> {

233

// Base interface for content filtering

234

}

235

236

public class ApplicationContentFilter implements ContentFilter<String> {

237

// Filter for application resources (class files, properties, etc.)

238

}

239

240

public class LibraryContentFilter implements ContentFilter<Library> {

241

// Filter for library dependencies

242

}

243

```

244

245

## Usage Examples

246

247

### Basic Layer Configuration

248

249

```java

250

import org.springframework.boot.loader.tools.*;

251

import java.io.File;

252

253

// Use standard layers for optimal Docker caching

254

File sourceJar = new File("myapp.jar");

255

Repackager repackager = new Repackager(sourceJar);

256

257

// Enable layered packaging

258

repackager.setLayers(Layers.IMPLICIT);

259

260

// Repackage with layer support

261

repackager.repackage(Libraries.NONE);

262

263

// The resulting JAR will include BOOT-INF/layers.idx

264

// Docker buildpacks can use this for efficient layer creation

265

```

266

267

### Custom Layer Configuration

268

269

```java

270

import org.springframework.boot.loader.tools.*;

271

import org.springframework.boot.loader.tools.layer.*;

272

import java.io.File;

273

import java.util.List;

274

275

// Define custom layers

276

Layer infrastructureLayer = new Layer("infrastructure");

277

Layer businessLogicLayer = new Layer("business-logic");

278

Layer configurationLayer = new Layer("configuration");

279

280

// Create content selectors

281

List<ContentSelector<String>> applicationSelectors = List.of(

282

new IncludeExcludeContentSelector<>(configurationLayer,

283

List.of("**/*.properties", "**/*.yml", "**/*.xml"),

284

List.of()),

285

new IncludeExcludeContentSelector<>(businessLogicLayer,

286

List.of("**/service/**", "**/business/**"),

287

List.of()),

288

new IncludeExcludeContentSelector<>(infrastructureLayer,

289

List.of("**"), // everything else

290

List.of())

291

);

292

293

List<ContentSelector<Library>> librarySelectors = List.of(

294

new IncludeExcludeContentSelector<>(infrastructureLayer,

295

List.of("org.springframework.*", "org.hibernate.*"),

296

List.of()),

297

new IncludeExcludeContentSelector<>(businessLogicLayer,

298

List.of("com.example.business.*"),

299

List.of())

300

);

301

302

// Create custom layers configuration

303

Layers customLayers = new CustomLayers(

304

List.of(infrastructureLayer, businessLogicLayer, configurationLayer),

305

applicationSelectors,

306

librarySelectors

307

);

308

309

// Apply to repackager

310

Repackager repackager = new Repackager(new File("myapp.jar"));

311

repackager.setLayers(customLayers);

312

repackager.repackage(Libraries.NONE);

313

```

314

315

### Layer Index Generation

316

317

```java

318

import org.springframework.boot.loader.tools.*;

319

import java.io.File;

320

import java.io.FileOutputStream;

321

import java.io.IOException;

322

323

// Manually create and write a layer index

324

Layer appLayer = new Layer("application");

325

Layer libLayer = new Layer("dependencies");

326

Layer loaderLayer = new Layer("spring-boot-loader");

327

328

LayersIndex index = new LayersIndex(appLayer, libLayer, loaderLayer);

329

330

// Add entries to layers

331

index.add(appLayer, "BOOT-INF/classes/com/example/Application.class");

332

index.add(appLayer, "BOOT-INF/classes/com/example/service/UserService.class");

333

index.add(libLayer, "BOOT-INF/lib/spring-boot-3.2.0.jar");

334

index.add(libLayer, "BOOT-INF/lib/logback-classic-1.4.5.jar");

335

index.add(loaderLayer, "org/springframework/boot/loader/launch/JarLauncher.class");

336

337

// Write index to file

338

File indexFile = new File("layers.idx");

339

try (FileOutputStream fos = new FileOutputStream(indexFile)) {

340

index.writeTo(fos);

341

}

342

343

// The index file can be included in the JAR at BOOT-INF/layers.idx

344

```

345

346

### Integration with Docker Buildpacks

347

348

```java

349

import org.springframework.boot.loader.tools.*;

350

import java.io.File;

351

352

// Configure layers for optimal buildpack performance

353

public class BuildpackOptimizedLayers extends StandardLayers {

354

// Additional custom layers for buildpack optimization

355

public static final Layer BUILDPACK_CACHE = new Layer("buildpack-cache");

356

public static final Layer CONFIGURATION = new Layer("configuration");

357

358

@Override

359

public Layer getLayer(String applicationResource) {

360

// Buildpack-specific caching frequently accessed resources

361

if (applicationResource.endsWith(".properties") ||

362

applicationResource.endsWith(".yml") ||

363

applicationResource.endsWith(".yaml")) {

364

return CONFIGURATION;

365

}

366

367

// Use standard logic for other resources

368

return super.getLayer(applicationResource);

369

}

370

371

@Override

372

public Layer getLayer(Library library) {

373

LibraryCoordinates coords = library.getCoordinates();

374

if (coords != null) {

375

// Separate buildpack-specific libraries

376

if (coords.getGroupId().startsWith("io.buildpacks") ||

377

coords.getGroupId().startsWith("org.cloudfoundry")) {

378

return BUILDPACK_CACHE;

379

}

380

}

381

382

return super.getLayer(library);

383

}

384

}

385

386

// Use with repackager

387

Repackager repackager = new Repackager(new File("myapp.jar"));

388

repackager.setLayers(new BuildpackOptimizedLayers());

389

repackager.repackage(Libraries.NONE);

390

```

391

392

### Layer Analysis and Inspection

393

394

```java

395

import org.springframework.boot.loader.tools.*;

396

import java.io.IOException;

397

import java.util.Map;

398

import java.util.concurrent.ConcurrentHashMap;

399

import java.util.concurrent.atomic.AtomicLong;

400

401

// Analyze layer distribution for optimization

402

public class LayerAnalyzer {

403

private final Map<Layer, AtomicLong> layerSizes = new ConcurrentHashMap<>();

404

private final Map<Layer, AtomicLong> layerCounts = new ConcurrentHashMap<>();

405

406

public void analyzeLayers(Layers layers, Libraries libraries) throws IOException {

407

// Analyze library distribution

408

libraries.doWithLibraries(library -> {

409

Layer layer = layers.getLayer(library);

410

long size = library.getFile().length();

411

412

layerSizes.computeIfAbsent(layer, k -> new AtomicLong(0)).addAndGet(size);

413

layerCounts.computeIfAbsent(layer, k -> new AtomicLong(0)).incrementAndGet();

414

});

415

416

// Print analysis results

417

System.out.println("Layer Analysis:");

418

System.out.println("==============");

419

420

for (Layer layer : layers) {

421

long size = layerSizes.getOrDefault(layer, new AtomicLong(0)).get();

422

long count = layerCounts.getOrDefault(layer, new AtomicLong(0)).get();

423

424

System.out.printf("Layer: %s%n", layer);

425

System.out.printf(" Size: %,d bytes (%.2f MB)%n", size, size / 1024.0 / 1024.0);

426

System.out.printf(" Count: %,d items%n", count);

427

System.out.printf(" Avg Size: %,d bytes%n", count > 0 ? size / count : 0);

428

System.out.println();

429

}

430

}

431

}

432

433

// Usage

434

LayerAnalyzer analyzer = new LayerAnalyzer();

435

analyzer.analyzeLayers(Layers.IMPLICIT, libraries);

436

```

437

438

### Dynamic Layer Assignment

439

440

```java

441

import org.springframework.boot.loader.tools.*;

442

import org.springframework.boot.loader.tools.layer.*;

443

import java.util.List;

444

import java.util.function.Predicate;

445

446

// Dynamic layer assignment based on runtime criteria

447

public class DynamicLayers implements Layers {

448

private final List<Layer> layers;

449

private final Predicate<String> frequentlyChanging;

450

private final Predicate<Library> heavyLibraries;

451

452

public DynamicLayers(Predicate<String> frequentlyChanging, Predicate<Library> heavyLibraries) {

453

this.layers = List.of(

454

StandardLayers.SPRING_BOOT_LOADER,

455

new Layer("heavy-dependencies"),

456

StandardLayers.DEPENDENCIES,

457

new Layer("volatile-app"),

458

StandardLayers.APPLICATION

459

);

460

this.frequentlyChanging = frequentlyChanging;

461

this.heavyLibraries = heavyLibraries;

462

}

463

464

@Override

465

public Iterator<Layer> iterator() {

466

return layers.iterator();

467

}

468

469

@Override

470

public Layer getLayer(String applicationResource) {

471

if (frequentlyChanging.test(applicationResource)) {

472

return new Layer("volatile-app");

473

}

474

return StandardLayers.APPLICATION;

475

}

476

477

@Override

478

public Layer getLayer(Library library) {

479

if (heavyLibraries.test(library)) {

480

return new Layer("heavy-dependencies");

481

}

482

483

LibraryCoordinates coords = library.getCoordinates();

484

if (coords != null && coords.getGroupId().startsWith("org.springframework")) {

485

return StandardLayers.SPRING_BOOT_LOADER;

486

}

487

488

return StandardLayers.DEPENDENCIES;

489

}

490

}

491

492

// Usage with intelligent predicates

493

DynamicLayers dynamicLayers = new DynamicLayers(

494

// Frequently changing application resources

495

resource -> resource.contains("/controller/") ||

496

resource.contains("/service/") ||

497

resource.endsWith(".properties"),

498

499

// Heavy libraries (>10MB)

500

library -> library.getFile().length() > 10 * 1024 * 1024

501

);

502

503

Repackager repackager = new Repackager(new File("myapp.jar"));

504

repackager.setLayers(dynamicLayers);

505

repackager.repackage(Libraries.NONE);

506

```

507

508

## Docker Integration

509

510

### Layer Index Format

511

512

The layers index file (`BOOT-INF/layers.idx`) uses the following format:

513

514

```

515

- "dependencies":

516

- "BOOT-INF/lib/spring-boot-3.2.0.jar"

517

- "BOOT-INF/lib/logback-classic-1.4.5.jar"

518

- "spring-boot-loader":

519

- "org/springframework/boot/loader/"

520

- "application":

521

- "BOOT-INF/classes/"

522

- "META-INF/"

523

```

524

525

### Dockerfile Integration

526

527

```dockerfile

528

# Multi-stage build with layer extraction

529

FROM eclipse-temurin:17-jre as builder

530

WORKDIR application

531

ARG JAR_FILE=target/*.jar

532

COPY ${JAR_FILE} application.jar

533

RUN java -Djarmode=layertools -jar application.jar extract

534

535

# Final image with optimized layers

536

FROM eclipse-temurin:17-jre

537

WORKDIR application

538

539

# Copy layers in order of change frequency (least to most)

540

COPY --from=builder application/dependencies/ ./

541

COPY --from=builder application/spring-boot-loader/ ./

542

COPY --from=builder application/snapshot-dependencies/ ./

543

COPY --from=builder application/application/ ./

544

545

ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

546

```

547

548

This layering strategy ensures that:

549

1. **Dependencies layer** - Cached until dependencies change

550

2. **Spring Boot loader layer** - Cached until Spring Boot version changes

551

3. **Snapshot dependencies layer** - Rebuilt when snapshot versions change

552

4. **Application layer** - Rebuilt on every application code change

553

554

The result is faster Docker builds and more efficient image distribution.