diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 8edd343..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,52 +0,0 @@ -version: 2.1 - -orbs: - win: circleci/windows@1.0.0 - -workflows: - test: - jobs: - - build-run-linux - - build-windows - -# This CI build ensures that the demo both compiles and works correctly. For the runtime test, -# we use an SDK key and flag key that are passed in via the CircleCI project configuration; -# the flag is configured to return a true value. - -jobs: - build-run-linux: - docker: - - image: circleci/openjdk:14-jdk-buster - steps: - - checkout - - run: - name: show dependency versions - command: ./gradlew dependencies - - run: - name: insert SDK key and flag key into demo code - command: | - sed -i.bak "s/SDK_KEY *= *\".*\"/SDK_KEY = \"${LD_DEMO_SDK_KEY}\"/" src/main/java/Hello.java - sed -i.bak "s/FEATURE_FLAG_KEY *= *\".*\"/FEATURE_FLAG_KEY = \"${LD_DEMO_FLAG_KEY}\"/" src/main/java/Hello.java - - run: - name: build demo - command: ./gradlew build - - run: - name: run demo - command: | - ./gradlew run | tee output.txt - grep "is true for this user" output.txt || (echo "Flag did not evaluate to expected true value" && exit 1) - - build-windows: - executor: - name: win/vs2019 - shell: powershell.exe - steps: - - checkout - - run: - name: install OpenJDK - command: | - $ProgressPreference = "SilentlyContinue" # prevents console errors from CircleCI host - iwr -outf openjdk.msi https://developers.redhat.com/download-manager/file/java-11-openjdk-11.0.5.10-2.windows.redhat.x86_64.msi - Start-Process msiexec.exe -Wait -ArgumentList '/I openjdk.msi /quiet' - - run: .\gradlew.bat --no-daemon dependencies # must use --no-daemon because CircleCI in Windows will hang if there's a daemon running - - run: .\gradlew.bat --no-daemon build # must use --no-daemon because CircleCI in Windows will hang if there's a daemon running diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8d72c89 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: Build and run +on: + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '0 9 * * *' + push: + branches: [ main, 'feat/**' ] + paths-ignore: + - '**.md' # Do not need to run CI for markdown changes. + pull_request: + branches: [ main, 'feat/**' ] + paths-ignore: + - '**.md' + +jobs: + build-and-run: + runs-on: ubuntu-latest + + permissions: + id-token: write # Needed if using OIDC to get release secrets. + + steps: + - uses: actions/checkout@v4 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + + - uses: launchdarkly/gh-actions/actions/verify-hello-app@verify-hello-app-v2.0.1 + with: + use_server_key: true + role_arn: ${{ vars.AWS_ROLE_ARN }} + command: ./gradlew run diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..35715d7 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,14 @@ +name: "Close stale issues and PRs" +on: + workflow_dispatch: + schedule: + # Happen once per day at 1:30 AM + - cron: "30 1 * * *" + +permissions: + issues: write + pull-requests: write + +jobs: + sdk-close-stale: + uses: launchdarkly/gh-actions/.github/workflows/sdk-stale.yml@main diff --git a/.gitignore b/.gitignore index fe64b5a..cfb6e1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ # Eclipse project files .classpath .project - +.settings + # Intellij project files *.iml *.ipr @@ -10,4 +11,6 @@ # Gradle .gradle -build \ No newline at end of file +build +bin +target diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..1046984 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,2 @@ +# Repository Maintainers +* @launchdarkly/team-sdk-java \ No newline at end of file diff --git a/README.md b/README.md index cc93626..03bc875 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,23 @@ -# LaunchDarkly Sample Java Application
 +# LaunchDarkly sample Java application We've built a simple console application that demonstrates how LaunchDarkly's SDK works. -
