0
# Build Integration
1
2
Utilities for integrating with build tools and generating build metadata for Spring Boot Actuator endpoints. The build integration system provides seamless integration with Maven, Gradle, and other build systems while generating runtime metadata for monitoring and management.
3
4
## Capabilities
5
6
### Build Properties Writer
7
8
Utility class for generating build-info.properties files consumed by Spring Boot Actuator's info endpoint.
9
10
```java { .api }
11
public final class BuildPropertiesWriter {
12
/**
13
* Create a build properties writer for the specified output file.
14
*
15
* @param outputFile the file where build properties will be written
16
*/
17
public BuildPropertiesWriter(File outputFile);
18
19
/**
20
* Write build properties for the given project details.
21
* Creates a properties file compatible with Spring Boot Actuator's info endpoint.
22
*
23
* @param projectDetails the project details to write
24
* @throws IOException if writing fails
25
*/
26
public void writeBuildProperties(ProjectDetails projectDetails) throws IOException;
27
28
/**
29
* Project details containing build metadata.
30
*/
31
public static final class ProjectDetails {
32
// Contains project information like name, version, build time, etc.
33
// Used to generate build-info.properties for actuator endpoints
34
}
35
36
/**
37
* Exception thrown when an additional property has a null value.
38
*/
39
public static class NullAdditionalPropertyValueException extends IllegalArgumentException {
40
public NullAdditionalPropertyValueException(String name);
41
}
42
}
43
```
44
45
### Java Executable
46
47
Utility for locating and working with Java executables in the runtime environment.
48
49
```java { .api }
50
public class JavaExecutable {
51
// Provides access to the Java executable used to run the current process
52
// Useful for spawning Java processes with consistent JVM configuration
53
}
54
```
55
56
### Process Execution
57
58
Utility for running external processes, commonly used for build tool integration and system commands.
59
60
```java { .api }
61
public class RunProcess {
62
/**
63
* Run an external process with the specified arguments.
64
*
65
* @param waitForProcess whether to wait for the process to complete
66
* @param args the command line arguments
67
* @return the process exit code (if waitForProcess is true)
68
* @throws IOException if process execution fails
69
*/
70
public int run(boolean waitForProcess, String... args) throws IOException;
71
}
72
```
73
74
## Usage Examples
75
76
### Basic Build Properties Generation
77
78
```java
79
import org.springframework.boot.loader.tools.BuildPropertiesWriter;
80
import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetails;
81
import java.io.File;
82
import java.io.IOException;
83
import java.time.Instant;
84
import java.util.Properties;
85
86
// Create build properties for actuator endpoint
87
File outputFile = new File("target/classes/META-INF/build-info.properties");
88
BuildPropertiesWriter writer = new BuildPropertiesWriter(outputFile);
89
90
// Create project details
91
ProjectDetails details = new ProjectDetails() {{
92
setGroup("com.example");
93
setArtifact("myapp");
94
setName("My Application");
95
setVersion("1.0.0");
96
setTime(Instant.now());
97
98
// Add custom build information
99
getAdditional().put("build.user", System.getProperty("user.name"));
100
getAdditional().put("build.java.version", System.getProperty("java.version"));
101
getAdditional().put("build.os", System.getProperty("os.name"));
102
}};
103
104
try {
105
writer.writeBuildProperties(details);
106
System.out.println("Build properties written to: " + outputFile.getAbsolutePath());
107
} catch (IOException e) {
108
System.err.println("Failed to write build properties: " + e.getMessage());
109
}
110
111
// The generated file will be available at /actuator/info endpoint:
112
// {
113
// "build": {
114
// "group": "com.example",
115
// "artifact": "myapp",
116
// "name": "My Application",
117
// "version": "1.0.0",
118
// "time": "2024-01-15T10:30:00.000Z",
119
// "build.user": "developer",
120
// "build.java.version": "17.0.1",
121
// "build.os": "Linux"
122
// }
123
// }
124
```
125
126
### Maven Integration
127
128
```java
129
import org.springframework.boot.loader.tools.*;
130
import java.io.File;
131
import java.io.IOException;
132
import java.time.Instant;
133
import java.util.Properties;
134
135
// Maven plugin integration example
136
public class MavenBuildPropertiesGenerator {
137
138
public void generateBuildProperties(MavenProject project, File outputFile) throws IOException {
139
BuildPropertiesWriter writer = new BuildPropertiesWriter(outputFile);
140
141
ProjectDetails details = new ProjectDetails();
142
details.setGroup(project.getGroupId());
143
details.setArtifact(project.getArtifactId());
144
details.setName(project.getName());
145
details.setVersion(project.getVersion());
146
details.setTime(Instant.now());
147
148
// Add Maven-specific properties
149
Properties additional = details.getAdditional();
150
additional.put("build.maven.version", getMavenVersion());
151
additional.put("build.java.target", project.getProperties().getProperty("maven.compiler.target"));
152
additional.put("build.java.source", project.getProperties().getProperty("maven.compiler.source"));
153
154
// Add SCM information if available
155
if (project.getScm() != null) {
156
additional.put("build.scm.url", project.getScm().getUrl());
157
additional.put("build.scm.connection", project.getScm().getConnection());
158
}
159
160
// Add CI/CD information
161
String buildNumber = System.getenv("BUILD_NUMBER");
162
if (buildNumber != null) {
163
additional.put("build.number", buildNumber);
164
}
165
166
String gitCommit = System.getenv("GIT_COMMIT");
167
if (gitCommit != null) {
168
additional.put("build.git.commit", gitCommit);
169
}
170
171
writer.writeBuildProperties(details);
172
}
173
174
private String getMavenVersion() {
175
// Implementation to get Maven version
176
return "3.8.1";
177
}
178
}
179
```
180
181
### Gradle Integration
182
183
```java
184
import org.springframework.boot.loader.tools.*;
185
import java.io.File;
186
import java.io.IOException;
187
import java.time.Instant;
188
import java.util.Properties;
189
190
// Gradle plugin integration example
191
public class GradleBuildPropertiesGenerator {
192
193
public void generateBuildProperties(String group, String name, String version,
194
File outputFile, Properties gradleProperties) throws IOException {
195
BuildPropertiesWriter writer = new BuildPropertiesWriter(outputFile);
196
197
ProjectDetails details = new ProjectDetails();
198
details.setGroup(group);
199
details.setArtifact(name);
200
details.setName(name);
201
details.setVersion(version);
202
details.setTime(Instant.now());
203
204
// Add Gradle-specific properties
205
Properties additional = details.getAdditional();
206
additional.put("build.gradle.version", getGradleVersion());
207
208
// Add project properties
209
gradleProperties.forEach((key, value) -> {
210
if (key.toString().startsWith("build.")) {
211
additional.put(key.toString(), value.toString());
212
}
213
});
214
215
// Add system information
216
additional.put("build.java.vendor", System.getProperty("java.vendor"));
217
additional.put("build.java.vm.name", System.getProperty("java.vm.name"));
218
219
writer.writeBuildProperties(details);
220
}
221
222
private String getGradleVersion() {
223
// Implementation to get Gradle version
224
return "7.6";
225
}
226
}
227
```
228
229
### Custom Build Information
230
231
```java
232
import org.springframework.boot.loader.tools.*;
233
import java.io.File;
234
import java.io.IOException;
235
import java.time.Instant;
236
import java.time.ZoneId;
237
import java.time.format.DateTimeFormatter;
238
import java.util.Properties;
239
240
// Generate comprehensive build information
241
public class ComprehensiveBuildInfo {
242
243
public void generateBuildInfo(String group, String artifact, String version,
244
File outputFile) throws IOException {
245
BuildPropertiesWriter writer = new BuildPropertiesWriter(outputFile);
246
247
ProjectDetails details = new ProjectDetails();
248
details.setGroup(group);
249
details.setArtifact(artifact);
250
details.setName(artifact);
251
details.setVersion(version);
252
details.setTime(Instant.now());
253
254
Properties additional = details.getAdditional();
255
256
// Build environment
257
additional.put("build.user", System.getProperty("user.name"));
258
additional.put("build.host", getHostname());
259
additional.put("build.time.formatted",
260
DateTimeFormatter.ISO_LOCAL_DATE_TIME
261
.withZone(ZoneId.systemDefault())
262
.format(Instant.now()));
263
264
// Java environment
265
additional.put("build.java.version", System.getProperty("java.version"));
266
additional.put("build.java.vendor", System.getProperty("java.vendor"));
267
additional.put("build.java.home", System.getProperty("java.home"));
268
269
// Operating system
270
additional.put("build.os.name", System.getProperty("os.name"));
271
additional.put("build.os.version", System.getProperty("os.version"));
272
additional.put("build.os.arch", System.getProperty("os.arch"));
273
274
// Runtime information
275
Runtime runtime = Runtime.getRuntime();
276
additional.put("build.runtime.processors", String.valueOf(runtime.availableProcessors()));
277
additional.put("build.runtime.max.memory", String.valueOf(runtime.maxMemory()));
278
279
// Custom application properties
280
additional.put("build.profile", System.getProperty("spring.profiles.active", "default"));
281
additional.put("build.type", "executable-jar");
282
283
try {
284
writer.writeBuildProperties(details);
285
System.out.println("Comprehensive build info written to: " + outputFile);
286
} catch (BuildPropertiesWriter.NullAdditionalPropertyValueException e) {
287
System.err.println("Null value detected for property: " + e.getMessage());
288
throw e;
289
}
290
}
291
292
private String getHostname() {
293
try {
294
return java.net.InetAddress.getLocalHost().getHostName();
295
} catch (Exception e) {
296
return "unknown";
297
}
298
}
299
}
300
```
301
302
### Process Execution for Build Tools
303
304
```java
305
import org.springframework.boot.loader.tools.RunProcess;
306
import java.io.IOException;
307
import java.util.Arrays;
308
309
// Execute build commands and capture results
310
public class BuildToolRunner {
311
private final RunProcess processRunner = new RunProcess();
312
313
public void runMavenBuild(String... goals) throws IOException {
314
String[] command = new String[goals.length + 1];
315
command[0] = "mvn";
316
System.arraycopy(goals, 0, command, 1, goals.length);
317
318
System.out.println("Executing: " + Arrays.toString(command));
319
int exitCode = processRunner.run(true, command);
320
321
if (exitCode != 0) {
322
throw new IOException("Maven build failed with exit code: " + exitCode);
323
}
324
325
System.out.println("Maven build completed successfully");
326
}
327
328
public void runGradleBuild(String... tasks) throws IOException {
329
String[] command = new String[tasks.length + 1];
330
command[0] = "./gradlew";
331
System.arraycopy(tasks, 0, command, 1, tasks.length);
332
333
System.out.println("Executing: " + Arrays.toString(command));
334
int exitCode = processRunner.run(true, command);
335
336
if (exitCode != 0) {
337
throw new IOException("Gradle build failed with exit code: " + exitCode);
338
}
339
340
System.out.println("Gradle build completed successfully");
341
}
342
343
public void runGitCommands() throws IOException {
344
// Get git information for build metadata
345
int exitCode;
346
347
// Get current commit hash
348
exitCode = processRunner.run(true, "git", "rev-parse", "HEAD");
349
if (exitCode != 0) {
350
System.out.println("Warning: Could not get git commit hash");
351
}
352
353
// Get current branch
354
exitCode = processRunner.run(true, "git", "rev-parse", "--abbrev-ref", "HEAD");
355
if (exitCode != 0) {
356
System.out.println("Warning: Could not get git branch");
357
}
358
359
// Check if working directory is clean
360
exitCode = processRunner.run(true, "git", "diff-index", "--quiet", "HEAD", "--");
361
if (exitCode != 0) {
362
System.out.println("Warning: Working directory has uncommitted changes");
363
}
364
}
365
}
366
367
// Usage
368
BuildToolRunner runner = new BuildToolRunner();
369
try {
370
runner.runGitCommands();
371
runner.runMavenBuild("clean", "compile", "test");
372
// or
373
// runner.runGradleBuild("clean", "build");
374
} catch (IOException e) {
375
System.err.println("Build process failed: " + e.getMessage());
376
}
377
```
378
379
### Actuator Integration
380
381
```java
382
import org.springframework.boot.loader.tools.*;
383
import java.io.File;
384
import java.io.IOException;
385
import java.time.Instant;
386
import java.util.Properties;
387
388
// Generate build properties specifically for Spring Boot Actuator
389
public class ActuatorBuildInfoGenerator {
390
391
public void generateForActuator(ProjectDetails projectDetails, File outputFile) throws IOException {
392
// Ensure output directory exists
393
outputFile.getParentFile().mkdirs();
394
395
BuildPropertiesWriter writer = new BuildPropertiesWriter(outputFile);
396
397
// Add actuator-specific properties
398
Properties additional = projectDetails.getAdditional();
399
400
// Health check information
401
additional.put("build.health.check.url", "/actuator/health");
402
additional.put("build.info.url", "/actuator/info");
403
additional.put("build.metrics.url", "/actuator/metrics");
404
405
// Build pipeline information
406
String pipelineId = System.getenv("CI_PIPELINE_ID");
407
if (pipelineId != null) {
408
additional.put("build.pipeline.id", pipelineId);
409
}
410
411
String buildUrl = System.getenv("BUILD_URL");
412
if (buildUrl != null) {
413
additional.put("build.url", buildUrl);
414
}
415
416
// Application configuration
417
additional.put("build.spring.boot.version", getSpringBootVersion());
418
additional.put("build.spring.version", getSpringVersion());
419
420
writer.writeBuildProperties(projectDetails);
421
422
System.out.println("Actuator build info generated: " + outputFile.getAbsolutePath());
423
System.out.println("Available at: /actuator/info");
424
}
425
426
private String getSpringBootVersion() {
427
// Get Spring Boot version from classpath
428
Package pkg = org.springframework.boot.SpringBootVersion.class.getPackage();
429
return pkg != null ? pkg.getImplementationVersion() : "unknown";
430
}
431
432
private String getSpringVersion() {
433
// Get Spring Framework version
434
Package pkg = org.springframework.core.SpringVersion.class.getPackage();
435
return pkg != null ? pkg.getImplementationVersion() : "unknown";
436
}
437
}
438
```
439
440
### Build Properties Validation
441
442
```java
443
import org.springframework.boot.loader.tools.*;
444
import java.io.File;
445
import java.io.FileInputStream;
446
import java.io.IOException;
447
import java.util.Properties;
448
449
// Validate generated build properties
450
public class BuildPropertiesValidator {
451
452
public void validateBuildProperties(File buildPropertiesFile) throws IOException {
453
if (!buildPropertiesFile.exists()) {
454
throw new IOException("Build properties file not found: " + buildPropertiesFile);
455
}
456
457
Properties props = new Properties();
458
try (FileInputStream fis = new FileInputStream(buildPropertiesFile)) {
459
props.load(fis);
460
}
461
462
// Validate required properties
463
validateRequired(props, "build.group", "Build group is required");
464
validateRequired(props, "build.artifact", "Build artifact is required");
465
validateRequired(props, "build.version", "Build version is required");
466
validateRequired(props, "build.time", "Build time is required");
467
468
// Validate property formats
469
validateVersion(props.getProperty("build.version"));
470
validateTime(props.getProperty("build.time"));
471
472
System.out.println("Build properties validation passed");
473
474
// Print summary
475
System.out.println("Build Properties Summary:");
476
System.out.println("========================");
477
props.forEach((key, value) -> {
478
if (key.toString().startsWith("build.")) {
479
System.out.println(key + " = " + value);
480
}
481
});
482
}
483
484
private void validateRequired(Properties props, String key, String message) throws IOException {
485
if (!props.containsKey(key) || props.getProperty(key).trim().isEmpty()) {
486
throw new IOException(message + ": " + key);
487
}
488
}
489
490
private void validateVersion(String version) throws IOException {
491
if (version == null || !version.matches("\\d+\\.\\d+\\.\\d+.*")) {
492
throw new IOException("Invalid version format: " + version);
493
}
494
}
495
496
private void validateTime(String time) throws IOException {
497
try {
498
Instant.parse(time);
499
} catch (Exception e) {
500
throw new IOException("Invalid build time format: " + time, e);
501
}
502
}
503
}
504
```
505
506
## Integration Patterns
507
508
### Maven Plugin Integration
509
510
```xml
511
<plugin>
512
<groupId>org.springframework.boot</groupId>
513
<artifactId>spring-boot-maven-plugin</artifactId>
514
<version>3.2.0</version>
515
<executions>
516
<execution>
517
<goals>
518
<goal>build-info</goal>
519
</goals>
520
<configuration>
521
<additionalProperties>
522
<build.user>${user.name}</build.user>
523
<build.java.target>${maven.compiler.target}</build.java.target>
524
<build.maven.version>${maven.version}</build.maven.version>
525
</additionalProperties>
526
</configuration>
527
</execution>
528
</executions>
529
</plugin>
530
```
531
532
### Gradle Plugin Integration
533
534
```groovy
535
springBoot {
536
buildInfo {
537
properties {
538
additional = [
539
'build.user': System.getProperty('user.name'),
540
'build.java.target': project.targetCompatibility,
541
'build.gradle.version': gradle.gradleVersion
542
]
543
}
544
}
545
}
546
```
547
548
The build integration system provides comprehensive support for capturing and exposing build metadata, enabling better application monitoring, debugging, and operational visibility through Spring Boot's Actuator endpoints.