Overview
CircleCI Orbs are reusable packages of configuration that help automate repeated processes. While orbs are typically written in YAML, we can create Java utilities to generate, validate, and work with CircleCI configurations programmatically.
Core Orb Generation Framework
1. CircleCI Configuration Builder
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import java.util.*;
import java.util.stream.Collectors;
public class CircleCIOrbBuilder {
private final String orbName;
private final String orbVersion;
private final Map<String, Object> orbConfig;
private final ObjectMapper yamlMapper;
public CircleCIOrbBuilder(String orbName, String orbVersion) {
this.orbName = orbName;
this.orbVersion = orbVersion;
this.orbConfig = new LinkedHashMap<>();
this.yamlMapper = new ObjectMapper(new YAMLFactory());
initializeOrbStructure();
}
private void initializeOrbStructure() {
orbConfig.put("version", "2.1");
orbConfig.put("orbs", new LinkedHashMap<String, Object>());
orbConfig.put("jobs", new LinkedHashMap<String, Object>());
orbConfig.put("commands", new LinkedHashMap<String, Object>());
orbConfig.put("executors", new LinkedHashMap<String, Object>());
orbConfig.put("parameters", new LinkedHashMap<String, Object>());
}
// Job definition methods
public CircleCIOrbBuilder addJob(String jobName, JobBuilder jobBuilder) {
Map<String, Object> jobs = (Map<String, Object>) orbConfig.get("jobs");
jobs.put(jobName, jobBuilder.build());
return this;
}
public CircleCIOrbBuilder addCommand(String commandName, CommandBuilder commandBuilder) {
Map<String, Object> commands = (Map<String, Object>) orbConfig.get("commands");
commands.put(commandName, commandBuilder.build());
return this;
}
public CircleCIOrbBuilder addExecutor(String executorName, ExecutorBuilder executorBuilder) {
Map<String, Object> executors = (Map<String, Object>) orbConfig.get("executors");
executors.put(executorName, executorBuilder.build());
return this;
}
public CircleCIOrbBuilder addParameter(String paramName, ParameterBuilder paramBuilder) {
Map<String, Object> parameters = (Map<String, Object>) orbConfig.get("parameters");
parameters.put(paramName, paramBuilder.build());
return this;
}
public String toYaml() {
try {
return yamlMapper.writeValueAsString(orbConfig);
} catch (Exception e) {
throw new RuntimeException("Failed to generate YAML configuration", e);
}
}
public void saveToFile(String filePath) {
try {
yamlMapper.writeValue(new java.io.File(filePath), orbConfig);
} catch (Exception e) {
throw new RuntimeException("Failed to save configuration to file", e);
}
}
// Builder classes for different orb components
public static class JobBuilder {
private final Map<String, Object> jobConfig = new LinkedHashMap<>();
public JobBuilder docker(String image) {
List<Map<String, String>> dockerConfig = new ArrayList<>();
dockerConfig.add(Map.of("image", image));
jobConfig.put("docker", dockerConfig);
return this;
}
public JobBuilder machine(boolean enable) {
if (enable) {
jobConfig.put("machine", Map.of("enabled", true));
}
return this;
}
public JobBuilder executor(String executorName) {
jobConfig.put("executor", executorName);
return this;
}
public JobBuilder workingDirectory(String directory) {
jobConfig.put("working_directory", directory);
return this;
}
public JobBuilder addStep(String stepName, Map<String, Object> stepConfig) {
if (!jobConfig.containsKey("steps")) {
jobConfig.put("steps", new ArrayList<Map<String, Object>>());
}
List<Map<String, Object>> steps = (List<Map<String, Object>>) jobConfig.get("steps");
steps.add(Map.of(stepName, stepConfig));
return this;
}
public JobBuilder addRunStep(String name, String command) {
return addStep("run", Map.of(
"name", name,
"command", command
));
}
public JobBuilder addCheckoutStep() {
return addStep("checkout", Collections.emptyMap());
}
public JobBuilder addOrbStep(String orb, String command, Map<String, Object> parameters) {
Map<String, Object> orbStep = new LinkedHashMap<>();
orbStep.put("command", orb + "/" + command);
if (parameters != null && !parameters.isEmpty()) {
orbStep.put("parameters", parameters);
}
return addStep(orb, orbStep);
}
public Map<String, Object> build() {
return jobConfig;
}
}
public static class CommandBuilder {
private final Map<String, Object> commandConfig = new LinkedHashMap<>();
private final List<Map<String, Object>> steps = new ArrayList<>();
private final Map<String, Object> parameters = new LinkedHashMap<>();
public CommandBuilder description(String description) {
commandConfig.put("description", description);
return this;
}
public CommandBuilder addParameter(String name, ParameterBuilder paramBuilder) {
parameters.put(name, paramBuilder.build());
return this;
}
public CommandBuilder addStep(String stepName, Map<String, Object> stepConfig) {
steps.add(Map.of(stepName, stepConfig));
return this;
}
public CommandBuilder addRunStep(String name, String command) {
return addStep("run", Map.of(
"name", name,
"command", command
));
}
public Map<String, Object> build() {
if (!parameters.isEmpty()) {
commandConfig.put("parameters", parameters);
}
commandConfig.put("steps", steps);
return commandConfig;
}
}
public static class ExecutorBuilder {
private final Map<String, Object> executorConfig = new LinkedHashMap<>();
public ExecutorBuilder docker(String image) {
executorConfig.put("docker", List.of(Map.of("image", image)));
return this;
}
public ExecutorBuilder resourceClass(String resourceClass) {
executorConfig.put("resource_class", resourceClass);
return this;
}
public ExecutorBuilder workingDirectory(String directory) {
executorConfig.put("working_directory", directory);
return this;
}
public ExecutorBuilder environment(Map<String, String> envVars) {
executorConfig.put("environment", envVars);
return this;
}
public Map<String, Object> build() {
return executorConfig;
}
}
public static class ParameterBuilder {
private final Map<String, Object> paramConfig = new LinkedHashMap<>();
private final String type;
public ParameterBuilder(String type) {
this.type = type;
paramConfig.put("type", type);
}
public ParameterBuilder description(String description) {
paramConfig.put("description", description);
return this;
}
public ParameterBuilder defaultValue(Object defaultValue) {
paramConfig.put("default", defaultValue);
return this;
}
public Map<String, Object> build() {
return paramConfig;
}
}
}
2. Java-Specific Orb Generators
public class JavaOrbGenerator {
private final CircleCIOrbBuilder orbBuilder;
public JavaOrbGenerator(String orbName, String orbVersion) {
this.orbBuilder = new CircleCIOrbBuilder(orbName, orbVersion);
}
public String generateJavaOrb() {
// Add common executors
addJavaExecutors();
// Add Maven commands and jobs
addMavenCommands();
addMavenJobs();
// Add Gradle commands and jobs
addGradleCommands();
addGradleJobs();
// Add utility commands
addUtilityCommands();
return orbBuilder.toYaml();
}
private void addJavaExecutors() {
// Java 11 executor
orbBuilder.addExecutor("java11", new CircleCIOrbBuilder.ExecutorBuilder()
.docker("cimg/openjdk:11.0")
.resourceClass("medium")
.workingDirectory("~/project"));
// Java 17 executor
orbBuilder.addExecutor("java17", new CircleCIOrbBuilder.ExecutorBuilder()
.docker("cimg/openjdk:17.0")
.resourceClass("medium")
.workingDirectory("~/project"));
// Java 21 executor
orbBuilder.addExecutor("java21", new CircleCIOrbBuilder.ExecutorBuilder()
.docker("cimg/openjdk:21.0")
.resourceClass("medium")
.workingDirectory("~/project"));
}
private void addMavenCommands() {
// Maven test command
orbBuilder.addCommand("maven-test", new CircleCIOrbBuilder.CommandBuilder()
.description("Run Maven tests")
.addParameter("test-command", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue("test"))
.addParameter("extra-args", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue(""))
.addRunStep("Run Tests", "mvn << parameters.test-command >> << parameters.extra-args >>"));
// Maven build command
orbBuilder.addCommand("maven-build", new CircleCIOrbBuilder.CommandBuilder()
.description("Build Maven project")
.addParameter("build-command", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue("clean compile"))
.addParameter("extra-args", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue(""))
.addRunStep("Build Project", "mvn << parameters.build-command >> << parameters.extra-args >>"));
// Maven dependency check
orbBuilder.addCommand("maven-dependency-check", new CircleCIOrbBuilder.CommandBuilder()
.description("Check for dependency updates")
.addRunStep("Check Dependencies", "mvn versions:display-dependency-updates"));
}
private void addMavenJobs() {
// Maven test job
orbBuilder.addJob("maven-test", new CircleCIOrbBuilder.JobBuilder()
.executor("java11")
.addCheckoutStep()
.addStep("maven-test", Map.of(
"command", "java-orb/maven-test",
"extra-args", "-DskipTests=false"
))
.addRunStep("Store Test Results", "mkdir -p test-results/junit && find . -type f -regex \".*/target/surefire-reports/.*xml\" -exec cp {} test-results/junit/ \\;")
.addStep("store_test_results", Map.of("path", "test-results"))
.addStep("store_artifacts", Map.of("path", "test-results")));
// Maven build job
orbBuilder.addJob("maven-build", new CircleCIOrbBuilder.JobBuilder()
.executor("java11")
.addCheckoutStep()
.addStep("maven-build", Map.of("command", "java-orb/maven-build"))
.addRunStep("Store Artifacts", "mkdir -p artifacts && cp target/*.jar artifacts/")
.addStep("store_artifacts", Map.of("path", "artifacts")));
}
private void addGradleCommands() {
// Gradle test command
orbBuilder.addCommand("gradle-test", new CircleCIOrbBuilder.CommandBuilder()
.description("Run Gradle tests")
.addParameter("test-task", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue("test"))
.addParameter("extra-args", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue(""))
.addRunStep("Run Tests", "./gradlew << parameters.test-task >> << parameters.extra-args >>"));
// Gradle build command
orbBuilder.addCommand("gradle-build", new CircleCIOrbBuilder.CommandBuilder()
.description("Build Gradle project")
.addParameter("build-task", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue("build"))
.addParameter("extra-args", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue(""))
.addRunStep("Build Project", "./gradlew << parameters.build-task >> << parameters.extra-args >>"));
}
private void addGradleJobs() {
// Gradle test job
orbBuilder.addJob("gradle-test", new CircleCIOrbBuilder.JobBuilder()
.executor("java11")
.addCheckoutStep()
.addRunStep("Make Gradle Wrapper Executable", "chmod +x ./gradlew")
.addStep("gradle-test", Map.of("command", "java-orb/gradle-test"))
.addRunStep("Store Test Results", "mkdir -p test-results/junit && find . -type f -regex \".*/build/test-results/.*xml\" -exec cp {} test-results/junit/ \\;")
.addStep("store_test_results", Map.of("path", "test-results"))
.addStep("store_artifacts", Map.of("path", "test-results")));
}
private void addUtilityCommands() {
// Cache setup for Maven
orbBuilder.addCommand("maven-cache", new CircleCIOrbBuilder.CommandBuilder()
.description("Setup Maven cache")
.addStep("restore_cache", Map.of(
"keys", List.of("v1-maven-cache-{{ checksum \"pom.xml\" }}", "v1-maven-cache-")
))
.addRunStep("Create Maven Directory", "mkdir -p ~/.m2")
.addStep("save_cache", Map.of(
"paths", List.of("~/.m2"),
"key", "v1-maven-cache-{{ checksum \"pom.xml\" }}"
)));
// Cache setup for Gradle
orbBuilder.addCommand("gradle-cache", new CircleCIOrbBuilder.CommandBuilder()
.description("Setup Gradle cache")
.addStep("restore_cache", Map.of(
"keys", List.of("v1-gradle-cache-{{ checksum \"build.gradle\" }}", "v1-gradle-cache-")
))
.addRunStep("Create Gradle Directory", "mkdir -p ~/.gradle")
.addStep("save_cache", Map.of(
"paths", List.of("~/.gradle"),
"key", "v1-gradle-cache-{{ checksum \"build.gradle\" }}"
)));
// Setup Java version
orbBuilder.addCommand("setup-java", new CircleCIOrbBuilder.CommandBuilder()
.description("Setup Java environment")
.addParameter("java-version", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue("11"))
.addRunStep("Setup Java", "echo \"Java version: << parameters.java-version >>\""));
}
}
3. Spring Boot Specific Orb Generator
public class SpringBootOrbGenerator {
private final CircleCIOrbBuilder orbBuilder;
public SpringBootOrbGenerator(String orbName, String orbVersion) {
this.orbBuilder = new CircleCIOrbBuilder(orbName, orbVersion);
}
public String generateSpringBootOrb() {
addSpringBootExecutors();
addSpringBootCommands();
addSpringBootJobs();
addDatabaseServices();
addDockerCommands();
return orbBuilder.toYaml();
}
private void addSpringBootExecutors() {
// Spring Boot with database support
orbBuilder.addExecutor("spring-boot-java11", new CircleCIOrbBuilder.ExecutorBuilder()
.docker("cimg/openjdk:11.0")
.environment(Map.of(
"SPRING_PROFILES_ACTIVE", "ci",
"DATABASE_URL", "jdbc:postgresql://localhost:5432/test"
)));
}
private void addSpringBootCommands() {
// Spring Boot test with profile
orbBuilder.addCommand("spring-boot-test", new CircleCIOrbBuilder.CommandBuilder()
.description("Run Spring Boot tests with CI profile")
.addParameter("profile", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue("ci"))
.addRunStep("Run Tests", "mvn test -Dspring.profiles.active=<< parameters.profile >>"));
// Spring Boot build with Docker
orbBuilder.addCommand("spring-boot-build", new CircleCIOrbBuilder.CommandBuilder()
.description("Build Spring Boot application with Docker")
.addParameter("image-name", new CircleCIOrbBuilder.ParameterBuilder("string")
.defaultValue("myapp"))
.addRunStep("Build JAR", "mvn clean package -DskipTests")
.addRunStep("Build Docker Image", "docker build -t << parameters.image-name >> .")));
// Database migration
orbBuilder.addCommand("db-migrate", new CircleCIOrbBuilder.CommandBuilder()
.description("Run database migrations")
.addRunStep("Run Migrations", "mvn flyway:migrate -Dflyway.url=$DATABASE_URL"));
}
private void addSpringBootJobs() {
// Spring Boot integration test job
orbBuilder.addJob("spring-boot-integration-test", new CircleCIOrbBuilder.JobBuilder()
.executor("spring-boot-java11")
.addCheckoutStep()
.addStep("setup-database", Map.of("command", "spring-boot-orb/start-postgres"))
.addStep("db-migrate", Map.of("command", "spring-boot-orb/db-migrate"))
.addStep("spring-boot-test", Map.of(
"command", "spring-boot-orb/spring-boot-test",
"profile", "ci"
))
.addStep("store_test_results", Map.of("path", "target/surefire-reports")));
// Spring Boot Docker build job
orbBuilder.addJob("spring-boot-docker-build", new CircleCIOrbBuilder.JobBuilder()
.executor("spring-boot-java11")
.addCheckoutStep()
.addStep("setup-docker", Map.of("command", "spring-boot-orb/setup-docker"))
.addStep("spring-boot-build", Map.of(
"command", "spring-boot-orb/spring-boot-build",
"image-name", "myapp:${CIRCLE_SHA1}"
))
.addRunStep("Login to Registry", "echo \"$DOCKER_PASSWORD\" | docker login -u \"$DOCKER_USERNAME\" --password-stdin")
.addRunStep("Push Image", "docker push myapp:${CIRCLE_SHA1}"));
}
private void addDatabaseServices() {
// Note: Services are typically added at the job level, but we can create commands to set them up
orbBuilder.addCommand("start-postgres", new CircleCIOrbBuilder.CommandBuilder()
.description("Start PostgreSQL service")
.addRunStep("Start PostgreSQL", "docker run -d -p 5432:5432 -e POSTGRES_USER=test -e POSTGRES_PASSWORD=test -e POSTGRES_DB=test postgres:13")));
orbBuilder.addCommand("start-redis", new CircleCIOrbBuilder.CommandBuilder()
.description("Start Redis service")
.addRunStep("Start Redis", "docker run -d -p 6379:6379 redis:6-alpine")));
}
private void addDockerCommands() {
orbBuilder.addCommand("setup-docker", new CircleCIOrbBuilder.CommandBuilder()
.description("Setup Docker environment")
.addRunStep("Install Docker", "curl -fsSL https://get.docker.com | sh")
.addRunStep("Start Docker", "sudo service docker start")));
}
}
4. Orb Validation and Testing
import org.yaml.snakeyaml.Yaml;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.*;
public class CircleCIOrbValidator {
private final Yaml yaml;
private final ScriptEngine scriptEngine;
public CircleCIOrbValidator() {
this.yaml = new Yaml();
ScriptEngineManager manager = new ScriptEngineManager();
this.scriptEngine = manager.getEngineByName("javascript");
}
public ValidationResult validateOrb(String orbYaml) {
ValidationResult result = new ValidationResult();
try {
Map<String, Object> orbConfig = yaml.load(orbYaml);
// Validate structure
validateStructure(orbConfig, result);
// Validate version
validateVersion(orbConfig, result);
// Validate jobs
validateJobs(orbConfig, result);
// Validate commands
validateCommands(orbConfig, result);
// Validate executors
validateExecutors(orbConfig, result);
// Validate parameters
validateParameters(orbConfig, result);
} catch (Exception e) {
result.addError("Invalid YAML format: " + e.getMessage());
}
return result;
}
private void validateStructure(Map<String, Object> orbConfig, ValidationResult result) {
List<String> requiredSections = Arrays.asList("version", "orbs", "jobs", "commands", "executors");
for (String section : requiredSections) {
if (!orbConfig.containsKey(section)) {
result.addWarning("Missing optional section: " + section);
}
}
}
private void validateVersion(Map<String, Object> orbConfig, ValidationResult result) {
Object version = orbConfig.get("version");
if (!"2.1".equals(version)) {
result.addError("Unsupported CircleCI version: " + version);
}
}
private void validateJobs(Map<String, Object> orbConfig, ValidationResult result) {
Map<String, Object> jobs = (Map<String, Object>) orbConfig.get("jobs");
if (jobs == null) return;
for (Map.Entry<String, Object> jobEntry : jobs.entrySet()) {
String jobName = jobEntry.getKey();
Map<String, Object> jobConfig = (Map<String, Object>) jobEntry.getValue();
// Check for required executor/docker/machine
if (!jobConfig.containsKey("executor") &&
!jobConfig.containsKey("docker") &&
!jobConfig.containsKey("machine")) {
result.addError("Job '" + jobName + "' must specify executor, docker, or machine");
}
// Check for steps
if (!jobConfig.containsKey("steps")) {
result.addError("Job '" + jobName + "' must have steps defined");
}
}
}
private void validateCommands(Map<String, Object> orbConfig, ValidationResult result) {
Map<String, Object> commands = (Map<String, Object>) orbConfig.get("commands");
if (commands == null) return;
for (Map.Entry<String, Object> commandEntry : commands.entrySet()) {
String commandName = commandEntry.getKey();
Map<String, Object> commandConfig = (Map<String, Object>) commandEntry.getValue();
// Check for steps
if (!commandConfig.containsKey("steps")) {
result.addError("Command '" + commandName + "' must have steps defined");
}
// Validate parameter types
if (commandConfig.containsKey("parameters")) {
validateParameters((Map<String, Object>) commandConfig.get("parameters"), result, "command " + commandName);
}
}
}
private void validateExecutors(Map<String, Object> orbConfig, ValidationResult result) {
Map<String, Object> executors = (Map<String, Object>) orbConfig.get("executors");
if (executors == null) return;
for (Map.Entry<String, Object> executorEntry : executors.entrySet()) {
String executorName = executorEntry.getKey();
Map<String, Object> executorConfig = (Map<String, Object>) executorEntry.getValue();
// Check for docker or machine
if (!executorConfig.containsKey("docker") && !executorConfig.containsKey("machine")) {
result.addError("Executor '" + executorName + "' must specify docker or machine");
}
}
}
private void validateParameters(Map<String, Object> orbConfig, ValidationResult result) {
Map<String, Object> parameters = (Map<String, Object>) orbConfig.get("parameters");
if (parameters == null) return;
validateParameters(parameters, result, "orb");
}
private void validateParameters(Map<String, Object> parameters, ValidationResult result, String context) {
for (Map.Entry<String, Object> paramEntry : parameters.entrySet()) {
String paramName = paramEntry.getKey();
Map<String, Object> paramConfig = (Map<String, Object>) paramEntry.getValue();
// Check for type
if (!paramConfig.containsKey("type")) {
result.addError("Parameter '" + paramName + "' in " + context + " must have a type");
}
// Validate type
String type = (String) paramConfig.get("type");
if (!Arrays.asList("string", "boolean", "integer", "enum").contains(type)) {
result.addError("Parameter '" + paramName + "' in " + context + " has invalid type: " + type);
}
}
}
public static class ValidationResult {
private final List<String> errors = new ArrayList<>();
private final List<String> warnings = new ArrayList<>();
public void addError(String error) {
errors.add(error);
}
public void addWarning(String warning) {
warnings.add(warning);
}
public boolean isValid() {
return errors.isEmpty();
}
public List<String> getErrors() {
return Collections.unmodifiableList(errors);
}
public List<String> getWarnings() {
return Collections.unmodifiableList(warnings);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (!errors.isEmpty()) {
sb.append("Errors:\n");
errors.forEach(error -> sb.append(" - ").append(error).append("\n"));
}
if (!warnings.isEmpty()) {
sb.append("Warnings:\n");
warnings.forEach(warning -> sb.append(" - ").append(warning).append("\n"));
}
return sb.toString();
}
}
}
5. Orb Template Engine
import freemarker.template.*;
import java.util.*;
public class OrbTemplateEngine {
private final Configuration freeMarkerConfig;
public OrbTemplateEngine() {
this.freeMarkerConfig = new Configuration(Configuration.VERSION_2_3_31);
this.freeMarkerConfig.setClassForTemplateLoading(getClass(), "/templates");
this.freeMarkerConfig.setDefaultEncoding("UTF-8");
this.freeMarkerConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
}
public String generateOrbFromTemplate(String templateName, Map<String, Object> dataModel) {
try {
Template template = freeMarkerConfig.getTemplate(templateName);
StringWriter writer = new StringWriter();
template.process(dataModel, writer);
return writer.toString();
} catch (Exception e) {
throw new RuntimeException("Failed to generate orb from template", e);
}
}
// Predefined templates for common Java workflows
public String generateMavenOrb(String projectName, String javaVersion,
boolean withTests, boolean withDocker) {
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("projectName", projectName);
dataModel.put("javaVersion", javaVersion);
dataModel.put("withTests", withTests);
dataModel.put("withDocker", withDocker);
return generateOrbFromTemplate("maven-orb-template.yaml", dataModel);
}
public String generateGradleOrb(String projectName, String javaVersion,
boolean withTests, boolean withDocker) {
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("projectName", projectName);
dataModel.put("javaVersion", javaVersion);
dataModel.put("withTests", withTests);
dataModel.put("withDocker", withDocker);
return generateOrbFromTemplate("gradle-orb-template.yaml", dataModel);
}
public String generateSpringBootOrb(String projectName, String javaVersion,
boolean withDatabase, boolean withDocker) {
Map<String, Object> dataModel = new HashMap<>();
dataModel.put("projectName", projectName);
dataModel.put("javaVersion", javaVersion);
dataModel.put("withDatabase", withDatabase);
dataModel.put("withDocker", withDocker);
return generateOrbFromTemplate("spring-boot-orb-template.yaml", dataModel);
}
}
6. Orb Deployment Manager
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.*;
public class OrbDeploymentManager {
private final RestTemplate restTemplate;
private final String circleToken;
private final String circleBaseUrl = "https://circleci.com/api/v2";
public OrbDeploymentManager(String circleToken) {
this.restTemplate = new RestTemplate();
this.circleToken = circleToken;
}
public DeploymentResult publishOrb(String orbNamespace, String orbName,
String orbVersion, String orbYaml) {
try {
// Validate orb first
CircleCIOrbValidator validator = new CircleCIOrbValidator();
CircleCIOrbValidator.ValidationResult validation = validator.validateOrb(orbYaml);
if (!validation.isValid()) {
return DeploymentResult.failure("Orb validation failed: " + validation.toString());
}
// Create orb if it doesn't exist
if (!orbExists(orbNamespace, orbName)) {
createOrb(orbNamespace, orbName);
}
// Publish orb version
return publishOrbVersion(orbNamespace, orbName, orbVersion, orbYaml);
} catch (Exception e) {
return DeploymentResult.failure("Deployment failed: " + e.getMessage());
}
}
private boolean orbExists(String namespace, String name) {
try {
String url = circleBaseUrl + "/orb/" + namespace + "/" + name;
HttpEntity<String> entity = createAuthEntity();
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.GET, entity, String.class);
return response.getStatusCode() == HttpStatus.OK;
} catch (Exception e) {
return false;
}
}
private void createOrb(String namespace, String name) {
String url = circleBaseUrl + "/orb/" + namespace + "/" + name;
HttpEntity<String> entity = createAuthEntity();
restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
}
private DeploymentResult publishOrbVersion(String namespace, String name,
String version, String orbYaml) {
try {
String url = circleBaseUrl + "/orb/" + namespace + "/" + name + "/" + version;
Map<String, String> requestBody = new HashMap<>();
requestBody.put("orb", orbYaml);
HttpEntity<Map<String, String>> entity = createAuthEntity(requestBody);
ResponseEntity<String> response = restTemplate.exchange(
url, HttpMethod.POST, entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
return DeploymentResult.success("Orb published successfully");
} else {
return DeploymentResult.failure("Failed to publish orb: " + response.getBody());
}
} catch (Exception e) {
return DeploymentResult.failure("Publish failed: " + e.getMessage());
}
}
public List<OrbInfo> listOrbs(String namespace) {
try {
String url = circleBaseUrl + "/orb?namespace=" + namespace;
HttpEntity<String> entity = createAuthEntity();
ResponseEntity<OrbListResponse> response = restTemplate.exchange(
url, HttpMethod.GET, entity, OrbListResponse.class);
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
return response.getBody().getOrbs();
}
} catch (Exception e) {
// Log error
}
return Collections.emptyList();
}
private HttpEntity<String> createAuthEntity() {
HttpHeaders headers = new HttpHeaders();
headers.set("Circle-Token", circleToken);
headers.setContentType(MediaType.APPLICATION_JSON);
return new HttpEntity<>(headers);
}
private <T> HttpEntity<T> createAuthEntity(T body) {
HttpHeaders headers = new HttpHeaders();
headers.set("Circle-Token", circleToken);
headers.setContentType(MediaType.APPLICATION_JSON);
return new HttpEntity<>(body, headers);
}
public static class DeploymentResult {
private final boolean success;
private final String message;
private DeploymentResult(boolean success, String message) {
this.success = success;
this.message = message;
}
public static DeploymentResult success(String message) {
return new DeploymentResult(true, message);
}
public static DeploymentResult failure(String message) {
return new DeploymentResult(false, message);
}
// Getters
public boolean isSuccess() { return success; }
public String getMessage() { return message; }
}
// Response classes for CircleCI API
public static class OrbListResponse {
private List<OrbInfo> orbs;
public List<OrbInfo> getOrbs() { return orbs; }
public void setOrbs(List<OrbInfo> orbs) { this.orbs = orbs; }
}
public static class OrbInfo {
private String name;
private String namespace;
private String version;
private String createdAt;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public String getCreatedAt() { return createdAt; }
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
}
}
7. Usage Examples
public class CircleCIOrbExamples {
public static void main(String[] args) {
// Example 1: Generate a basic Java orb
JavaOrbGenerator javaOrbGenerator = new JavaOrbGenerator("java-orb", "1.0.0");
String javaOrbYaml = javaOrbGenerator.generateJavaOrb();
System.out.println("Generated Java Orb:\n" + javaOrbYaml);
// Example 2: Generate a Spring Boot orb
SpringBootOrbGenerator springBootOrbGenerator = new SpringBootOrbGenerator("spring-boot-orb", "1.0.0");
String springBootOrbYaml = springBootOrbGenerator.generateSpringBootOrb();
System.out.println("Generated Spring Boot Orb:\n" + springBootOrbYaml);
// Example 3: Validate an orb
CircleCIOrbValidator validator = new CircleCIOrbValidator();
CircleCIOrbValidator.ValidationResult result = validator.validateOrb(javaOrbYaml);
System.out.println("Validation Result: " + (result.isValid() ? "VALID" : "INVALID"));
System.out.println(result);
// Example 4: Programmatic orb creation
CircleCIOrbBuilder customOrbBuilder = new CircleCIOrbBuilder("custom-java-orb", "1.0.0");
customOrbBuilder.addJob("custom-test", new CircleCIOrbBuilder.JobBuilder()
.executor("java11")
.addCheckoutStep()
.addRunStep("Custom Test", "mvn test -Dcustom.param=value")
.addStep("store_test_results", Map.of("path", "target/surefire-reports")));
String customOrbYaml = customOrbBuilder.toYaml();
System.out.println("Custom Orb:\n" + customOrbYaml);
// Example 5: Deploy orb (requires CircleCI token)
if (args.length > 0) {
String circleToken = args[0];
OrbDeploymentManager deploymentManager = new OrbDeploymentManager(circleToken);
OrbDeploymentManager.DeploymentResult deploymentResult =
deploymentManager.publishOrb("my-namespace", "java-orb", "1.0.0", javaOrbYaml);
System.out.println("Deployment: " +
(deploymentResult.isSuccess() ? "SUCCESS" : "FAILED"));
System.out.println("Message: " + deploymentResult.getMessage());
}
}
}
8. Spring Boot Integration
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
@Service
public class OrbManagementService {
@Value("${circleci.token:}")
private String circleToken;
private final OrbTemplateEngine templateEngine;
private final OrbDeploymentManager deploymentManager;
public OrbManagementService() {
this.templateEngine = new OrbTemplateEngine();
this.deploymentManager = new OrbDeploymentManager(circleToken);
}
public String generateProjectOrb(ProjectConfig config) {
switch (config.getBuildTool()) {
case "maven":
return templateEngine.generateMavenOrb(
config.getProjectName(),
config.getJavaVersion(),
config.isWithTests(),
config.isWithDocker()
);
case "gradle":
return templateEngine.generateGradleOrb(
config.getProjectName(),
config.getJavaVersion(),
config.isWithTests(),
config.isWithDocker()
);
case "spring-boot":
return templateEngine.generateSpringBootOrb(
config.getProjectName(),
config.getJavaVersion(),
config.isWithDatabase(),
config.isWithDocker()
);
default:
throw new IllegalArgumentException("Unsupported build tool: " + config.getBuildTool());
}
}
public OrbDeploymentManager.DeploymentResult deployOrb(ProjectConfig config) {
String orbYaml = generateProjectOrb(config);
return deploymentManager.publishOrb(
config.getNamespace(),
config.getOrbName(),
config.getOrbVersion(),
orbYaml
);
}
public static class ProjectConfig {
private String projectName;
private String buildTool;
private String javaVersion;
private boolean withTests;
private boolean withDocker;
private boolean withDatabase;
private String namespace;
private String orbName;
private String orbVersion;
// Getters and setters
public String getProjectName() { return projectName; }
public void setProjectName(String projectName) { this.projectName = projectName; }
public String getBuildTool() { return buildTool; }
public void setBuildTool(String buildTool) { this.buildTool = buildTool; }
public String getJavaVersion() { return javaVersion; }
public void setJavaVersion(String javaVersion) { this.javaVersion = javaVersion; }
public boolean isWithTests() { return withTests; }
public void setWithTests(boolean withTests) { this.withTests = withTests; }
public boolean isWithDocker() { return withDocker; }
public void setWithDocker(boolean withDocker) { this.withDocker = withDocker; }
public boolean isWithDatabase() { return withDatabase; }
public void setWithDatabase(boolean withDatabase) { this.withDatabase = withDatabase; }
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public String getOrbName() { return orbName; }
public void setOrbName(String orbName) { this.orbName = orbName; }
public String getOrbVersion() { return orbVersion; }
public void setOrbVersion(String orbVersion) { this.orbVersion = orbVersion; }
}
}
This comprehensive framework allows you to:
- Programmatically generate CircleCI orbs for Java projects
- Validate orb configurations before deployment
- Template-based generation for common Java workflows
- Deploy orbs to CircleCI programmatically
- Manage orb lifecycle through Java APIs
The framework supports Maven, Gradle, Spring Boot, and custom Java workflows with features like caching, Docker integration, database testing, and more.