Nacos Configuration Management in Java
/**
* POST TITLE: Nacos Configuration Management in Java
* 
* Complete implementation of Nacos for dynamic configuration management in Java applications
*/
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
public class NacosJavaIntegration {
/**
* Nacos Configuration Manager
*/
public static class NacosConfigManager {
private final String serverAddr;
private final String namespace;
private final ConfigService configService;
private final Map<String, List<ConfigListener>> listeners;
private final ScheduledExecutorService scheduler;
public NacosConfigManager(String serverAddr, String namespace) throws NacosException {
this.serverAddr = serverAddr;
this.namespace = namespace;
this.listeners = new ConcurrentHashMap<>();
this.scheduler = Executors.newScheduledThreadPool(2);
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
properties.put("namespace", namespace);
this.configService = NacosFactory.createConfigService(properties);
System.out.println("✅ Nacos Config Manager initialized: " + serverAddr);
}
/**
* Configuration Data Class
*/
public static class ConfigData {
private final String dataId;
private final String group;
private final String content;
private final String type;
private final long timestamp;
public ConfigData(String dataId, String group, String content, String type) {
this.dataId = dataId;
this.group = group;
this.content = content;
this.type = type;
this.timestamp = System.currentTimeMillis();
}
// Getters
public String getDataId() { return dataId; }
public String getGroup() { return group; }
public String getContent() { return content; }
public String getType() { return type; }
public long getTimestamp() { return timestamp; }
public <T> T parseAs(Class<T> clazz) {
// Implementation would use Jackson or similar for JSON/YAML parsing
return null; // Simplified
}
}
/**
* Configuration Listener Interface
*/
public interface ConfigListener {
void onConfigChanged(ConfigData newConfig);
void onConfigDeleted(String dataId, String group);
void onError(Exception exception);
}
/**
* Get configuration
*/
public ConfigData getConfig(String dataId, String group) throws NacosException {
return getConfig(dataId, group, 5000);
}
public ConfigData getConfig(String dataId, String group, long timeoutMs) throws NacosException {
String content = configService.getConfig(dataId, group, timeoutMs);
String type = inferConfigType(dataId);
return new ConfigData(dataId, group, content, type);
}
/**
* Get configuration with retry
*/
public ConfigData getConfigWithRetry(String dataId, String group, int maxRetries) throws NacosException {
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
return getConfig(dataId, group);
} catch (NacosException e) {
if (attempt == maxRetries) {
throw e;
}
System.out.println("🔄 Retry attempt " + attempt + " for: " + dataId);
try {
Thread.sleep(1000 * attempt);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new NacosException(NacosException.SERVER_ERROR, "Retry interrupted", ie);
}
}
}
throw new NacosException(NacosException.SERVER_ERROR, "All retry attempts failed");
}
/**
* Publish configuration
*/
public boolean publishConfig(String dataId, String group, String content) throws NacosException {
return publishConfig(dataId, group, content, inferConfigType(dataId));
}
public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException {
boolean success = configService.publishConfig(dataId, group, content, type);
if (success) {
System.out.println("✅ Configuration published: " + dataId + " in group: " + group);
} else {
System.out.println("❌ Failed to publish configuration: " + dataId);
}
return success;
}
/**
* Delete configuration
*/
public boolean deleteConfig(String dataId, String group) throws NacosException {
boolean success = configService.removeConfig(dataId, group);
if (success) {
System.out.println("✅ Configuration deleted: " + dataId + " from group: " + group);
notifyConfigDeleted(dataId, group);
}
return success;
}
/**
* Listen for configuration changes
*/
public void addListener(String dataId, String group, ConfigListener listener) throws NacosException {
String key = dataId + "|" + group;
listeners.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(listener);
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
ConfigData configData = new ConfigData(dataId, group, configInfo, inferConfigType(dataId));
notifyConfigChanged(dataId, group, configData);
}
@Override
public Executor getExecutor() {
return Executors.newSingleThreadExecutor();
}
});
System.out.println("👂 Listening for changes: " + dataId + " in group: " + group);
}
/**
* Remove listener
*/
public void removeListener(String dataId, String group, ConfigListener listener) {
String key = dataId + "|" + group;
List<ConfigListener> listenerList = listeners.get(key);
if (listenerList != null) {
listenerList.remove(listener);
if (listenerList.isEmpty()) {
listeners.remove(key);
}
}
}
/**
* Batch get configurations
*/
public Map<String, ConfigData> getConfigs(List<String> dataIds, String group) throws NacosException {
Map<String, ConfigData> configs = new HashMap<>();
for (String dataId : dataIds) {
try {
ConfigData config = getConfig(dataId, group);
configs.put(dataId, config);
} catch (NacosException e) {
System.err.println("❌ Failed to get config: " + dataId + " - " + e.getMessage());
}
}
return configs;
}
/**
* Batch publish configurations
*/
public Map<String, Boolean> publishConfigs(Map<String, String> configs, String group) throws NacosException {
Map<String, Boolean> results = new HashMap<>();
for (Map.Entry<String, String> entry : configs.entrySet()) {
try {
boolean success = publishConfig(entry.getKey(), group, entry.getValue());
results.put(entry.getKey(), success);
} catch (NacosException e) {
results.put(entry.getKey(), false);
System.err.println("❌ Failed to publish config: " + entry.getKey() + " - " + e.getMessage());
}
}
return results;
}
private String inferConfigType(String dataId) {
if (dataId.endsWith(".yaml") || dataId.endsWith(".yml")) {
return "yaml";
} else if (dataId.endsWith(".properties")) {
return "properties";
} else if (dataId.endsWith(".json")) {
return "json";
} else if (dataId.endsWith(".xml")) {
return "xml";
}
return "text";
}
private void notifyConfigChanged(String dataId, String group, ConfigData newConfig) {
String key = dataId + "|" + group;
List<ConfigListener> listenerList = listeners.get(key);
if (listenerList != null) {
for (ConfigListener listener : listenerList) {
try {
listener.onConfigChanged(newConfig);
} catch (Exception e) {
System.err.println("❌ Error in config listener: " + e.getMessage());
listener.onError(e);
}
}
}
}
private void notifyConfigDeleted(String dataId, String group) {
String key = dataId + "|" + group;
List<ConfigListener> listenerList = listeners.get(key);
if (listenerList != null) {
for (ConfigListener listener : listenerList) {
try {
listener.onConfigDeleted(dataId, group);
} catch (Exception e) {
System.err.println("❌ Error in config listener: " + e.getMessage());
listener.onError(e);
}
}
}
}
/**
* Health check
*/
public boolean healthCheck() {
try {
// Try to get server status
configService.getServerStatus();
return true;
} catch (NacosException e) {
return false;
}
}
/**
* Shutdown
*/
public void shutdown() {
try {
configService.shutDown();
scheduler.shutdown();
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (Exception e) {
System.err.println("❌ Error shutting down Nacos config manager: " + e.getMessage());
}
}
}
/**
* Configuration Properties Manager
*/
public static class ConfigPropertiesManager {
private final NacosConfigManager configManager;
private final Map<String, Properties> cachedProperties;
private final ScheduledExecutorService refreshScheduler;
public ConfigPropertiesManager(NacosConfigManager configManager) {
this.configManager = configManager;
this.cachedProperties = new ConcurrentHashMap<>();
this.refreshScheduler = Executors.newScheduledThreadPool(1);
startAutoRefresh();
}
/**
* Get properties from Nacos
*/
public Properties getProperties(String dataId, String group) throws NacosException {
String cacheKey = dataId + "|" + group;
Properties props = cachedProperties.get(cacheKey);
if (props == null) {
props = loadPropertiesFromNacos(dataId, group);
cachedProperties.put(cacheKey, props);
// Register for changes
configManager.addListener(dataId, group, new NacosConfigManager.ConfigListener() {
@Override
public void onConfigChanged(NacosConfigManager.ConfigData newConfig) {
try {
Properties newProps = parseProperties(newConfig.getContent(), newConfig.getType());
cachedProperties.put(cacheKey, newProps);
System.out.println("🔄 Properties updated: " + dataId);
} catch (Exception e) {
System.err.println("❌ Failed to parse updated properties: " + e.getMessage());
}
}
@Override
public void onConfigDeleted(String deletedDataId, String deletedGroup) {
cachedProperties.remove(cacheKey);
System.out.println("🗑️ Properties removed from cache: " + deletedDataId);
}
@Override
public void onError(Exception exception) {
System.err.println("❌ Error in properties listener: " + exception.getMessage());
}
});
}
return props;
}
/**
* Get property value
*/
public String getProperty(String dataId, String group, String key) throws NacosException {
Properties props = getProperties(dataId, group);
return props.getProperty(key);
}
public String getProperty(String dataId, String group, String key, String defaultValue) throws NacosException {
String value = getProperty(dataId, group, key);
return value != null ? value : defaultValue;
}
/**
* Get integer property
*/
public Integer getIntProperty(String dataId, String group, String key) throws NacosException {
String value = getProperty(dataId, group, key);
return value != null ? Integer.parseInt(value) : null;
}
public int getIntProperty(String dataId, String group, String key, int defaultValue) throws NacosException {
Integer value = getIntProperty(dataId, group, key);
return value != null ? value : defaultValue;
}
/**
* Get boolean property
*/
public Boolean getBooleanProperty(String dataId, String group, String key) throws NacosException {
String value = getProperty(dataId, group, key);
return value != null ? Boolean.parseBoolean(value) : null;
}
public boolean getBooleanProperty(String dataId, String group, String key, boolean defaultValue) throws NacosException {
Boolean value = getBooleanProperty(dataId, group, key);
return value != null ? value : defaultValue;
}
private Properties loadPropertiesFromNacos(String dataId, String group) throws NacosException {
NacosConfigManager.ConfigData config = configManager.getConfig(dataId, group);
return parseProperties(config.getContent(), config.getType());
}
private Properties parseProperties(String content, String type) {
Properties props = new Properties();
try {
if ("properties".equals(type)) {
props.load(new StringReader(content));
} else if ("yaml".equals(type) || "yml".equals(type)) {
// For YAML, we'd use SnakeYAML or similar
// This is a simplified implementation
parseYamlToProperties(content, props);
} else {
// For other types, treat as key=value
props.load(new StringReader(content));
}
} catch (Exception e) {
System.err.println("❌ Failed to parse properties: " + e.getMessage());
}
return props;
}
private void parseYamlToProperties(String yamlContent, Properties props) {
// Simplified YAML parsing - in production use SnakeYAML
String[] lines = yamlContent.split("\n");
for (String line : lines) {
line = line.trim();
if (!line.isEmpty() && !line.startsWith("#") && line.contains(":")) {
String[] parts = line.split(":", 2);
if (parts.length == 2) {
String key = parts[0].trim();
String value = parts[1].trim();
props.setProperty(key, value);
}
}
}
}
private void startAutoRefresh() {
// Refresh all cached properties every 5 minutes
refreshScheduler.scheduleAtFixedRate(() -> {
try {
refreshAllProperties();
} catch (Exception e) {
System.err.println("❌ Auto-refresh failed: " + e.getMessage());
}
}, 5, 5, TimeUnit.MINUTES);
}
private void refreshAllProperties() throws NacosException {
for (String cacheKey : cachedProperties.keySet()) {
String[] parts = cacheKey.split("\\|");
if (parts.length == 2) {
String dataId = parts[0];
String group = parts[1];
try {
Properties freshProps = loadPropertiesFromNacos(dataId, group);
cachedProperties.put(cacheKey, freshProps);
} catch (NacosException e) {
System.err.println("❌ Failed to refresh properties: " + dataId);
}
}
}
}
/**
* Clear cache
*/
public void clearCache() {
cachedProperties.clear();
System.out.println("🧹 Properties cache cleared");
}
/**
* Shutdown
*/
public void shutdown() {
refreshScheduler.shutdown();
try {
if (!refreshScheduler.awaitTermination(5, TimeUnit.SECONDS)) {
refreshScheduler.shutdownNow();
}
} catch (InterruptedException e) {
refreshScheduler.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
/**
* Spring Boot Integration
*/
@Configuration
@ConfigurationProperties(prefix = "nacos.config")
public static class NacosConfigProperties {
private String serverAddr = "localhost:8848";
private String namespace = "";
private String group = "DEFAULT_GROUP";
private String fileExtension = "properties";
private int timeout = 3000;
private boolean autoRefresh = true;
// Getters and setters
public String getServerAddr() { return serverAddr; }
public void setServerAddr(String serverAddr) { this.serverAddr = serverAddr; }
public String getNamespace() { return namespace; }
public void setNamespace(String namespace) { this.namespace = namespace; }
public String getGroup() { return group; }
public void setGroup(String group) { this.group = group; }
public String getFileExtension() { return fileExtension; }
public void setFileExtension(String fileExtension) { this.fileExtension = fileExtension; }
public int getTimeout() { return timeout; }
public void setTimeout(int timeout) { this.timeout = timeout; }
public boolean isAutoRefresh() { return autoRefresh; }
public void setAutoRefresh(boolean autoRefresh) { this.autoRefresh = autoRefresh; }
}
@SpringBootApplication
public static class NacosIntegrationApplication {
@Bean
public NacosConfigManager nacosConfigManager(NacosConfigProperties properties) throws NacosException {
return new NacosConfigManager(properties.getServerAddr(), properties.getNamespace());
}
@Bean
public ConfigPropertiesManager configPropertiesManager(NacosConfigManager configManager) {
return new ConfigPropertiesManager(configManager);
}
@Bean
@RefreshScope
public ApplicationConfig applicationConfig(ConfigPropertiesManager propertiesManager, 
NacosConfigProperties nacosProperties) throws NacosException {
return new ApplicationConfig(propertiesManager, nacosProperties.getGroup());
}
}
@RefreshScope
@Component
public static class ApplicationConfig {
private final ConfigPropertiesManager propertiesManager;
private final String group;
public ApplicationConfig(ConfigPropertiesManager propertiesManager, String group) {
this.propertiesManager = propertiesManager;
this.group = group;
}
@Value("${app.name:${spring.application.name:unknown}}")
private String appName;
public String getDatabaseUrl() {
try {
return propertiesManager.getProperty("datasource.properties", group, "url");
} catch (Exception e) {
return "jdbc:mysql://localhost:3306/mydb";
}
}
public String getDatabaseUsername() {
try {
return propertiesManager.getProperty("datasource.properties", group, "username");
} catch (Exception e) {
return "root";
}
}
public String getDatabasePassword() {
try {
return propertiesManager.getProperty("datasource.properties", group, "password");
} catch (Exception e) {
return "password";
}
}
public int getServerPort() {
try {
return propertiesManager.getIntProperty("server.properties", group, "port", 8080);
} catch (Exception e) {
return 8080;
}
}
public boolean isFeatureEnabled(String featureName) {
try {
return propertiesManager.getBooleanProperty("features.properties", group, featureName, false);
} catch (Exception e) {
return false;
}
}
public Map<String, String> getExternalServices() {
try {
Properties props = propertiesManager.getProperties("services.properties", group);
Map<String, String> services = new HashMap<>();
for (String key : props.stringPropertyNames()) {
if (key.startsWith("service.")) {
String serviceName = key.substring(8); // Remove "service." prefix
services.put(serviceName, props.getProperty(key));
}
}
return services;
} catch (Exception e) {
return Collections.emptyMap();
}
}
}
@RestController
@RequestMapping("/api/config")
public static class ConfigController {
private final NacosConfigManager configManager;
private final ConfigPropertiesManager propertiesManager;
private final ApplicationConfig applicationConfig;
private final NacosConfigProperties nacosProperties;
public ConfigController(NacosConfigManager configManager,
ConfigPropertiesManager propertiesManager,
ApplicationConfig applicationConfig,
NacosConfigProperties nacosProperties) {
this.configManager = configManager;
this.propertiesManager = propertiesManager;
this.applicationConfig = applicationConfig;
this.nacosProperties = nacosProperties;
}
@GetMapping("/{dataId}")
public ResponseEntity<?> getConfig(@PathVariable String dataId,
@RequestParam(defaultValue = "DEFAULT_GROUP") String group) {
try {
NacosConfigManager.ConfigData config = configManager.getConfig(dataId, group);
Map<String, Object> response = new HashMap<>();
response.put("dataId", config.getDataId());
response.put("group", config.getGroup());
response.put("type", config.getType());
response.put("timestamp", config.getTimestamp());
response.put("content", config.getContent());
return ResponseEntity.ok(response);
} catch (NacosException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", "Failed to get config: " + e.getMessage()));
}
}
@PostMapping("/{dataId}")
public ResponseEntity<String> publishConfig(@PathVariable String dataId,
@RequestParam(defaultValue = "DEFAULT_GROUP") String group,
@RequestBody Map<String, String> request) {
try {
String content = request.get("content");
String type = request.getOrDefault("type", inferTypeFromDataId(dataId));
boolean success = configManager.publishConfig(dataId, group, content, type);
if (success) {
return ResponseEntity.ok("Configuration published successfully");
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to publish configuration");
}
} catch (NacosException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to publish config: " + e.getMessage());
}
}
@DeleteMapping("/{dataId}")
public ResponseEntity<String> deleteConfig(@PathVariable String dataId,
@RequestParam(defaultValue = "DEFAULT_GROUP") String group) {
try {
boolean success = configManager.deleteConfig(dataId, group);
if (success) {
return ResponseEntity.ok("Configuration deleted successfully");
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to delete configuration");
}
} catch (NacosException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to delete config: " + e.getMessage());
}
}
@GetMapping("/properties/{dataId}")
public ResponseEntity<?> getProperties(@PathVariable String dataId,
@RequestParam(defaultValue = "DEFAULT_GROUP") String group) {
try {
Properties props = propertiesManager.getProperties(dataId, group);
return ResponseEntity.ok(props);
} catch (NacosException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", "Failed to get properties: " + e.getMessage()));
}
}
@GetMapping("/app/config")
public ResponseEntity<Map<String, Object>> getApplicationConfig() {
Map<String, Object> config = new HashMap<>();
config.put("databaseUrl", applicationConfig.getDatabaseUrl());
config.put("databaseUsername", applicationConfig.getDatabaseUsername());
config.put("serverPort", applicationConfig.getServerPort());
config.put("externalServices", applicationConfig.getExternalServices());
// Check some feature flags
config.put("featureAEnabled", applicationConfig.isFeatureEnabled("featureA"));
config.put("featureBEnabled", applicationConfig.isFeatureEnabled("featureB"));
return ResponseEntity.ok(config);
}
@PostMapping("/cache/clear")
public ResponseEntity<String> clearCache() {
propertiesManager.clearCache();
return ResponseEntity.ok("Cache cleared successfully");
}
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> healthCheck() {
Map<String, Object> health = new HashMap<>();
health.put("status", configManager.healthCheck() ? "UP" : "DOWN");
health.put("server", nacosProperties.getServerAddr());
health.put("namespace", nacosProperties.getNamespace());
health.put("timestamp", new Date());
return ResponseEntity.ok(health);
}
private String inferTypeFromDataId(String dataId) {
if (dataId.endsWith(".yaml") || dataId.endsWith(".yml")) return "yaml";
if (dataId.endsWith(".properties")) return "properties";
if (dataId.endsWith(".json")) return "json";
if (dataId.endsWith(".xml")) return "xml";
return "text";
}
}
/**
* Configuration Examples Generator
*/
public static class ConfigExamplesGenerator {
private final NacosConfigManager configManager;
public ConfigExamplesGenerator(NacosConfigManager configManager) {
this.configManager = configManager;
}
/**
* Generate sample configurations for a microservices application
*/
public void generateSampleConfigurations() throws NacosException {
// Database configuration
String datasourceConfig = """
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=admin
spring.datasource.password=secret123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
""";
configManager.publishConfig("datasource.properties", "DEFAULT_GROUP", datasourceConfig);
// Server configuration
String serverConfig = """
server.port=8080
server.servlet.context-path=/api
spring.mvc.format.date=yyyy-MM-dd
logging.level.com.example=DEBUG
""";
configManager.publishConfig("server.properties", "DEFAULT_GROUP", serverConfig);
// Feature flags
String featuresConfig = """
feature.payment.enabled=true
feature.notification.enabled=false
feature.analytics.enabled=true
feature.experimental.ui=false
""";
configManager.publishConfig("features.properties", "DEFAULT_GROUP", featuresConfig);
// External services
String servicesConfig = """
service.payment.url=https://payment-service:8081
service.user.url=https://user-service:8082
service.product.url=https://product-service:8083
service.notification.url=https://notification-service:8084
""";
configManager.publishConfig("services.properties", "DEFAULT_GROUP", servicesConfig);
// YAML configuration example
String applicationYaml = """
app:
name: order-service
version: 1.0.0
environment: development
database:
primary:
host: localhost
port: 5432
name: order_db
replica:
host: replica-host
port: 5432
name: order_db_replica
cache:
redis:
host: redis-host
port: 6379
timeout: 5000
""";
configManager.publishConfig("application.yaml", "DEFAULT_GROUP", applicationYaml, "yaml");
System.out.println("✅ Sample configurations generated successfully");
}
/**
* Generate configuration for specific service
*/
public void generateServiceConfig(String serviceName, int port, String databaseUrl) throws NacosException {
String serviceConfig = String.format("""
spring.application.name=%s
server.port=%d
spring.datasource.url=%s
spring.datasource.username=service_user
spring.datasource.password=service_pass_123
logging.level.com.%s=INFO
management.endpoints.web.exposure.include=health,info,metrics
""", serviceName, port, databaseUrl, serviceName);
String dataId = serviceName + ".properties";
configManager.publishConfig(dataId, "DEFAULT_GROUP", serviceConfig);
System.out.println("✅ Service configuration generated: " + dataId);
}
}
/**
* Demo and Usage Examples
*/
public static void main(String[] args) {
System.out.println("☁️  Nacos Configuration Management in Java");
System.out.println("==========================================\n");
try {
// Demo 1: Initialize Nacos Config Manager
System.out.println("1. ⚙️  Initializing Nacos Config Manager");
NacosConfigManager configManager = new NacosConfigManager("localhost:8848", "");
System.out.println("   Nacos server: localhost:8848");
System.out.println("   Health check: " + (configManager.healthCheck() ? "✅" : "❌"));
// Demo 2: Configuration Operations
System.out.println("\n2. 📝 Configuration Operations");
ConfigExamplesGenerator examplesGenerator = new ConfigExamplesGenerator(configManager);
examplesGenerator.generateSampleConfigurations();
// Demo 3: Properties Management
System.out.println("\n3. 🗂️  Properties Management");
ConfigPropertiesManager propsManager = new ConfigPropertiesManager(configManager);
Properties datasourceProps = propsManager.getProperties("datasource.properties", "DEFAULT_GROUP");
System.out.println("   Database URL: " + datasourceProps.getProperty("spring.datasource.url"));
System.out.println("   Database User: " + datasourceProps.getProperty("spring.datasource.username"));
// Demo 4: Configuration Listening
System.out.println("\n4. 👂 Configuration Change Listening");
configManager.addListener("features.properties", "DEFAULT_GROUP", 
new NacosConfigManager.ConfigListener() {
@Override
public void onConfigChanged(NacosConfigManager.ConfigData newConfig) {
System.out.println("   🔄 Features configuration changed!");
System.out.println("   New content: " + newConfig.getContent().substring(0, 50) + "...");
}
@Override
public void onConfigDeleted(String dataId, String group) {
System.out.println("   🗑️ Configuration deleted: " + dataId);
}
@Override
public void onError(Exception exception) {
System.err.println("   ❌ Listener error: " + exception.getMessage());
}
});
System.out.println("   Listening for changes to features.properties");
// Demo 5: Spring-like Configuration
System.out.println("\n5. 🌱 Spring-like Configuration Management");
ApplicationConfig appConfig = new ApplicationConfig(propsManager, "DEFAULT_GROUP");
System.out.println("   Database URL: " + appConfig.getDatabaseUrl());
System.out.println("   Server Port: " + appConfig.getServerPort());
// Demo 6: Cleanup
System.out.println("\n6. 🧹 Cleanup");
propsManager.shutdown();
configManager.shutdown();
System.out.println("\n✅ Nacos Configuration Demo Completed");
System.out.println("\n📚 Key Features:");
System.out.println("   - Dynamic configuration management");
System.out.println("   - Real-time configuration updates");
System.out.println("   - Multiple configuration formats (Properties, YAML, JSON)");
System.out.println("   - Spring Boot integration with @RefreshScope");
System.out.println("   - Configuration change listeners");
System.out.println("   - Health monitoring and auto-refresh");
System.out.println("   - Batch operations for efficiency");
} catch (Exception e) {
System.err.println("❌ Demo failed: " + e.getMessage());
e.printStackTrace();
}
}
}

Maven Dependencies

<!-- pom.xml -->
<dependencies>
<!-- Nacos Client -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.2.3</version>
</dependency>
<!-- Spring Cloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2022.0.0.0</version>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.7.0</version>
</dependency>
<!-- Spring Cloud Context (for @RefreshScope) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>3.1.5</version>
</dependency>
</dependencies>

Configuration Examples

application.yml (Spring Boot)

spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: localhost:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml
refresh-enabled: true
config:
import: optional:nacos:application.yaml
server:
port: 8080
management:
endpoints:
web:
exposure:
include: health,info,metrics,refresh

Nacos Configuration Examples

application.yaml in Nacos

app:
name: order-service
version: 1.0.0
environment: production
database:
primary:
host: db-primary.example.com
port: 5432
name: order_db
replica:
host: db-replica.example.com
port: 5432
name: order_db_replica
redis:
host: redis-cluster.example.com
port: 6379
password: ${REDIS_PASSWORD}
timeout: 5000
features:
payment-enabled: true
notification-enabled: false
analytics-enabled: true

datasource.properties in Nacos

spring.datasource.url=jdbc:postgresql://db-primary.example.com:5432/order_db
spring.datasource.username=order_service
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false

features.properties in Nacos

feature.payment.enabled=true
feature.notification.enabled=false
feature.analytics.enabled=true
feature.experimental.ui=false
feature.rate.limiting.enabled=true

Usage Examples

// Initialize Nacos config manager
NacosConfigManager configManager = new NacosConfigManager("localhost:8848", "dev");
// Get configuration
NacosConfigManager.ConfigData config = configManager.getConfig("application.yaml", "DEFAULT_GROUP");
System.out.println("Config content: " + config.getContent());
// Listen for changes
configManager.addListener("application.yaml", "DEFAULT_GROUP", 
new NacosConfigManager.ConfigListener() {
@Override
public void onConfigChanged(NacosConfigManager.ConfigData newConfig) {
System.out.println("Configuration updated!");
// Reload application configuration
reloadApplicationConfig();
}
@Override
public void onConfigDeleted(String dataId, String group) {
System.out.println("Configuration deleted: " + dataId);
}
@Override
public void onError(Exception exception) {
System.err.println("Error in config listener: " + exception.getMessage());
}
});
// Use with Spring Boot
@RefreshScope
@Component
public class DatabaseConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
// Configuration will be automatically refreshed when changed in Nacos
}

This Nacos configuration management implementation provides:

  1. Complete Nacos Integration with dynamic configuration management
  2. Multiple Configuration Formats support (Properties, YAML, JSON, XML)
  3. Real-time Configuration Updates with listener pattern
  4. Spring Boot Integration with @RefreshScope support
  5. Configuration Caching with automatic refresh
  6. Health Monitoring and connection management
  7. Batch Operations for efficient configuration handling
  8. Error Handling with retry mechanisms
  9. Namespace and Group Management for multi-environment support
  10. REST API for configuration management

The solution enables Java applications to leverage Nacos for centralized configuration management with dynamic updates and Spring Boot integration for seamless adoption.

Java Logistics, Shipping Integration & Enterprise Inventory Automation (Tracking, ERP, RFID & Billing Systems)

https://macronepal.com/blog/aftership-tracking-in-java-enterprise-package-visibility/
Explains how to integrate AfterShip tracking services into Java applications to provide real-time shipment visibility, delivery status updates, and centralized tracking across multiple courier services.

https://macronepal.com/blog/shipping-integration-using-fedex-api-with-java-for-logistics-automation/
Explains how to integrate the FedEx API into Java systems to automate shipping tasks such as creating shipments, calculating delivery costs, generating shipping labels, and tracking packages.

https://macronepal.com/blog/shipping-and-logistics-integrating-ups-apis-with-java-applications/
Explains UPS API integration in Java to enable automated shipping operations including rate calculation, shipment scheduling, tracking, and delivery confirmation management.

https://macronepal.com/blog/generating-and-reading-qr-codes-for-products-in-java/
Explains how Java applications generate and read QR codes for product identification, tracking, and authentication, supporting faster inventory handling and product verification processes.

https://macronepal.com/blog/designing-a-robust-pick-and-pack-workflow-in-java/
Explains how to design an efficient pick-and-pack workflow in Java warehouse systems, covering order processing, item selection, packaging steps, and logistics preparation to improve fulfillment efficiency.

https://macronepal.com/blog/rfid-inventory-management-system-in-java-a-complete-guide/
Explains how RFID technology integrates with Java applications to automate inventory tracking, reduce manual errors, and enable real-time stock monitoring in warehouses and retail environments.

https://macronepal.com/blog/erp-integration-with-odoo-in-java/
Explains how Java applications connect with Odoo ERP systems to synchronize inventory, orders, customer records, and financial data across enterprise systems.

https://macronepal.com/blog/automated-invoice-generation-creating-professional-excel-invoices-with-apache-poi-in-java/
Explains how to automatically generate professional Excel invoices in Java using Apache POI, enabling structured billing documents and automated financial record creation.

https://macronepal.com/blog/enterprise-financial-integration-using-quickbooks-api-in-java-applications/
Explains QuickBooks API integration in Java to automate financial workflows such as invoice management, payment tracking, accounting synchronization, and financial reporting.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper