docs
This documentation has been enhanced for AI coding agents with comprehensive examples, complete API signatures, thread safety notes, error handling patterns, and production-ready usage patterns.
Package: org.springframework.boot.cloud
Module: org.springframework.boot:spring-boot
Since: 1.3.0
Spring Boot's cloud platform detection automatically identifies the cloud environment where an application is running and configures appropriate settings. This enables cloud-specific optimizations and feature toggling without code changes.
The cloud platform system provides:
Enum representing supported cloud platforms with detection logic.
package org.springframework.boot.cloud;
import org.springframework.core.env.Environment;
/**
* Simple detection for well known cloud platforms. Detection can be forced using the
* "spring.main.cloud-platform" configuration property.
*
* Each platform has unique environment variable patterns for detection.
*
* @since 1.3.0
*/
public enum CloudPlatform {
/**
* No cloud platform detected.
*/
NONE {
@Override
public boolean isDetected(Environment environment) {
return false;
}
},
/**
* Cloud Foundry platform.
* Detected via VCAP_APPLICATION or VCAP_SERVICES environment variables.
*/
CLOUD_FOUNDRY {
@Override
public boolean isDetected(Environment environment) {
return environment.containsProperty("VCAP_APPLICATION") ||
environment.containsProperty("VCAP_SERVICES");
}
},
/**
* Heroku platform.
* Detected via DYNO environment variable.
*/
HEROKU {
@Override
public boolean isDetected(Environment environment) {
return environment.containsProperty("DYNO");
}
},
/**
* SAP Cloud Platform.
* Detected via HC_LANDSCAPE environment variable.
*/
SAP {
@Override
public boolean isDetected(Environment environment) {
return environment.containsProperty("HC_LANDSCAPE");
}
},
/**
* Kubernetes platform.
* Detected via KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT
* environment variables.
*/
KUBERNETES {
@Override
public boolean isDetected(Environment environment) {
return environment.containsProperty("KUBERNETES_SERVICE_HOST") &&
environment.containsProperty("KUBERNETES_SERVICE_PORT");
}
},
/**
* Microsoft Azure platform.
* Detected via WEBSITE_INSTANCE_ID environment variable (Azure App Service).
*
* @since 3.4.0
*/
AZURE {
@Override
public boolean isDetected(Environment environment) {
return environment.containsProperty("WEBSITE_INSTANCE_ID");
}
},
/**
* Amazon Web Services platform.
* Detected via AWS_EXECUTION_ENV environment variable or specific Lambda
* environment variables.
*
* @since 3.4.0
*/
AMAZON_WEB_SERVICES {
@Override
public boolean isDetected(Environment environment) {
return environment.containsProperty("AWS_EXECUTION_ENV") ||
environment.containsProperty("AWS_LAMBDA_FUNCTION_NAME");
}
};
/**
* Determines if the platform is detected in the environment.
*
* @param environment the environment to check
* @return true if this platform is active
*/
public abstract boolean isDetected(Environment environment);
/**
* Returns the active CloudPlatform or NONE if not running on a known platform.
* Checks platforms in order: forced via property, then each platform's detection.
*
* @param environment the environment
* @return the active platform (never null)
*/
public static CloudPlatform getActive(Environment environment) {
// First check for forced platform via spring.main.cloud-platform
String forcedPlatform = environment.getProperty("spring.main.cloud-platform");
if (forcedPlatform != null) {
return valueOf(forcedPlatform.toUpperCase().replace('-', '_'));
}
// Auto-detect platform
for (CloudPlatform platform : values()) {
if (platform.isDetected(environment)) {
return platform;
}
}
return NONE;
}
/**
* Returns true if the platform is active in the given environment.
*
* @param environment the environment
* @return true if this platform is active
*/
public boolean isActive(Environment environment) {
return getActive(environment) == this;
}
/**
* Returns true if running on any cloud platform (not NONE).
*
* @param environment the environment
* @return true if running on a cloud platform
* @since 3.0.0
*/
public static boolean isCloudPlatform(Environment environment) {
return getActive(environment) != NONE;
}
}Environment post-processor that parses Cloud Foundry VCAP variables.
package org.springframework.boot.cloud;
import org.springframework.boot.EnvironmentPostProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* An EnvironmentPostProcessor that knows where to find VCAP (Cloud Foundry) metadata
* in the existing environment. Parses VCAP_APPLICATION and VCAP_SERVICES metadata
* and flattens it into properties that are easily consumed by Environment users.
*
* VCAP_APPLICATION is a shallow hash with basic application info (name, instance id, etc.)
* VCAP_SERVICES is a hash of lists where keys are service labels and values are
* lists of service instance metadata.
*
* Properties are flattened as:
* - VCAP_APPLICATION -> vcap.application.*
* - VCAP_SERVICES -> vcap.services.{service-name}.*
*
* Thread Safety: Thread-safe. Can be called concurrently.
*
* @since 1.3.0
*/
public class CloudFoundryVcapEnvironmentPostProcessor
implements EnvironmentPostProcessor, Ordered {
/**
* Create a new CloudFoundryVcapEnvironmentPostProcessor instance.
* @param logFactory the log factory to use
* @since 3.0.0
*/
public CloudFoundryVcapEnvironmentPostProcessor(DeferredLogFactory logFactory) {
}
/**
* Set the order of this post-processor.
* Default: Before ConfigDataEnvironmentPostProcessor (ORDER - 5).
* @param order the order value
*/
public void setOrder(int order) {
}
@Override
public int getOrder() {
// Returns ConfigDataEnvironmentPostProcessor.ORDER - 5
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// Parses VCAP_APPLICATION and VCAP_SERVICES if running on Cloud Foundry
// Adds flattened properties to environment as "vcap" property source
}
}Example VCAP Properties:
# VCAP_APPLICATION properties
vcap.application.instance_id=2ce0ac627a6c8e47e936d829a3a47b5b
vcap.application.instance_index=0
vcap.application.name=myapp
vcap.application.uris[0]=myapp.cfapps.io
vcap.application.version=0138c4a6-2a73-416b-aca0-572c09f7ca53
# VCAP_SERVICES properties (flattened by service name)
vcap.services.mysql.name=mysql
vcap.services.mysql.label=rds-mysql-1.0
vcap.services.mysql.plan=10mb
vcap.services.mysql.credentials.hostname=mysql-service.aws.com
vcap.services.mysql.credentials.port=3306
vcap.services.mysql.credentials.username=dbuser
vcap.services.mysql.credentials.password=dbpass
vcap.services.mysql.credentials.name=mydbUsage in Application:
@Component
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class CloudFoundryServiceBinder {
@Value("${vcap.services.mysql.credentials.hostname}")
private String dbHost;
@Value("${vcap.services.mysql.credentials.port}")
private int dbPort;
@Value("${vcap.services.mysql.credentials.username}")
private String dbUsername;
@Value("${vcap.services.mysql.credentials.password}")
private String dbPassword;
@PostConstruct
public void bindServices() {
System.out.printf("Connecting to MySQL: %s:%d%n", dbHost, dbPort);
// Configure DataSource with VCAP credentials
}
}package com.example.config;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* Component that detects and reports cloud platform.
*/
@Component
public class PlatformDetector {
private final Environment environment;
public PlatformDetector(Environment environment) {
this.environment = environment;
}
public void detectPlatform() {
CloudPlatform platform = CloudPlatform.getActive(environment);
System.out.println("Running on: " + platform);
switch (platform) {
case KUBERNETES:
System.out.println("Kubernetes detected - using pod-based configuration");
break;
case CLOUD_FOUNDRY:
System.out.println("Cloud Foundry detected - using VCAP services");
break;
case HEROKU:
System.out.println("Heroku detected - using dyno configuration");
break;
case AMAZON_WEB_SERVICES:
System.out.println("AWS detected - using cloud-native features");
break;
case AZURE:
System.out.println("Azure detected - using App Service configuration");
break;
case NONE:
System.out.println("No cloud platform detected - using default configuration");
break;
}
}
public boolean isCloud() {
return CloudPlatform.isCloudPlatform(environment);
}
public boolean isKubernetes() {
return CloudPlatform.KUBERNETES.isActive(environment);
}
}package com.example.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Platform-specific configuration beans.
*/
@Configuration
public class CloudPlatformConfig {
/**
* Kubernetes-specific configuration.
*/
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public ServiceDiscovery kubernetesServiceDiscovery() {
System.out.println("Configuring Kubernetes service discovery");
return new KubernetesServiceDiscovery();
}
/**
* Cloud Foundry-specific configuration.
*/
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public ServiceDiscovery cloudFoundryServiceDiscovery() {
System.out.println("Configuring Cloud Foundry service discovery");
return new CloudFoundryServiceDiscovery();
}
/**
* AWS-specific configuration.
*/
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.AMAZON_WEB_SERVICES)
public ServiceDiscovery awsServiceDiscovery() {
System.out.println("Configuring AWS service discovery");
return new AwsServiceDiscovery();
}
/**
* Configuration for non-cloud environments.
*/
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.NONE)
public ServiceDiscovery localServiceDiscovery() {
System.out.println("Configuring local service discovery");
return new LocalServiceDiscovery();
}
}# application.properties
# Force specific platform (useful for testing)
spring.main.cloud-platform=kubernetes
# Or disable cloud detection
spring.main.cloud-platform=none# application.yml
spring:
main:
cloud-platform: kubernetes # kubernetes, cloud-foundry, heroku, aws, azure, nonepackage com.example.config;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* Configuration that adapts based on cloud platform.
*/
@Component
@ConfigurationProperties(prefix = "app")
public class AdaptiveConfiguration {
private final Environment environment;
private String serviceUrl;
private int timeout;
public AdaptiveConfiguration(Environment environment) {
this.environment = environment;
adaptToPlatform();
}
private void adaptToPlatform() {
CloudPlatform platform = CloudPlatform.getActive(environment);
switch (platform) {
case KUBERNETES:
// Use service DNS names in Kubernetes
serviceUrl = "http://backend-service:8080";
timeout = 5000;
break;
case CLOUD_FOUNDRY:
// Use VCAP_SERVICES in Cloud Foundry
serviceUrl = resolveFromVcapServices();
timeout = 10000;
break;
case HEROKU:
// Use Heroku config vars
serviceUrl = environment.getProperty("BACKEND_URL",
"https://backend.herokuapp.com");
timeout = 30000;
break;
case AMAZON_WEB_SERVICES:
// Use AWS service endpoints
serviceUrl = resolveFromAwsServices();
timeout = 5000;
break;
default:
// Local development defaults
serviceUrl = "http://localhost:8080";
timeout = 30000;
}
}
private String resolveFromVcapServices() {
// Parse VCAP_SERVICES JSON
return "http://backend-service";
}
private String resolveFromAwsServices() {
// Use AWS service discovery
return "http://backend.internal";
}
public String getServiceUrl() {
return serviceUrl;
}
public int getTimeout() {
return timeout;
}
}package com.example.health;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
/**
* Health indicator including cloud platform information.
*/
@Component
public class PlatformHealthIndicator implements HealthIndicator {
private final Environment environment;
public PlatformHealthIndicator(Environment environment) {
this.environment = environment;
}
@Override
public Health health() {
CloudPlatform platform = CloudPlatform.getActive(environment);
return Health.up()
.withDetail("platform", platform.name())
.withDetail("isCloud", CloudPlatform.isCloudPlatform(environment))
.withDetail("environment", determineEnvironment(platform))
.build();
}
private String determineEnvironment(CloudPlatform platform) {
if (platform == CloudPlatform.NONE) {
return "local";
}
return environment.getProperty("spring.profiles.active", "unknown");
}
}package com.example.features;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
/**
* Service with platform-aware feature flags.
*/
@Service
public class FeatureToggleService {
private final Environment environment;
public FeatureToggleService(Environment environment) {
this.environment = environment;
}
public boolean isDistributedTracingEnabled() {
// Enable in cloud environments
return CloudPlatform.isCloudPlatform(environment);
}
public boolean isServiceMeshEnabled() {
// Only enable in Kubernetes
return CloudPlatform.KUBERNETES.isActive(environment);
}
public boolean isAutoScalingEnabled() {
CloudPlatform platform = CloudPlatform.getActive(environment);
// Enable for cloud platforms that support auto-scaling
return platform == CloudPlatform.KUBERNETES ||
platform == CloudPlatform.CLOUD_FOUNDRY ||
platform == CloudPlatform.AMAZON_WEB_SERVICES ||
platform == CloudPlatform.AZURE;
}
public boolean useCloudLoadBalancer() {
CloudPlatform platform = CloudPlatform.getActive(environment);
// Use cloud load balancers instead of client-side
return platform != CloudPlatform.NONE;
}
}KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443VCAP_APPLICATION={"application_id":"..."}
VCAP_SERVICES={"mysql":[{"credentials":{...}}]}DYNO=web.1
PORT=5000AWS_EXECUTION_ENV=AWS_Lambda_java11
AWS_LAMBDA_FUNCTION_NAME=my-function
AWS_REGION=us-east-1WEBSITE_INSTANCE_ID=abcd1234
WEBSITE_SITE_NAME=myapppackage com.example;
import org.junit.jupiter.api.Test;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for cloud platform detection.
*/
@SpringBootTest
class CloudPlatformDetectionTest {
@Autowired
private Environment environment;
@Test
@TestPropertySource(properties = {
"KUBERNETES_SERVICE_HOST=10.0.0.1",
"KUBERNETES_SERVICE_PORT=443"
})
void detectsKubernetes() {
CloudPlatform platform = CloudPlatform.getActive(environment);
assertThat(platform).isEqualTo(CloudPlatform.KUBERNETES);
}
@Test
@TestPropertySource(properties = {
"spring.main.cloud-platform=cloud-foundry"
})
void forcesCloudFoundry() {
CloudPlatform platform = CloudPlatform.getActive(environment);
assertThat(platform).isEqualTo(CloudPlatform.CLOUD_FOUNDRY);
}
@Test
void detectsNoneByDefault() {
CloudPlatform platform = CloudPlatform.getActive(environment);
assertThat(platform).isEqualTo(CloudPlatform.NONE);
}
}Complete configuration system that adapts to detected cloud platform:
package com.example.cloud;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Manages platform-specific configuration with fallback strategies.
*/
@Component
public class MultiCloudConfigManager {
private final Environment environment;
private final CloudPlatform platform;
private final Map<String, String> configCache = new ConcurrentHashMap<>();
public MultiCloudConfigManager(Environment environment) {
this.environment = environment;
this.platform = CloudPlatform.getActive(environment);
initializeConfiguration();
}
private void initializeConfiguration() {
System.out.println("Initializing configuration for platform: " + platform);
switch (platform) {
case KUBERNETES:
loadKubernetesConfig();
break;
case CLOUD_FOUNDRY:
loadCloudFoundryConfig();
break;
case AMAZON_WEB_SERVICES:
loadAwsConfig();
break;
case AZURE:
loadAzureConfig();
break;
case HEROKU:
loadHerokuConfig();
break;
default:
loadLocalConfig();
}
}
private void loadKubernetesConfig() {
// Service discovery via DNS
configCache.put("service.discovery.type", "kubernetes-dns");
configCache.put("service.backend.url",
"http://backend-service.default.svc.cluster.local:8080");
// ConfigMap and Secret paths
String configPath = "/etc/config";
String secretPath = "/etc/secrets";
configCache.put("config.path", configPath);
configCache.put("secret.path", secretPath);
// Health check endpoints
configCache.put("management.health.livenessState.enabled", "true");
configCache.put("management.health.readinessState.enabled", "true");
}
private void loadCloudFoundryConfig() {
// VCAP_SERVICES parsing
String vcapServices = environment.getProperty("VCAP_SERVICES");
if (vcapServices != null) {
configCache.put("service.discovery.type", "cloud-foundry-vcap");
parseVcapServices(vcapServices);
}
// Cloud Foundry specific settings
configCache.put("server.port", environment.getProperty("PORT", "8080"));
configCache.put("service.registry.url",
environment.getProperty("EUREKA_URL", "http://eureka-server"));
}
private void loadAwsConfig() {
// AWS service endpoints
String region = environment.getProperty("AWS_REGION", "us-east-1");
configCache.put("cloud.aws.region.static", region);
configCache.put("service.discovery.type", "aws-service-discovery");
// Lambda-specific configuration
if (environment.containsProperty("AWS_LAMBDA_FUNCTION_NAME")) {
configCache.put("spring.main.web-application-type", "none");
configCache.put("logging.level.root", "INFO");
}
// ECS-specific configuration
if (environment.containsProperty("AWS_EXECUTION_ENV")) {
configCache.put("service.backend.url",
"http://backend.internal:8080");
}
}
private void loadAzureConfig() {
// Azure App Service configuration
String instanceId = environment.getProperty("WEBSITE_INSTANCE_ID");
String siteName = environment.getProperty("WEBSITE_SITE_NAME");
configCache.put("cloud.azure.instance.id", instanceId);
configCache.put("cloud.azure.site.name", siteName);
configCache.put("service.discovery.type", "azure-app-service");
// Azure-specific endpoints
configCache.put("service.backend.url",
String.format("https://%s-backend.azurewebsites.net", siteName));
}
private void loadHerokuConfig() {
// Heroku dyno configuration
String dyno = environment.getProperty("DYNO");
configCache.put("heroku.dyno", dyno);
// Database URL from Heroku
String databaseUrl = environment.getProperty("DATABASE_URL");
if (databaseUrl != null) {
configCache.put("spring.datasource.url", databaseUrl);
}
// Service URLs from config vars
configCache.put("service.backend.url",
environment.getProperty("BACKEND_URL", "http://localhost:8080"));
}
private void loadLocalConfig() {
configCache.put("service.discovery.type", "local");
configCache.put("service.backend.url", "http://localhost:8080");
configCache.put("spring.datasource.url",
"jdbc:postgresql://localhost:5432/devdb");
}
private void parseVcapServices(String vcapJson) {
// Parse VCAP_SERVICES JSON and extract service bindings
// Simplified for example - use proper JSON parsing in production
configCache.put("vcap.parsed", "true");
}
public String getConfig(String key) {
return configCache.getOrDefault(key,
environment.getProperty(key));
}
public String getConfig(String key, String defaultValue) {
return configCache.getOrDefault(key,
environment.getProperty(key, defaultValue));
}
public CloudPlatform getPlatform() {
return platform;
}
public boolean isCloud() {
return platform != CloudPlatform.NONE;
}
public Map<String, String> getAllConfig() {
return new HashMap<>(configCache);
}
}Service discovery implementation that adapts to cloud platform:
package com.example.discovery;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Platform-aware service discovery with caching and fallback.
*/
@Service
public class PlatformAwareServiceDiscovery {
private final Environment environment;
private final CloudPlatform platform;
private final HttpClient httpClient;
private final ConcurrentMap<String, ServiceEndpoint> cache;
public PlatformAwareServiceDiscovery(Environment environment) {
this.environment = environment;
this.platform = CloudPlatform.getActive(environment);
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
this.cache = new ConcurrentHashMap<>();
System.out.println("Service discovery initialized for: " + platform);
}
public ServiceEndpoint discoverService(String serviceName) {
// Check cache first
ServiceEndpoint cached = cache.get(serviceName);
if (cached != null && !cached.isExpired()) {
return cached;
}
// Discover based on platform
ServiceEndpoint endpoint = switch (platform) {
case KUBERNETES -> discoverKubernetesService(serviceName);
case CLOUD_FOUNDRY -> discoverCloudFoundryService(serviceName);
case AMAZON_WEB_SERVICES -> discoverAwsService(serviceName);
case AZURE -> discoverAzureService(serviceName);
case HEROKU -> discoverHerokuService(serviceName);
default -> discoverLocalService(serviceName);
};
// Cache the result
if (endpoint != null) {
cache.put(serviceName, endpoint);
}
return endpoint;
}
private ServiceEndpoint discoverKubernetesService(String serviceName) {
// Kubernetes DNS-based service discovery
String namespace = environment.getProperty("kubernetes.namespace", "default");
String domain = environment.getProperty("kubernetes.cluster.domain",
"svc.cluster.local");
String url = String.format("http://%s.%s.%s", serviceName, namespace, domain);
// Try to resolve the service
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url + "/actuator/health"))
.timeout(Duration.ofSeconds(3))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return new ServiceEndpoint(serviceName, url, platform,
Duration.ofMinutes(5));
}
} catch (Exception e) {
System.err.println("Failed to discover Kubernetes service: " + e.getMessage());
}
return null;
}
private ServiceEndpoint discoverCloudFoundryService(String serviceName) {
// Cloud Foundry service discovery via VCAP_SERVICES
String vcapServices = environment.getProperty("VCAP_SERVICES");
if (vcapServices != null) {
// Parse VCAP_SERVICES and find service by name
// Simplified - use proper JSON parsing in production
String url = parseVcapServicesForUrl(vcapServices, serviceName);
if (url != null) {
return new ServiceEndpoint(serviceName, url, platform,
Duration.ofMinutes(10));
}
}
// Fallback to environment variable
String envUrl = environment.getProperty(serviceName.toUpperCase() + "_URL");
if (envUrl != null) {
return new ServiceEndpoint(serviceName, envUrl, platform,
Duration.ofMinutes(10));
}
return null;
}
private ServiceEndpoint discoverAwsService(String serviceName) {
// AWS service discovery via Cloud Map or ECS service discovery
String region = environment.getProperty("AWS_REGION", "us-east-1");
// Try ECS service discovery first
String ecsServiceUrl = String.format("http://%s.internal:8080", serviceName);
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(ecsServiceUrl + "/actuator/health"))
.timeout(Duration.ofSeconds(3))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return new ServiceEndpoint(serviceName, ecsServiceUrl, platform,
Duration.ofMinutes(5));
}
} catch (Exception e) {
System.err.println("Failed to discover AWS service: " + e.getMessage());
}
return null;
}
private ServiceEndpoint discoverAzureService(String serviceName) {
// Azure App Service discovery
String siteName = environment.getProperty("WEBSITE_SITE_NAME");
String url = String.format("https://%s-%s.azurewebsites.net",
siteName, serviceName);
return new ServiceEndpoint(serviceName, url, platform,
Duration.ofMinutes(10));
}
private ServiceEndpoint discoverHerokuService(String serviceName) {
// Heroku service discovery via config vars
String envVarName = serviceName.toUpperCase().replace("-", "_") + "_URL";
String url = environment.getProperty(envVarName);
if (url != null) {
return new ServiceEndpoint(serviceName, url, platform,
Duration.ofMinutes(10));
}
// Fallback to Heroku subdomain pattern
url = String.format("https://%s.herokuapp.com", serviceName);
return new ServiceEndpoint(serviceName, url, platform,
Duration.ofMinutes(10));
}
private ServiceEndpoint discoverLocalService(String serviceName) {
// Local development - use localhost
int port = environment.getProperty(serviceName + ".port", Integer.class, 8080);
String url = String.format("http://localhost:%d", port);
return new ServiceEndpoint(serviceName, url, platform,
Duration.ofMinutes(1));
}
private String parseVcapServicesForUrl(String vcapJson, String serviceName) {
// Simplified JSON parsing - use Jackson or Gson in production
return null;
}
public void invalidateCache(String serviceName) {
cache.remove(serviceName);
}
public void clearCache() {
cache.clear();
}
public static class ServiceEndpoint {
private final String name;
private final String url;
private final CloudPlatform platform;
private final long expiresAt;
public ServiceEndpoint(String name, String url, CloudPlatform platform,
Duration ttl) {
this.name = name;
this.url = url;
this.platform = platform;
this.expiresAt = System.currentTimeMillis() + ttl.toMillis();
}
public String getName() { return name; }
public String getUrl() { return url; }
public CloudPlatform getPlatform() { return platform; }
public boolean isExpired() {
return System.currentTimeMillis() > expiresAt;
}
@Override
public String toString() {
return String.format("ServiceEndpoint{name='%s', url='%s', platform=%s}",
name, url, platform);
}
}
}Tool for testing application behavior across different platforms:
package com.example.migration;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* Helper for simulating different cloud platforms during testing/migration.
*/
@Component
@Profile({"test", "migration"})
public class CloudPlatformMigrationHelper {
private final ConfigurableEnvironment environment;
public CloudPlatformMigrationHelper(ConfigurableEnvironment environment) {
this.environment = environment;
}
public void simulateKubernetes() {
Map<String, Object> kubernetesProps = new HashMap<>();
kubernetesProps.put("KUBERNETES_SERVICE_HOST", "10.0.0.1");
kubernetesProps.put("KUBERNETES_SERVICE_PORT", "443");
applyPlatformProperties("kubernetes-simulation", kubernetesProps);
verifyPlatform(CloudPlatform.KUBERNETES);
}
public void simulateCloudFoundry() {
Map<String, Object> cfProps = new HashMap<>();
cfProps.put("VCAP_APPLICATION", "{\"application_id\":\"test-app\"}");
cfProps.put("VCAP_SERVICES", "{\"mysql\":[{\"credentials\":{}}]}");
applyPlatformProperties("cloud-foundry-simulation", cfProps);
verifyPlatform(CloudPlatform.CLOUD_FOUNDRY);
}
public void simulateAws() {
Map<String, Object> awsProps = new HashMap<>();
awsProps.put("AWS_EXECUTION_ENV", "AWS_ECS_FARGATE");
awsProps.put("AWS_REGION", "us-east-1");
applyPlatformProperties("aws-simulation", awsProps);
verifyPlatform(CloudPlatform.AMAZON_WEB_SERVICES);
}
public void simulateAzure() {
Map<String, Object> azureProps = new HashMap<>();
azureProps.put("WEBSITE_INSTANCE_ID", "test-instance-123");
azureProps.put("WEBSITE_SITE_NAME", "test-app");
applyPlatformProperties("azure-simulation", azureProps);
verifyPlatform(CloudPlatform.AZURE);
}
public void simulateHeroku() {
Map<String, Object> herokuProps = new HashMap<>();
herokuProps.put("DYNO", "web.1");
herokuProps.put("PORT", "5000");
applyPlatformProperties("heroku-simulation", herokuProps);
verifyPlatform(CloudPlatform.HEROKU);
}
public void simulateLocal() {
// Remove all cloud platform environment variables
removePlatformProperties();
verifyPlatform(CloudPlatform.NONE);
}
private void applyPlatformProperties(String sourceName, Map<String, Object> props) {
MapPropertySource propertySource = new MapPropertySource(sourceName, props);
environment.getPropertySources().addFirst(propertySource);
System.out.println("Applied platform simulation: " + sourceName);
System.out.println("Properties: " + props);
}
private void removePlatformProperties() {
environment.getPropertySources().remove("kubernetes-simulation");
environment.getPropertySources().remove("cloud-foundry-simulation");
environment.getPropertySources().remove("aws-simulation");
environment.getPropertySources().remove("azure-simulation");
environment.getPropertySources().remove("heroku-simulation");
}
private void verifyPlatform(CloudPlatform expected) {
CloudPlatform actual = CloudPlatform.getActive(environment);
if (actual != expected) {
throw new IllegalStateException(
String.format("Platform verification failed. Expected: %s, Actual: %s",
expected, actual)
);
}
System.out.println("Successfully simulating platform: " + expected);
}
public void runCompatibilityTest() {
System.out.println("\n=== Running Cloud Platform Compatibility Test ===\n");
try {
System.out.println("Testing Kubernetes compatibility...");
simulateKubernetes();
testPlatformBehavior();
System.out.println("\nTesting Cloud Foundry compatibility...");
simulateCloudFoundry();
testPlatformBehavior();
System.out.println("\nTesting AWS compatibility...");
simulateAws();
testPlatformBehavior();
System.out.println("\nTesting Azure compatibility...");
simulateAzure();
testPlatformBehavior();
System.out.println("\nTesting Heroku compatibility...");
simulateHeroku();
testPlatformBehavior();
System.out.println("\n=== All compatibility tests passed ===\n");
} catch (Exception e) {
System.err.println("Compatibility test failed: " + e.getMessage());
throw e;
} finally {
simulateLocal();
}
}
private void testPlatformBehavior() {
CloudPlatform platform = CloudPlatform.getActive(environment);
System.out.println(" Current platform: " + platform);
System.out.println(" Is cloud: " + CloudPlatform.isCloudPlatform(environment));
System.out.println(" Platform active: " + platform.isActive(environment));
}
}Complete feature flag system based on cloud platform:
package com.example.features;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Platform-aware feature flag system with override support.
*/
@Service
public class PlatformFeatureFlagService {
private final Environment environment;
private final CloudPlatform platform;
private final Map<String, FeatureFlag> features;
public PlatformFeatureFlagService(Environment environment) {
this.environment = environment;
this.platform = CloudPlatform.getActive(environment);
this.features = new ConcurrentHashMap<>();
initializeFeatures();
}
private void initializeFeatures() {
// Define features with platform-specific defaults
registerFeature("distributed-tracing",
Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));
registerFeature("auto-scaling",
Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));
registerFeature("service-mesh",
Set.of(CloudPlatform.KUBERNETES));
registerFeature("cloud-native-logging",
Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));
registerFeature("external-secrets",
Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
CloudPlatform.AZURE));
registerFeature("health-probes",
Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));
registerFeature("container-optimization",
Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY, CloudPlatform.HEROKU));
registerFeature("graceful-shutdown",
Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));
registerFeature("native-load-balancing",
Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));
registerFeature("serverless-optimization",
Set.of(CloudPlatform.AMAZON_WEB_SERVICES)); // Lambda-specific
}
private void registerFeature(String featureName, Set<CloudPlatform> enabledPlatforms) {
boolean defaultEnabled = enabledPlatforms.contains(platform);
// Check for property override
String propertyKey = "features." + featureName + ".enabled";
Boolean override = environment.getProperty(propertyKey, Boolean.class);
boolean enabled = (override != null) ? override : defaultEnabled;
features.put(featureName, new FeatureFlag(
featureName,
enabled,
enabledPlatforms,
override != null
));
System.out.printf("Feature '%s': %s (platform: %s, override: %s)%n",
featureName, enabled ? "ENABLED" : "DISABLED", platform, override != null);
}
public boolean isEnabled(String featureName) {
FeatureFlag feature = features.get(featureName);
return feature != null && feature.enabled;
}
public boolean isEnabledForPlatform(String featureName, CloudPlatform targetPlatform) {
FeatureFlag feature = features.get(featureName);
return feature != null && feature.enabledPlatforms.contains(targetPlatform);
}
public Map<String, Boolean> getAllFeatures() {
Map<String, Boolean> result = new HashMap<>();
features.forEach((name, flag) -> result.put(name, flag.enabled));
return result;
}
public void printFeatureReport() {
System.out.println("\n=== Feature Flag Report ===");
System.out.println("Platform: " + platform);
System.out.println("\nFeature Status:");
features.forEach((name, flag) -> {
System.out.printf(" %-30s %s%s%n",
name,
flag.enabled ? "[ENABLED] " : "[DISABLED]",
flag.overridden ? " (overridden)" : ""
);
});
System.out.println();
}
private static class FeatureFlag {
final String name;
final boolean enabled;
final Set<CloudPlatform> enabledPlatforms;
final boolean overridden;
FeatureFlag(String name, boolean enabled, Set<CloudPlatform> enabledPlatforms,
boolean overridden) {
this.name = name;
this.enabled = enabled;
this.enabledPlatforms = enabledPlatforms;
this.overridden = overridden;
}
}
}Comprehensive health check system with platform-specific checks:
package com.example.health;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
* Platform-aware health indicator with platform-specific checks.
*/
@Component
public class PlatformHealthIndicator implements HealthIndicator {
private final Environment environment;
private final CloudPlatform platform;
private final HttpClient httpClient;
public PlatformHealthIndicator(Environment environment) {
this.environment = environment;
this.platform = CloudPlatform.getActive(environment);
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
}
@Override
public Health health() {
Map<String, Object> details = new HashMap<>();
details.put("platform", platform.name());
details.put("isCloud", CloudPlatform.isCloudPlatform(environment));
// Platform-specific health checks
boolean platformHealthy = switch (platform) {
case KUBERNETES -> checkKubernetesHealth(details);
case CLOUD_FOUNDRY -> checkCloudFoundryHealth(details);
case AMAZON_WEB_SERVICES -> checkAwsHealth(details);
case AZURE -> checkAzureHealth(details);
case HEROKU -> checkHerokuHealth(details);
default -> checkLocalHealth(details);
};
if (platformHealthy) {
return Health.up()
.withDetails(details)
.build();
} else {
return Health.down()
.withDetails(details)
.build();
}
}
private boolean checkKubernetesHealth(Map<String, Object> details) {
boolean healthy = true;
// Check if Kubernetes API is accessible
String kubernetesHost = environment.getProperty("KUBERNETES_SERVICE_HOST");
String kubernetesPort = environment.getProperty("KUBERNETES_SERVICE_PORT");
details.put("kubernetes.api.host", kubernetesHost);
details.put("kubernetes.api.port", kubernetesPort);
// Check ConfigMaps and Secrets availability
Path configPath = Path.of("/etc/config");
Path secretPath = Path.of("/etc/secrets");
if (Files.exists(configPath)) {
details.put("configmap.mounted", true);
} else {
details.put("configmap.mounted", false);
}
if (Files.exists(secretPath)) {
details.put("secrets.mounted", true);
} else {
details.put("secrets.mounted", false);
}
// Check pod metadata
String podName = environment.getProperty("HOSTNAME");
details.put("pod.name", podName);
return healthy;
}
private boolean checkCloudFoundryHealth(Map<String, Object> details) {
String vcapApplication = environment.getProperty("VCAP_APPLICATION");
String vcapServices = environment.getProperty("VCAP_SERVICES");
details.put("vcap.application.present", vcapApplication != null);
details.put("vcap.services.present", vcapServices != null);
if (vcapApplication == null && vcapServices == null) {
details.put("warning", "VCAP environment variables not found");
return false;
}
return true;
}
private boolean checkAwsHealth(Map<String, Object> details) {
String region = environment.getProperty("AWS_REGION");
String executionEnv = environment.getProperty("AWS_EXECUTION_ENV");
details.put("aws.region", region);
details.put("aws.execution.env", executionEnv);
// Check if Lambda
String lambdaFunction = environment.getProperty("AWS_LAMBDA_FUNCTION_NAME");
if (lambdaFunction != null) {
details.put("aws.lambda", true);
details.put("aws.lambda.function", lambdaFunction);
}
// Check ECS metadata
String ecsMetadataUri = environment.getProperty("ECS_CONTAINER_METADATA_URI_V4");
if (ecsMetadataUri != null) {
details.put("aws.ecs", true);
details.put("aws.ecs.metadata.available", checkEcsMetadata(ecsMetadataUri));
}
return true;
}
private boolean checkAzureHealth(Map<String, Object> details) {
String instanceId = environment.getProperty("WEBSITE_INSTANCE_ID");
String siteName = environment.getProperty("WEBSITE_SITE_NAME");
details.put("azure.instance.id", instanceId);
details.put("azure.site.name", siteName);
if (instanceId == null || siteName == null) {
details.put("warning", "Azure environment variables not found");
return false;
}
return true;
}
private boolean checkHerokuHealth(Map<String, Object> details) {
String dyno = environment.getProperty("DYNO");
String port = environment.getProperty("PORT");
details.put("heroku.dyno", dyno);
details.put("heroku.port", port);
if (dyno == null) {
details.put("warning", "DYNO environment variable not found");
return false;
}
return true;
}
private boolean checkLocalHealth(Map<String, Object> details) {
details.put("environment", "local");
return true;
}
private boolean checkEcsMetadata(String metadataUri) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(metadataUri))
.timeout(Duration.ofSeconds(3))
.GET()
.build();
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
return response.statusCode() == 200;
} catch (IOException | InterruptedException e) {
return false;
}
}
}Problem: Setting spring.main.cloud-platform doesn't take effect
Error:
Application detects wrong platform despite configurationSolution:
# Correct property name (with dashes, not dots in the value)
spring.main.cloud-platform=kubernetes
# NOT this (underscores in value won't work):
spring.main.cloud-platform=KUBERNETES// Programmatic forcing during tests
@TestPropertySource(properties = {
"spring.main.cloud-platform=kubernetes"
})
class MyTest {
@Test
void testKubernetesBehavior() {
CloudPlatform platform = CloudPlatform.getActive(environment);
assertThat(platform).isEqualTo(CloudPlatform.KUBERNETES);
}
}Rationale: The property value must be lowercase with dashes, matching the enum name conversion logic. Spring Boot normalizes the value by converting to uppercase and replacing dashes with underscores before calling valueOf().
Problem: Multiple platform environment variables present causes wrong detection
Error: Application detects unexpected platform when running in Docker on Kubernetes
Solution:
// Explicitly force the correct platform
@Configuration
public class PlatformConfig {
@Bean
public ApplicationRunner platformChecker(Environment environment) {
return args -> {
CloudPlatform detected = CloudPlatform.getActive(environment);
CloudPlatform expected = CloudPlatform.KUBERNETES;
if (detected != expected) {
throw new IllegalStateException(
String.format("Wrong platform detected. Expected: %s, Got: %s. " +
"Set spring.main.cloud-platform=%s",
expected, detected, expected.name().toLowerCase().replace('_', '-'))
);
}
};
}
}# Force specific platform to avoid detection issues
spring.main.cloud-platform=kubernetesRationale: CloudPlatform checks platforms in order defined in the enum. If multiple platform environment variables exist (e.g., running Docker locally with AWS credentials), the first matching platform is detected. Always explicitly set the platform property in ambiguous environments.
Problem: Platform detection fails in tests due to missing environment variables
Error:
Platform is NONE in tests, expected KUBERNETESSolution:
@SpringBootTest
@TestPropertySource(properties = {
"KUBERNETES_SERVICE_HOST=10.0.0.1",
"KUBERNETES_SERVICE_PORT=443"
})
class KubernetesPlatformTest {
@Autowired
private Environment environment;
@Test
void shouldDetectKubernetes() {
CloudPlatform platform = CloudPlatform.getActive(environment);
assertThat(platform).isEqualTo(CloudPlatform.KUBERNETES);
}
}
// Or use system properties
@SpringBootTest
class CloudFoundryTest {
@BeforeEach
void setupPlatform() {
System.setProperty("VCAP_APPLICATION", "{\"application_id\":\"test\"}");
}
@AfterEach
void tearDown() {
System.clearProperty("VCAP_APPLICATION");
}
}Rationale: Tests run without cloud environment variables. You must either inject them via @TestPropertySource or use spring.main.cloud-platform to force platform detection for testing.
Problem: Application crashes when platform detection returns NONE
Error:
NullPointerException when accessing platform-specific configurationSolution:
@Service
public class ConfigService {
private final Environment environment;
public String getServiceUrl(String serviceName) {
CloudPlatform platform = CloudPlatform.getActive(environment);
// Always provide fallback for NONE
return switch (platform) {
case KUBERNETES -> getKubernetesServiceUrl(serviceName);
case CLOUD_FOUNDRY -> getCloudFoundryServiceUrl(serviceName);
case AMAZON_WEB_SERVICES -> getAwsServiceUrl(serviceName);
case AZURE -> getAzureServiceUrl(serviceName);
case HEROKU -> getHerokuServiceUrl(serviceName);
case SAP -> getSapServiceUrl(serviceName);
case NONE -> getLocalServiceUrl(serviceName); // Always provide fallback
};
}
private String getLocalServiceUrl(String serviceName) {
// Default for local development
return environment.getProperty(
serviceName + ".url",
"http://localhost:8080" // Safe default
);
}
}Rationale: CloudPlatform.NONE is the default when no platform is detected. Code that switches on platform must always handle NONE to avoid crashes in local development or unexpected environments.
Problem: @ConditionalOnCloudPlatform beans fail to register
Error:
NoSuchBeanDefinitionException: No qualifying bean of type 'ServiceDiscovery'Solution:
@Configuration
public class ServiceDiscoveryConfig {
// Platform-specific beans
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public ServiceDiscovery kubernetesServiceDiscovery() {
return new KubernetesServiceDiscovery();
}
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public ServiceDiscovery cloudFoundryServiceDiscovery() {
return new CloudFoundryServiceDiscovery();
}
// IMPORTANT: Always provide a fallback bean for non-cloud environments
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.NONE)
public ServiceDiscovery localServiceDiscovery() {
return new LocalServiceDiscovery();
}
// OR use @ConditionalOnMissingBean as ultimate fallback
@Bean
@ConditionalOnMissingBean
public ServiceDiscovery defaultServiceDiscovery() {
return new LocalServiceDiscovery();
}
}Rationale: When using @ConditionalOnCloudPlatform, you must provide a bean for every possible platform, or use @ConditionalOnMissingBean as a fallback. Without a fallback, the application fails to start when no matching platform is detected.
Problem: Checking platform before Environment is fully initialized
Error:
Platform detection returns NONE during EnvironmentPostProcessorSolution:
// WRONG: Checking platform too early
@Component
public class EarlyPlatformCheck implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// Platform environment variables might not be loaded yet
CloudPlatform platform = CloudPlatform.getActive(environment); // May be NONE
}
}
// CORRECT: Check platform after context is initialized
@Component
public class PlatformCheck implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
// Environment is fully prepared with all sources
CloudPlatform platform = CloudPlatform.getActive(event.getEnvironment());
System.out.println("Detected platform: " + platform);
}
}
// OR use ApplicationReadyEvent for guaranteed detection
@Component
public class LatePlatformCheck {
@EventListener(ApplicationReadyEvent.class)
public void onReady(ApplicationReadyEvent event) {
CloudPlatform platform = CloudPlatform.getActive(event.getApplicationContext().getEnvironment());
// Platform is definitely detected by this point
}
}Rationale: Platform detection depends on environment variables which are loaded at specific lifecycle points. Checking too early (e.g., in constructors, EnvironmentPostProcessor) may return NONE even in cloud environments. Wait for ApplicationEnvironmentPreparedEvent or later events.
Problem: Application thinks it's running on Kubernetes when it's just Docker
Error: Application enables Kubernetes-specific features in non-Kubernetes Docker containers
Solution:
# In Dockerfile, do NOT set these unless actually running on Kubernetes:
# KUBERNETES_SERVICE_HOST
# KUBERNETES_SERVICE_PORT
# These are automatically set by Kubernetes, don't set them manually# If needed, force non-Kubernetes platform
spring.main.cloud-platform=none// Defensive programming - verify Kubernetes features
@Component
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class KubernetesFeatures {
@PostConstruct
public void verifyKubernetesEnvironment() {
// Verify actual Kubernetes features are available
Path serviceAccountPath = Path.of("/var/run/secrets/kubernetes.io/serviceaccount");
if (!Files.exists(serviceAccountPath)) {
throw new IllegalStateException(
"Kubernetes platform detected but service account not found. " +
"This may be a false detection. Set spring.main.cloud-platform=none"
);
}
}
}Rationale: Setting Kubernetes environment variables manually in Docker causes false platform detection. These variables should only be present when actually running in Kubernetes. Always let Kubernetes inject these variables automatically.
Problem: Incorrect parsing of VCAP_SERVICES JSON
Error:
JsonParseException when accessing Cloud Foundry servicesSolution:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@Component
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class VcapServicesParser {
private final ObjectMapper objectMapper = new ObjectMapper();
public Map<String, Object> parseVcapServices(String vcapJson) {
if (vcapJson == null || vcapJson.isEmpty()) {
return Collections.emptyMap();
}
try {
return objectMapper.readValue(vcapJson,
new TypeReference<Map<String, Object>>() {});
} catch (JsonProcessingException e) {
throw new IllegalStateException(
"Failed to parse VCAP_SERVICES JSON: " + e.getMessage(), e
);
}
}
public String getServiceCredential(String serviceType, String serviceName,
String credentialKey) {
String vcapServices = System.getenv("VCAP_SERVICES");
Map<String, Object> services = parseVcapServices(vcapServices);
try {
List<Map<String, Object>> serviceList =
(List<Map<String, Object>>) services.get(serviceType);
if (serviceList == null) {
throw new IllegalArgumentException(
"Service type not found in VCAP_SERVICES: " + serviceType
);
}
for (Map<String, Object> service : serviceList) {
if (serviceName.equals(service.get("name"))) {
Map<String, Object> credentials =
(Map<String, Object>) service.get("credentials");
return (String) credentials.get(credentialKey);
}
}
throw new IllegalArgumentException(
"Service not found: " + serviceName
);
} catch (ClassCastException e) {
throw new IllegalStateException(
"Unexpected VCAP_SERVICES structure: " + e.getMessage(), e
);
}
}
}Rationale: VCAP_SERVICES is a complex nested JSON structure. Use a proper JSON parser (Jackson, Gson) and handle parsing errors gracefully. Never use string manipulation or regex to parse VCAP JSON.
Problem: Cannot distinguish between Lambda and ECS in AWS
Error: Application applies Lambda optimizations to ECS containers
Solution:
@Service
public class AwsPlatformDetector {
private final Environment environment;
public boolean isAwsLambda() {
return environment.getProperty("AWS_LAMBDA_FUNCTION_NAME") != null;
}
public boolean isAwsEcs() {
return environment.getProperty("AWS_EXECUTION_ENV") != null &&
environment.getProperty("AWS_LAMBDA_FUNCTION_NAME") == null;
}
public boolean isAwsEc2() {
// EC2 instances have AWS_EXECUTION_ENV but no Lambda or ECS specific vars
return environment.getProperty("AWS_EXECUTION_ENV") != null &&
environment.getProperty("AWS_LAMBDA_FUNCTION_NAME") == null &&
environment.getProperty("ECS_CONTAINER_METADATA_URI") == null;
}
@PostConstruct
public void configureForAwsEnvironment() {
if (!CloudPlatform.AMAZON_WEB_SERVICES.isActive(environment)) {
return;
}
if (isAwsLambda()) {
System.out.println("Configuring for AWS Lambda");
configureLambda();
} else if (isAwsEcs()) {
System.out.println("Configuring for AWS ECS");
configureEcs();
} else {
System.out.println("Configuring for AWS EC2");
configureEc2();
}
}
private void configureLambda() {
// Lambda-specific: disable web server, optimize cold starts
}
private void configureEcs() {
// ECS-specific: enable container insights
}
private void configureEc2() {
// EC2-specific: standard configuration
}
}Rationale: CloudPlatform.AMAZON_WEB_SERVICES matches multiple AWS environments (Lambda, ECS, EC2). You must check specific environment variables to distinguish between them and apply appropriate optimizations.
Problem: Application doesn't bind to Heroku-assigned PORT
Error:
Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 secondsSolution:
# application.properties
# Use PORT environment variable on Heroku, fallback to 8080
server.port=${PORT:8080}// Or programmatically
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.HEROKU)
public class HerokuConfig {
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> herokuPortCustomizer(
Environment environment) {
return factory -> {
String port = environment.getProperty("PORT");
if (port != null) {
factory.setPort(Integer.parseInt(port));
System.out.println("Heroku port configured: " + port);
}
};
}
}# application.yml
server:
port: ${PORT:8080}Rationale: Heroku dynamically assigns the PORT environment variable. Applications must bind to this port, not a hardcoded port. Using ${PORT:8080} syntax provides Heroku compatibility while maintaining local development defaults.
CloudPlatform: Enum is inherently thread-safe. Detection methods are stateless and can be called concurrently.
@ConditionalOnCloudPlatform for platform-specific beansspring.main.cloud-platform to test platform-specific code// Cloud platform detection
import org.springframework.boot.cloud.CloudPlatform;
// Conditional configuration
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
// Environment access
import org.springframework.core.env.Environment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;