Below, you'll find the basic build procedure, but for more comprehensive instructions, you can visit your [Quickstart page](https://app.launchdarkly.com/quickstart#/) or the [Java SDK reference guide](https://docs.launchdarkly.com/sdk/server-side/java). +Below, you'll find the basic build procedure, but for more comprehensive instructions, you can visit your [Quickstart page](https://app.launchdarkly.com/quickstart#/) or the [Java SDK reference guide](https://docs.launchdarkly.com/sdk/server-side/java). -## Build instructions
 +## Build instructions -This project uses [Gradle](https://gradle.org/). It requires that Java is already installed on your system (version 8 or higher). It will automatically use the latest release of the LaunchDarkly SDK with major version 5. +This project uses [Gradle](https://gradle.org/). It requires that Java is already installed on your system (version 8 or higher). It will automatically use the latest release of the LaunchDarkly SDK with major version 7. -1. Edit `src/main/java/Hello.java` and set the value of `SDK_KEY` to your LaunchDarkly SDK key. If there is an existing boolean feature flag in your LaunchDarkly project that you want to evaluate, set `FEATURE_FLAG_KEY` to the flag key. +1. Set the value of environment variable `LAUNCHDARKLY_SDK_KEY` to your LaunchDarkly SDK key. If there is an existing boolean feature flag in your LaunchDarkly project that you want to evaluate, edit `src/main/java/Hello.java` and set `FEATURE_FLAG_KEY` in code to the flag key. -```java - static final String SDK_KEY = "1234567890abcdef"; +```sh + export LAUNCHDARKLY_SDK_KEY=1234567890abcdef +``` +```java static final String FEATURE_FLAG_KEY = "my-flag"; ``` 2. On the command line, run `./gradlew run` (or, on Windows, `gradlew run`). -The demo should print `"Feature flag '' is for this user"`. +You should receive the message "The feature flag evaluates to ." The application will run continuously and react to the flag changes in LaunchDarkly. diff --git a/build.gradle b/build.gradle index fd49b9d..9ed79d1 100644 --- a/build.gradle +++ b/build.gradle @@ -20,14 +20,9 @@ application { } ext.versions = [ - "sdk": "5.+", - "slf4j": "1.7.22" + "sdk": "7+" ] dependencies { implementation "com.launchdarkly:launchdarkly-java-server-sdk:${versions.sdk}" - - // Adding slf4j-simple enables the basic console logging implementation of SLF4J. Most real - // applications will use one of the other SLF4J adapters. See: http://www.slf4j.org/ - implementation "org.slf4j:slf4j-simple:${versions.slf4j}" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 622ab64..48c0a02 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/Hello.java b/src/main/java/Hello.java index de9cd75..2fd5f4c 100644 --- a/src/main/java/Hello.java +++ b/src/main/java/Hello.java @@ -1,42 +1,111 @@ -import com.launchdarkly.sdk.LDUser; -import com.launchdarkly.sdk.LDValue; -import com.launchdarkly.sdk.server.LDClient; - import java.io.IOException; +import com.launchdarkly.sdk.*; +import com.launchdarkly.sdk.server.*; + public class Hello { - // Set SDK_KEY to your LaunchDarkly SDK key before compiling - static final String SDK_KEY = ""; + // Set SDK_KEY to your LaunchDarkly SDK key. + static String SDK_KEY = ""; + + // Set FEATURE_FLAG_KEY to the feature flag key you want to evaluate. + static String FEATURE_FLAG_KEY = "sample-feature"; + + private static void showMessage(String s) { + System.out.println("*** " + s); + System.out.println(); + } + + private static void showBanner() { + showMessage("\n ██ \n" + + " ██ \n" + + " ████████ \n" + + " ███████ \n" + + "██ LAUNCHDARKLY █\n" + + " ███████ \n" + + " ████████ \n" + + " ██ \n" + + " ██ \n"); + } + + public static void main(String... args) throws Exception { + boolean CIMode = System.getenv("CI") != null; + + String envSDKKey = System.getenv("LAUNCHDARKLY_SDK_KEY"); + if(envSDKKey != null) { + SDK_KEY = envSDKKey; + } + + String envFlagKey = System.getenv("LAUNCHDARKLY_FLAG_KEY"); + if(envFlagKey != null) { + FEATURE_FLAG_KEY = envFlagKey; + } - // Set FEATURE_FLAG_KEY to the feature flag key you want to evaluate - static final String FEATURE_FLAG_KEY = "YOUR_FEATURE_KEY"; + LDConfig config = new LDConfig.Builder().build(); - public static void main(String... args) throws IOException { - if (SDK_KEY.equals("")) { - System.out.println("Please edit Hello.java to set SDK_KEY to your LaunchDarkly SDK key first"); + if (SDK_KEY == null || SDK_KEY.equals("")) { + showMessage("Please set the LAUNCHDARKLY_SDK_KEY environment variable or edit Hello.java to set SDK_KEY to your LaunchDarkly SDK key first."); System.exit(1); } - LDClient client = new LDClient(SDK_KEY); + final LDClient client = new LDClient(SDK_KEY, config); + if (client.isInitialized()) { + showMessage("SDK successfully initialized!"); + } else { + showMessage("SDK failed to initialize. Please check your internet connection and SDK credential for any typo."); + System.exit(1); + } + + // Set up the evaluation context. This context should appear on your + // LaunchDarkly contexts dashboard soon after you run the demo. + final LDContext context = LDContext.builder("example-user-key") + .name("Sandy") + .build(); + + // Evaluate the feature flag for this context. + boolean flagValue = client.boolVariation(FEATURE_FLAG_KEY, context, false); + showMessage("The '" + FEATURE_FLAG_KEY + "' feature flag evaluates to " + flagValue + "."); + + if (flagValue) { + showBanner(); + } + + //If this is building for CI, we don't need to keep running the Hello App continously. + if(CIMode) { + System.exit(0); + } - // Set up the user properties. This user should appear on your LaunchDarkly users dashboard - // soon after you run the demo. - LDUser user = new LDUser.Builder("bob@example.com") - .firstName("Bob") - .lastName("Loblaw") - .custom("groups", LDValue.buildArray().add("beta_testers").build()) - .build(); + // We set up a flag change listener so you can see flag changes as you change + // the flag rules. + client.getFlagTracker().addFlagValueChangeListener(FEATURE_FLAG_KEY, context, event -> { + showMessage("The '" + FEATURE_FLAG_KEY + "' feature flag evaluates to " + event.getNewValue() + "."); - boolean showFeature = client.boolVariation(FEATURE_FLAG_KEY, user, false); + if (event.getNewValue().booleanValue()) { + showBanner(); + } + }); + showMessage("Listening for feature flag changes."); - System.out.println("Feature flag '" + FEATURE_FLAG_KEY + "' is " + showFeature + " for this user"); + // Here we ensure that when the application terminates, the SDK shuts down + // cleanly and has a chance to deliver analytics events to LaunchDarkly. + // If analytics events are not delivered, the context attributes and flag usage + // statistics may not appear on your dashboard. In a normal long-running + // application, the SDK would continue running and events would be delivered + // automatically in the background. + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + public void run() { + try { + client.close(); + } catch (IOException e) { + // ignore + } + } + }, "ldclient-cleanup-thread")); - // Calling client.close() ensures that the SDK shuts down cleanly before the program exits. - // Unless you do this, the SDK may not have a chance to deliver analytics events to LaunchDarkly, - // so the user properties and the flag usage statistics may not appear on your dashboard. In a - // normal long-running application, events would be delivered automatically in the background - // and you would not need to close the client. - client.close(); + // Keeps example application alive. + Object mon = new Object(); + synchronized (mon) { + mon.wait(); + } } } diff --git a/src/main/resources/simplelogger.properties b/src/main/resources/simplelogger.properties deleted file mode 100644 index df45e59..0000000 --- a/src/main/resources/simplelogger.properties +++ /dev/null @@ -1,13 +0,0 @@ -# SLF4J's SimpleLogger configuration file - -# For the purposes of this demo, we are using the simple console logging implementation that is -# provided by org.slf4j:slf4j-simple. In a real application, there are many possible ways to -# configure the logging behavior. See: http://www.slf4j.org/ - -# Set defaultLogLevel to "info" or "debug" to see more detailed log output from the SDK. -org.slf4j.simpleLogger.defaultLogLevel=warn - -org.slf4j.simpleLogger.showDateTime=true -org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z -org.slf4j.simpleLogger.showThreadName=true -org.slf4j.simpleLogger.showLogName=true