/**
* POST TITLE: Implementing LaunchDarkly Feature Flags in Java
*
* Comprehensive guide to using LaunchDarkly Java SDK for feature management
*/
import com.launchdarkly.sdk.*;
import com.launchdarkly.sdk.server.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class LaunchDarklyDemo {
private static LDClient ldClient;
private static final String SDK_KEY = "sdk-12345678-1234-1234-1234-123456789012";
public static void main(String[] args) {
initializeLaunchDarkly();
demonstrateFeatureFlags();
demonstrateUserTargeting();
demonstrateCustomEvents();
cleanup();
}
/**
* Initialize LaunchDarkly client
*/
public static void initializeLaunchDarkly() {
try {
LDConfig config = new LDConfig.Builder()
.events(
Components.sendEvents()
.flushInterval(3000) // Flush events every 3 seconds
)
.logging(
Components.logging().level(com.launchdarkly.logging.LDLogLevel.INFO)
)
.build();
ldClient = new LDClient(SDK_KEY, config);
System.out.println("✅ LaunchDarkly client initialized successfully");
} catch (Exception e) {
System.err.println("❌ Failed to initialize LaunchDarkly client: " + e.getMessage());
}
}
/**
* Demonstrate basic feature flag usage
*/
public static void demonstrateFeatureFlags() {
System.out.println("\n=== FEATURE FLAG DEMONSTRATION ===");
// Create a basic user context
LDUser user = new LDUser.Builder("user-12345")
.email("[email protected]")
.firstName("John")
.lastName("Doe")
.custom("plan", "premium")
.build();
// Check various types of feature flags
checkBooleanFlag(user, "new-ui-feature");
checkStringFlag(user, "theme-color");
checkNumericFlag(user, "max-search-results");
checkJsonFlag(user, "feature-config");
// Check flag with detailed evaluation
checkFlagWithDetail(user, "new-ui-feature");
}
/**
* Check boolean feature flag
*/
public static void checkBooleanFlag(LDUser user, String flagKey) {
boolean flagValue = ldClient.boolVariation(flagKey, user, false);
System.out.println("🔘 Boolean Flag '" + flagKey + "': " + flagValue);
if (flagValue) {
System.out.println(" → New feature enabled for user: " + user.getKey());
} else {
System.out.println(" → Using default/old behavior for user: " + user.getKey());
}
}
/**
* Check string feature flag
*/
public static void checkStringFlag(LDUser user, String flagKey) {
String flagValue = ldClient.stringVariation(flagKey, user, "blue");
System.out.println("🔘 String Flag '" + flagKey + "': " + flagValue);
}
/**
* Check numeric feature flag
*/
public static void checkNumericFlag(LDUser user, String flagKey) {
int flagValue = ldClient.intVariation(flagKey, user, 10);
System.out.println("🔘 Numeric Flag '" + flagKey + "': " + flagValue);
}
/**
* Check JSON feature flag
*/
public static void checkJsonFlag(LDUser user, String flagKey) {
LDValue defaultConfig = LDValue.buildObject()
.put("enabled", false)
.put("threshold", 50)
.put("message", "default")
.build();
LDValue config = ldClient.jsonValueVariation(flagKey, user, defaultConfig);
System.out.println("🔘 JSON Flag '" + flagKey + "': " + config.toJsonString());
}
/**
* Check flag with detailed evaluation information
*/
public static void checkFlagWithDetail(LDUser user, String flagKey) {
EvaluationDetail<Boolean> detail = ldClient.boolVariationDetail(flagKey, user, false);
System.out.println("\n📊 Detailed Evaluation for '" + flagKey + "':");
System.out.println(" Value: " + detail.getValue());
System.out.println(" Variation Index: " + detail.getVariationIndex());
System.out.println(" Reason: " + detail.getReason());
}
/**
* Demonstrate user targeting and segmentation
*/
public static void demonstrateUserTargeting() {
System.out.println("\n=== USER TARGETING DEMONSTRATION ===");
// Different user types
LDUser[] users = {
// Premium user from US
new LDUser.Builder("premium-user-001")
.email("[email protected]")
.country("US")
.custom("plan", "premium")
.custom("signupDate", "2024-01-15")
.build(),
// Free user from EU
new LDUser.Builder("free-user-001")
.email("[email protected]")
.country("DE")
.custom("plan", "free")
.custom("signupDate", "2024-03-01")
.build(),
// Beta tester
new LDUser.Builder("beta-tester-001")
.email("[email protected]")
.custom("betaTester", true)
.custom("plan", "premium")
.build()
};
String[] flagsToCheck = {"new-ui-feature", "advanced-search", "export-feature"};
for (LDUser user : users) {
System.out.println("\n👤 User: " + user.getKey() +
" (Plan: " + user.getCustom("plan") +
", Country: " + user.getCountry() + ")");
for (String flag : flagsToCheck) {
boolean isEnabled = ldClient.boolVariation(flag, user, false);
System.out.println(" " + flag + ": " + (isEnabled ? "✅" : "❌"));
}
}
}
/**
* Demonstrate tracking custom events
*/
public static void demonstrateCustomEvents() {
System.out.println("\n=== CUSTOM EVENTS DEMONSTRATION ===");
LDUser user = new LDUser.Builder("user-999")
.email("[email protected]")
.custom("plan", "premium")
.build();
// Track a feature flag evaluation
boolean searchEnabled = ldClient.boolVariation("advanced-search", user, false);
ldClient.track("search-feature-evaluated", user, LDValue.of(searchEnabled));
// Track custom conversion events
if (searchEnabled) {
ldClient.track("advanced-search-used", user,
LDValue.buildObject()
.put("searchTerm", "java sdk")
.put("resultsCount", 15)
.build());
System.out.println("📊 Tracked advanced search usage for user: " + user.getKey());
}
// Track revenue event
ldClient.track("purchase-completed", user,
LDValue.buildObject()
.put("product", "enterprise-plan")
.put("amount", 299.99)
.put("currency", "USD")
.build());
System.out.println("💰 Tracked purchase event for user: " + user.getKey());
}
/**
* Advanced: Feature flag manager class for real-world usage
*/
public static class FeatureFlagManager {
private LDClient client;
public FeatureFlagManager(LDClient client) {
this.client = client;
}
public boolean isFeatureEnabled(String featureKey, LDUser user) {
return client.boolVariation(featureKey, user, false);
}
public String getFeatureString(String featureKey, LDUser user, String defaultValue) {
return client.stringVariation(featureKey, user, defaultValue);
}
public int getFeatureInt(String featureKey, LDUser user, int defaultValue) {
return client.intVariation(featureKey, user, defaultValue);
}
public Map<String, Boolean> getFeatureStates(LDUser user, String... featureKeys) {
Map<String, Boolean> states = new HashMap<>();
for (String key : featureKeys) {
states.put(key, isFeatureEnabled(key, user));
}
return states;
}
public void trackFeatureUsage(String eventName, LDUser user, String featureKey, boolean enabled) {
client.track(eventName, user,
LDValue.buildObject()
.put("feature", featureKey)
.put("enabled", enabled)
.put("timestamp", System.currentTimeMillis())
.build());
}
}
/**
* Example usage in a web application context
*/
public static class UserService {
private FeatureFlagManager flagManager;
public UserService(FeatureFlagManager flagManager) {
this.flagManager = flagManager;
}
public void processUserRegistration(String userId, String email, String country) {
LDUser user = new LDUser.Builder(userId)
.email(email)
.country(country)
.custom("registrationDate", java.time.LocalDate.now().toString())
.build();
// Check feature flags for new user
boolean showWelcomeBonus = flagManager.isFeatureEnabled("welcome-bonus", user);
String theme = flagManager.getString("ui-theme", user, "light");
int maxStorage = flagManager.getFeatureInt("max-storage-mb", user, 100);
System.out.println("\n🎯 Processing registration for: " + email);
System.out.println(" Welcome Bonus: " + (showWelcomeBonus ? "✅" : "❌"));
System.out.println(" UI Theme: " + theme);
System.out.println(" Max Storage: " + maxStorage + "MB");
// Track the registration event
flagManager.trackFeatureUsage("user-registered", user, "registration-flow", true);
}
}
/**
* Clean up resources
*/
public static void cleanup() {
if (ldClient != null) {
try {
ldClient.flush();
ldClient.close();
System.out.println("\n✅ LaunchDarkly client closed successfully");
} catch (IOException e) {
System.err.println("❌ Error closing LaunchDarkly client: " + e.getMessage());
}
}
}
}
Maven Dependency
<!-- pom.xml --> <dependencies> <dependency> <groupId>com.launchdarkly</groupId> <artifactId>launchdarkly-java-server-sdk</artifactId> <version>6.3.0</version> </dependency> </dependencies>
Configuration Class
/**
* Configuration class for LaunchDarkly setup
*/
public class LaunchDarklyConfig {
@Value("${launchdarkly.sdk.key:}")
private String sdkKey;
@Value("${launchdarkly.environment:production}")
private String environment;
@Bean
public LDClient ldClient() {
if (sdkKey == null || sdkKey.trim().isEmpty()) {
// Return a mock client for local development
return createMockClient();
}
LDConfig config = new LDConfig.Builder()
.events(Components.sendEvents().flushInterval(5000))
.logging(Components.logging().level(com.launchdarkly.logging.LDLogLevel.WARN))
.build();
return new LDClient(sdkKey, config);
}
@Bean
public FeatureFlagManager featureFlagManager(LDClient ldClient) {
return new FeatureFlagManager(ldClient);
}
private LDClient createMockClient() {
System.out.println("⚠️ Using mock LaunchDarkly client - feature flags will return default values");
// In a real implementation, you might use a test LDClient or mock
return null; // Simplified for example
}
}
This implementation provides:
- SDK Initialization with proper configuration
- Multiple Flag Types (boolean, string, numeric, JSON)
- User Targeting based on attributes and custom properties
- Custom Event Tracking for analytics
- Production-ready Manager Class for easy integration
- Proper Resource Cleanup to prevent memory leaks
The code follows LaunchDarkly best practices and can be easily integrated into Spring Boot applications or other Java frameworks.