/** * 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.