Article
CUE (Configure, Unify, Execute) is a powerful configuration language developed by Google that combines type safety, validation, and templating into a single unified language. For Java applications, CUE provides a robust solution for managing complex configurations, validating data, and generating type-safe code.
In this guide, we'll explore how to integrate CUE with Java applications to create type-safe, validated configuration management systems.
Why CUE for Java Applications?
- Type-Safe Configuration: Define schemas and validate configurations at compile time
- Data Validation: Comprehensive validation beyond JSON Schema capabilities
- Configuration Templating: Reusable configuration templates with inheritance
- Code Generation: Generate Java classes from CUE definitions
- Multi-Environment Management: Manage dev, staging, prod configurations safely
- Policy as Code: Define and enforce configuration policies
Part 1: CUE Language Fundamentals for Java Developers
1.1 Basic CUE Syntax
// Basic types and definitions #AppConfig: { // Required string field name: string // Optional field with default version?: string | *"1.0.0" // Number constraints port: int & >=3000 & <=9999 // Enumerated values environment: "dev" | "staging" | "production" // Nested objects database: { host: string port: int name: string username: string password: string } // Arrays with constraints features: [...string] // Maps labels: [string]: string } // Concrete configuration appConfig: #AppConfig & { name: "my-java-app" port: 8080 environment: "dev" database: { host: "localhost" port: 5432 name: "mydb" username: "admin" password: "secret" } features: ["auth", "cache", "metrics"] labels: { "team": "backend" "project": "java-services" } } 1.2 Advanced CUE Features
// Reusable components #DatabaseConfig: { host: string port: int & >0 name: string pool: { min: int & >=1 & <=10 max: int & >=5 & <=50 timeout: int & >=30 } } #ServiceConfig: { name: string version: string endpoints: [string]: { path: string method: "GET" | "POST" | "PUT" | "DELETE" timeout: int } } // Configuration unification devConfig: #AppConfig & { name: "java-service" environment: "dev" database: #DatabaseConfig & { host: "localhost" port: 5432 name: "dev_db" pool: { min: 2 max: 10 timeout: 60 } } } prodConfig: devConfig & { environment: "production" database: { host: "prod-db.cluster.example.com" port: 5432 name: "prod_db" pool: { min: 5 max: 25 timeout: 30 } } } Part 2: Java-CUE Integration Libraries
2.1 Dependencies Setup
Maven:
<dependencies> <!-- CUE Java API --> <dependency> <groupId>org.cuelang</groupId> <artifactId>cue-core</artifactId> <version>0.5.0</version> </dependency> <!-- Jackson for JSON serialization --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency> <!-- CUE Maven Plugin for code generation --> <dependency> <groupId>org.cuelang</groupId> <artifactId>cue-maven-plugin</artifactId> <version>0.5.0</version> </dependency> <!-- Configuration libraries --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies>
Gradle:
dependencies { implementation 'org.cuelang:cue-core:0.5.0' implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' implementation 'org.springframework.boot:spring-boot-configuration-processor' } 2.2 Project Structure
java-cue-app/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/example/ │ │ └── resources/ │ │ └── config/ │ │ ├── app.cue │ │ ├── database.cue │ │ └── services.cue │ └── generated/ │ └── sources/ │ └── cue/ │ └── com/example/config/ ├── cue.mod/ │ └── module.cue └── pom.xml
Part 3: Java CUE API Integration
3.1 Basic CUE Loading and Validation
// File: src/main/java/com/example/cue/CueConfigLoader.java package com.example.cue; import org.cuelang.core.Runtime; import org.cuelang.core.Value; import org.cuelang.core.BuildException; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @Component public class CueConfigLoader { private final Runtime runtime; private final ObjectMapper objectMapper; public CueConfigLoader() { this.runtime = Runtime.getDefault(); this.objectMapper = new ObjectMapper(); } public <T> T loadConfig(String cueFilePath, String configPath, Class<T> configType) { try { String cueContent = Files.readString(Path.of(cueFilePath)); Value value = runtime.compile(cueContent); // Get specific configuration value Value configValue = value.lookupPath(configPath); // Validate and convert to Java object if (configValue.isConcrete()) { String json = configValue.toJson(); return objectMapper.readValue(json, configType); } else { throw new CueValidationException("Configuration is not concrete: " + configValue); } } catch (IOException | BuildException e) { throw new CueConfigException("Failed to load CUE configuration", e); } } public void validateConfig(String cueFilePath, String configPath) { try { String cueContent = Files.readString(Path.of(cueFilePath)); Value value = runtime.compile(cueContent); Value configValue = value.lookupPath(configPath); if (!configValue.isConcrete()) { throw new CueValidationException("Configuration validation failed: " + configValue); } } catch (IOException | BuildException e) { throw new CueConfigException("Configuration validation error", e); } } } // Custom exceptions class CueConfigException extends RuntimeException { public CueConfigException(String message, Throwable cause) { super(message, cause); } } class CueValidationException extends RuntimeException { public CueValidationException(String message) { super(message); } } 3.2 Configuration Data Classes
// File: src/main/java/com/example/config/AppConfig.java package com.example.config; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import java.util.Map; public class AppConfig { private String name; private String version; private int port; private String environment; private DatabaseConfig database; private List<String> features; private Map<String, String> labels; // Constructors, getters, and setters public AppConfig() {} public AppConfig(String name, String version, int port, String environment, DatabaseConfig database, List<String> features, Map<String, String> labels) { this.name = name; this.version = version; this.port = port; this.environment = environment; this.database = database; this.features = features; this.labels = labels; } // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { this.environment = environment; } public DatabaseConfig getDatabase() { return database; } public void setDatabase(DatabaseConfig database) { this.database = database; } public List<String> getFeatures() { return features; } public void setFeatures(List<String> features) { this.features = features; } public Map<String, String> getLabels() { return labels; } public void setLabels(Map<String, String> labels) { this.labels = labels; } } // File: src/main/java/com/example/config/DatabaseConfig.java package com.example.config; public class DatabaseConfig { private String host; private int port; private String name; private String username; private String password; private ConnectionPool pool; // Constructors, getters, and setters public DatabaseConfig() {} public DatabaseConfig(String host, int port, String name, String username, String password, ConnectionPool pool) { this.host = host; this.port = port; this.name = name; this.username = username; this.password = password; this.pool = pool; } // Getters and setters public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public ConnectionPool getPool() { return pool; } public void setPool(ConnectionPool pool) { this.pool = pool; } } // File: src/main/java/com/example/config/ConnectionPool.java package com.example.config; public class ConnectionPool { private int min; private int max; private int timeout; // Constructors, getters, and setters public ConnectionPool() {} public ConnectionPool(int min, int max, int timeout) { this.min = min; this.max = max; this.timeout = timeout; } public int getMin() { return min; } public void setMin(int min) { this.min = min; } public int getMax() { return max; } public void setMax(int max) { this.max = max; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } } Part 4: Spring Boot Integration
4.1 CUE-Based Configuration Properties
// File: src/main/java/com/example/config/CueConfigurationProperties.java package com.example.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.validation.annotation.Validated; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.util.List; import java.util.Map; @Configuration @ConfigurationProperties(prefix = "app") @Validated public class CueConfigurationProperties { @NotBlank private String name; private String version = "1.0.0"; @Min(1024) @Max(65535) private int port = 8080; @NotBlank private String environment; @NotNull private DatabaseProperties database; private List<String> features; private Map<String, String> labels; // Getters and setters public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getEnvironment() { return environment; } public void setEnvironment(String environment) { this.environment = environment; } public DatabaseProperties getDatabase() { return database; } public void setDatabase(DatabaseProperties database) { this.database = database; } public List<String> getFeatures() { return features; } public void setFeatures(List<String> features) { this.features = features; } public Map<String, String> getLabels() { return labels; } public void setLabels(Map<String, String> labels) { this.labels = labels; } } // File: src/main/java/com/example/config/DatabaseProperties.java package com.example.config; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Positive; public class DatabaseProperties { @NotBlank private String host; @Positive private int port = 5432; @NotBlank private String name; @NotBlank private String username; @NotBlank private String password; @NotNull private ConnectionPoolProperties pool; // Getters and setters public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public ConnectionPoolProperties getPool() { return pool; } public void setPool(ConnectionPoolProperties pool) { this.pool = pool; } } 4.2 CUE Configuration Service
// File: src/main/java/com/example/service/CueConfigService.java package com.example.service; import com.example.cue.CueConfigLoader; import com.example.config.AppConfig; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; @Service public class CueConfigService { private final CueConfigLoader cueConfigLoader; private final Environment environment; private AppConfig appConfig; public CueConfigService(CueConfigLoader cueConfigLoader, Environment environment) { this.cueConfigLoader = cueConfigLoader; this.environment = environment; } @PostConstruct public void initialize() { String environmentName = environment.getProperty("app.environment", "dev"); String configPath = String.format("configs.%sConfig", environmentName); this.appConfig = cueConfigLoader.loadConfig( "src/main/resources/config/app.cue", configPath, AppConfig.class ); } public AppConfig getAppConfig() { return appConfig; } public void validateConfiguration() { String environmentName = environment.getProperty("app.environment", "dev"); String configPath = String.format("configs.%sConfig", environmentName); cueConfigLoader.validateConfig("src/main/resources/config/app.cue", configPath); } } 4.3 Spring Configuration Bootstrap
// File: src/main/java/com/example/config/CueBootstrapConfiguration.java package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.example.cue.CueConfigLoader; @Configuration public class CueBootstrapConfiguration { @Bean public CueConfigLoader cueConfigLoader() { return new CueConfigLoader(); } @Bean public CueConfigurationProperties cueConfigurationProperties(CueConfigLoader configLoader) { // Load configuration from CUE and map to Spring properties AppConfig appConfig = configLoader.loadConfig( "src/main/resources/config/app.cue", "configs.devConfig", // Default, will be overridden by profile AppConfig.class ); CueConfigurationProperties properties = new CueConfigurationProperties(); properties.setName(appConfig.getName()); properties.setVersion(appConfig.getVersion()); properties.setPort(appConfig.getPort()); properties.setEnvironment(appConfig.getEnvironment()); properties.setFeatures(appConfig.getFeatures()); properties.setLabels(appConfig.getLabels()); // Map database configuration DatabaseProperties dbProperties = new DatabaseProperties(); dbProperties.setHost(appConfig.getDatabase().getHost()); dbProperties.setPort(appConfig.getDatabase().getPort()); dbProperties.setName(appConfig.getDatabase().getName()); dbProperties.setUsername(appConfig.getDatabase().getUsername()); dbProperties.setPassword(appConfig.getDatabase().getPassword()); properties.setDatabase(dbProperties); return properties; } } Part 5: Advanced CUE-Java Integration
5.1 Code Generation with CUE
// File: src/main/resources/schemas/java.cue #JavaClass: { package: string className: string fields: [string]: { type: "String" | "int" | "boolean" | "List<String>" | "Map<String, String>" defaultValue?: string validation?: string } } #ConfigClass: #JavaClass & { package: "com.example.generated" className: "GeneratedConfig" fields: { appName: { type: "String" validation: "@NotBlank" } maxConnections: { type: "int" defaultValue: "10" validation: "@Min(1) @Max(100)" } enabled: { type: "boolean" defaultValue: "true" } } } 5.2 CUE Template Generation
// File: src/main/java/com/example/cue/JavaCodeGenerator.java package com.example.cue; import org.cuelang.core.Runtime; import org.cuelang.core.Value; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @Component public class JavaCodeGenerator { private final Runtime runtime; private final ObjectMapper objectMapper; public JavaCodeGenerator() { this.runtime = Runtime.getDefault(); this.objectMapper = new ObjectMapper(); } public void generateJavaClass(String cueSchemaPath, String outputPath) { try { String cueContent = Files.readString(Path.of(cueSchemaPath)); Value value = runtime.compile(cueContent); Value classDefinition = value.lookupPath("#ConfigClass"); if (classDefinition.isConcrete()) { String json = classDefinition.toJson(); JsonNode classNode = objectMapper.readTree(json); String javaCode = generateJavaSource(classNode); Files.writeString(Path.of(outputPath), javaCode); } } catch (IOException e) { throw new RuntimeException("Failed to generate Java class", e); } } private String generateJavaSource(JsonNode classNode) { String packageName = classNode.get("package").asText(); String className = classNode.get("className").asText(); JsonNode fields = classNode.get("fields"); StringBuilder sb = new StringBuilder(); sb.append("package ").append(packageName).append(";\n\n"); sb.append("import javax.validation.constraints.*;\n"); sb.append("import java.util.*;\n\n"); sb.append("public class ").append(className).append(" {\n"); // Generate fields fields.fieldNames().forEachRemaining(fieldName -> { JsonNode field = fields.get(fieldName); String type = field.get("type").asText(); String validation = field.has("validation") ? field.get("validation").asText() : ""; String defaultValue = field.has("defaultValue") ? " = " + field.get("defaultValue").asText() : ""; if (!validation.isEmpty()) { sb.append(" ").append(validation).append("\n"); } sb.append(" private ").append(type).append(" ") .append(fieldName).append(defaultValue).append(";\n\n"); }); // Generate getters and setters fields.fieldNames().forEachRemaining(fieldName -> { JsonNode field = fields.get(fieldName); String type = field.get("type").asText(); String capitalized = capitalize(fieldName); // Getter sb.append(" public ").append(type).append(" get").append(capitalized) .append("() { return this.").append(fieldName).append("; }\n\n"); // Setter sb.append(" public void set").append(capitalized).append("(") .append(type).append(" ").append(fieldName).append(") { this.") .append(fieldName).append(" = ").append(fieldName).append("; }\n\n"); }); sb.append("}"); return sb.toString(); } private String capitalize(String str) { return str.substring(0, 1).toUpperCase() + str.substring(1); } } Part 6: Testing CUE-Java Integration
6.1 Unit Tests for CUE Configuration
// File: src/test/java/com/example/cue/CueConfigLoaderTest.java package com.example.cue; import com.example.config.AppConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import static org.junit.jupiter.api.Assertions.*; class CueConfigLoaderTest { private CueConfigLoader configLoader; @TempDir Path tempDir; @BeforeEach void setUp() { configLoader = new CueConfigLoader(); } @Test void shouldLoadValidConfiguration() throws IOException { // Given String cueContent = """ #AppConfig: { name: string port: int & >=3000 database: { host: string port: int } } config: #AppConfig & { name: "test-app" port: 8080 database: { host: "localhost" port: 5432 } } """; Path cueFile = tempDir.resolve("test.cue"); Files.writeString(cueFile, cueContent); // When AppConfig config = configLoader.loadConfig(cueFile.toString(), "config", AppConfig.class); // Then assertNotNull(config); assertEquals("test-app", config.getName()); assertEquals(8080, config.getPort()); assertNotNull(config.getDatabase()); assertEquals("localhost", config.getDatabase().getHost()); } @Test void shouldThrowExceptionForInvalidConfiguration() throws IOException { // Given String cueContent = """ config: { port: "not-a-number" // This should cause validation error } """; Path cueFile = tempDir.resolve("invalid.cue"); Files.writeString(cueFile, cueContent); // When & Then assertThrows(CueConfigException.class, () -> { configLoader.loadConfig(cueFile.toString(), "config", AppConfig.class); }); } } 6.2 Integration Tests
// File: src/test/java/com/example/service/CueConfigServiceIntegrationTest.java package com.example.service; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.TestPropertySource; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest @TestPropertySource(properties = { "app.environment=dev" }) class CueConfigServiceIntegrationTest { @Autowired private CueConfigService cueConfigService; @Test void shouldLoadConfigurationOnStartup() { // When var appConfig = cueConfigService.getAppConfig(); // Then assertNotNull(appConfig); assertNotNull(appConfig.getName()); assertTrue(appConfig.getPort() > 0); assertNotNull(appConfig.getDatabase()); } @Test void shouldValidateConfiguration() { // This should not throw an exception for valid configuration assertDoesNotThrow(() -> cueConfigService.validateConfiguration()); } } Part 7: Real-World Use Cases
7.1 Multi-Environment Configuration
// File: src/main/resources/config/environments.cue package config import "strings" #BaseConfig: { app: { name: string version: string port: int & >=3000 } database: { host: string port: int name: string } } devConfig: #BaseConfig & { app: { name: "my-java-app" version: "1.0.0" port: 8080 } database: { host: "localhost" port: 5432 name: "app_dev" } } stagingConfig: devConfig & { app: { port: 8081 } database: { host: "staging-db.example.com" name: "app_staging" } } prodConfig: devConfig & { app: { port: 80 } database: { host: "prod-db.example.com" name: "app_production" } } // Export configuration based on environment configs: { dev: devConfig staging: stagingConfig production: prodConfig } 7.2 Feature Flag Configuration
// File: src/main/resources/config/features.cue #FeatureFlag: { name: string enabled: bool percentage: number & >=0 & <=100 targetUsers: [...string] } #FeatureConfig: { darkMode: #FeatureFlag & { name: "dark_mode" enabled: true percentage: 50.0 targetUsers: ["user1", "user2"] } newPayment: #FeatureFlag & { name: "new_payment_service" enabled: false percentage: 10.0 targetUsers: [] } experimentalAPI: #FeatureFlag & { name: "experimental_api" enabled: true percentage: 25.0 targetUsers: ["beta-tester"] } } features: #FeatureConfig & {} Best Practices for CUE with Java
- Schema-First Development: Define CUE schemas before implementing Java classes
- Validation Layers: Use CUE for structural validation and Java annotations for behavioral validation
- Code Generation: Generate Java DTOs from CUE schemas for type safety
- Environment Separation: Maintain separate CUE files for different environments
- Testing: Test CUE configurations independently from Java code
- Documentation: Use CUE's built-in documentation features
- Version Control: Track CUE schemas alongside Java code
- CI/CD Integration: Validate CUE configurations in build pipelines
Conclusion
Integrating CUE with Java applications provides a powerful combination of type-safe configuration management, validation, and code generation. By leveraging CUE's strong typing and unification capabilities, Java teams can:
- Eliminate configuration errors through compile-time validation
- Manage complex configurations across multiple environments safely
- Generate type-safe Java code from configuration schemas
- Reduce boilerplate code for configuration validation
- Maintain consistency between configuration and application code
The patterns and examples in this guide demonstrate how to effectively integrate CUE into Java applications, from basic configuration loading to advanced code generation and Spring Boot integration. This approach brings the benefits of type-safe configuration to the Java ecosystem, improving reliability and developer productivity.
Pyroscope Profiling in Java
Explains how to use Pyroscope for continuous profiling in Java applications, helping developers analyze CPU and memory usage patterns to improve performance and identify bottlenecks.
https://macronepal.com/blog/pyroscope-profiling-in-java/
OpenTelemetry Metrics in Java: Comprehensive Guide
Provides a complete guide to collecting and exporting metrics in Java using OpenTelemetry, including counters, histograms, gauges, and integration with monitoring tools. (MACRO NEPAL)
https://macronepal.com/blog/opentelemetry-metrics-in-java-comprehensive-guide/
OTLP Exporter in Java: Complete Guide for OpenTelemetry
Explains how to configure OTLP exporters in Java to send telemetry data such as traces, metrics, and logs to monitoring systems using HTTP or gRPC protocols. (MACRO NEPAL)
https://macronepal.com/blog/otlp-exporter-in-java-complete-guide-for-opentelemetry/
Thanos Integration in Java: Global View of Metrics
Explains how to integrate Thanos with Java monitoring systems to create a scalable global metrics view across multiple Prometheus instances.
https://macronepal.com/blog/thanos-integration-in-java-global-view-of-metrics
Time Series with InfluxDB in Java: Complete Guide (Version 2)
Explains how to manage time-series data using InfluxDB in Java applications, including storing, querying, and analyzing metrics data.
https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide-2
Time Series with InfluxDB in Java: Complete Guide
Provides an overview of integrating InfluxDB with Java for time-series data handling, including monitoring applications and managing performance metrics.
https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide
Implementing Prometheus Remote Write in Java (Version 2)
Explains how to configure Java applications to send metrics data to Prometheus-compatible systems using the remote write feature for scalable monitoring.
https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide-2
Implementing Prometheus Remote Write in Java: Complete Guide
Provides instructions for sending metrics from Java services to Prometheus servers, enabling centralized monitoring and real-time analytics.
https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide
Building a TileServer GL in Java: Vector and Raster Tile Server
Explains how to build a TileServer GL in Java for serving vector and raster map tiles, useful for geographic visualization and mapping applications.
https://macronepal.com/blog/building-a-tileserver-gl-in-java-vector-and-raster-tile-server
Indoor Mapping in Java
Explains how to create indoor mapping systems in Java, including navigation inside buildings, spatial data handling, and visualization techniques.