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.