Skip to content

Commit aa30e86

Browse files
Add Dropwizard OpenFeature Guide (#1192)
## This PR Both Spring Boot and Dropwizard are examples of using OpenFeature in a Java framework, adding a top level section for Java SDK with Spring Boot and Dropwizard as examples. ~Create documentation of how to integrate OpenFeature Java SDK in Dropwizard using the `InMemoryProvider` either via the SDK only, or using the dropwizard-openfeature module.~ Create documentation on how to integrate OpenFeature Java SDK into Dropwizard either by using the SDK directly, or the https://github.com/sideshowcoder/dropwizard-openfeature module. In either case using `flagd` as the provider of flags, with the shared content from the Spring Boot tutorial. --------- Signed-off-by: Philipp Fehre <[email protected]>
1 parent 2934266 commit aa30e86

File tree

4 files changed

+412
-2
lines changed

4 files changed

+412
-2
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"position": 3,
3+
"label": "Java SDK",
4+
"collapsible": true,
5+
"collapsed": false,
6+
"link": {
7+
"type": "generated-index",
8+
"title": "Java SDK tutorials",
9+
"description": "These step-by-step guides walk you through the basics of the OpenFeature SDK usage for Java."
10+
}
11+
}
Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
---
2+
title: Dropwizard
3+
description: Getting Started with the OpenFeature Java SDK and Dropwizard
4+
---
5+
6+
import FlagdContent from '@site/src/components/custom/tutorial/flagd-content.mdx';
7+
import FlagdChangeContent from '@site/src/components/custom/tutorial/flagd-change-content.mdx';
8+
import WhyDefaultContent from '@site/src/components/custom/tutorial/why-default-content.mdx';
9+
import Tabs from '@theme/Tabs';
10+
import TabItem from '@theme/TabItem';
11+
12+
# Getting Started with the OpenFeature Java SDK and Dropwizard
13+
14+
## Introduction
15+
16+
This walk-through teaches you the basics of using OpenFeature in Java in the context of a Dropwizard application.
17+
18+
You'll learn how to:
19+
20+
- Integrate the OpenFeature Java SDK
21+
- Install and configure the OpenFeature provider
22+
- Perform basic feature flagging
23+
24+
## Requirements
25+
26+
This walk-through assumes that:
27+
28+
- You have a basic knowledge of Java and Dropwizard
29+
- You have Java 8 or later
30+
- You have Docker installed and running on the host system
31+
32+
This guide uses Java 21 syntax, adjustments might be needed for earlier Java versions.
33+
34+
## Walk-through
35+
36+
### Step 1: Create a Dropwizard application
37+
38+
Create a new Dropwizard application following the guide on [dropwizard.io](https://www.dropwizard.io/en/stable/getting-started.html).
39+
40+
For this example we are using the maven archetype
41+
42+
```shell
43+
$ mvn archetype:generate -DarchetypeGroupId=io.dropwizard.archetypes -DarchetypeArtifactId=java-simple -DarchetypeVersion=4.0.13
44+
...
45+
Define value for property 'name': OpenFeatureExample
46+
...
47+
Define value for property 'groupId': dev.openfeature
48+
Define value for property 'artifactId': openfeature-example
49+
Define value for property 'version' 1.0-SNAPSHOT:
50+
Define value for property 'package' dev.openfeature:
51+
Confirm properties configuration:
52+
name: OpenFeatureExample
53+
description: null
54+
shaded: true
55+
groupId: dev.openfeature
56+
artifactId: openfeature-example
57+
version: 1.0-SNAPSHOT
58+
package: dev.openfeature
59+
Y: Y
60+
...
61+
```
62+
63+
We now have an application we can open
64+
65+
```shell
66+
cd openfeature-example
67+
```
68+
69+
### Step 2: Add dependencies
70+
71+
For dropwizard we can either use the SDK directly, or use a community module [dropwizard-openfeature](https://github.com/sideshowcoder/dropwizard-openfeature),
72+
and while dropwizard-openfeature provides some benefits like built-in healthchecks, and managing the startup and shutdown of resources associated with the
73+
OpenFeature Java SDK, it is not however not officially supported, so use at your own risk.
74+
75+
Depending what you choose add the following to your `pom.xml`
76+
77+
<Tabs groupId="dependency">
78+
<TabItem value="sdk" label="sdk-only">
79+
80+
```xml
81+
<dependency>
82+
<groupId>dev.openfeature</groupId>
83+
<artifactId>sdk</artifactId>
84+
<version>1.15.1</version>
85+
</dependency>
86+
<dependency>
87+
<groupId>dev.openfeature.contrib.providers</groupId>
88+
<artifactId>flagd</artifactId>
89+
<version>0.11.8</version>
90+
</dependency>
91+
```
92+
93+
</TabItem>
94+
<TabItem value="dropwizard-module" label="dropwizard-openfeature">
95+
96+
```xml
97+
<dependency>
98+
<groupId>io.github.sideshowcoder</groupId>
99+
<artifactId>dropwizard-openfeature</artifactId>
100+
<version>1.0.1</version>
101+
</dependency>
102+
```
103+
104+
</TabItem>
105+
</Tabs>
106+
107+
### Step 3: Create a resource
108+
109+
We create a resource to serve the `/welcome` endpoint.
110+
Depending on the value of the feature flag named `"welcome-message"`, it serves different messages.
111+
112+
```java
113+
@Path("/welcome")
114+
@Produces(TEXT_PLAIN)
115+
public class WelcomeResource {
116+
117+
private final Client client;
118+
119+
public WelcomeResource(Client client) {
120+
this.client = client;
121+
}
122+
123+
@GET
124+
public String getWelcome() {
125+
if(client.getBooleanValue("welcome-message", false)) {
126+
return "Welcome to OpenFeature in Dropwizard!\n";
127+
}
128+
129+
return "Welcome!\n";
130+
}
131+
}
132+
```
133+
134+
to make this resource available we need to register it, and inject the OpenFeature `Client`.
135+
136+
```java
137+
public class OpenFeatureExampleApplication extends Application<OpenFeatureExampleConfiguration> {
138+
139+
public static void main(final String[] args) throws Exception {
140+
new OpenFeatureExampleApplication().run(args);
141+
}
142+
143+
@Override
144+
public String getName() {
145+
return "OpenFeatureExample";
146+
}
147+
148+
@Override
149+
public void initialize(final Bootstrap<OpenFeatureExampleConfiguration> bootstrap) {
150+
// TODO: application initialization
151+
}
152+
153+
@Override
154+
public void run(final OpenFeatureExampleConfiguration configuration, final Environment environment) {
155+
// diff-remove
156+
// TODO: implement application
157+
// diff-add-block-start
158+
var client = OpenFeatureAPI.getInstance().getClient("dev.openfeature.OpenFeatureExample");
159+
var welcomeResource = new WelcomeResource(client);
160+
environment.jersey().register(welcomeResource);
161+
// diff-add-block-end
162+
}
163+
}
164+
```
165+
166+
### Step 4: Run the application
167+
168+
Now we can build and run the initial version of the application.
169+
170+
```shell
171+
$ mvn clean package
172+
$ java -jar target/openfeature-example-1.0-SNAPSHOT.jar server config.yml
173+
INFO [2025-06-13 10:00:00,000] io.dropwizard.core.server.DefaultServerFactory: Registering jersey handler with root path prefix: /
174+
INFO [2025-06-13 10:00:00,000] io.dropwizard.core.server.DefaultServerFactory: Registering admin handler with root path prefix: /
175+
INFO [2025-06-13 10:00:00,000] io.dropwizard.core.server.ServerFactory: Starting OpenFeatureExample
176+
================================================================================
177+
178+
OpenFeatureExample
179+
180+
================================================================================
181+
182+
INFO [2025-06-13 10:00:00,000] org.eclipse.jetty.setuid.SetUIDListener: Opened application@748e9b20{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
183+
INFO [2025-06-13 10:00:00,000] org.eclipse.jetty.setuid.SetUIDListener: Opened admin@2063c53e{HTTP/1.1, (http/1.1)}{0.0.0.0:8081}
184+
INFO [2025-06-13 10:00:00,000] org.eclipse.jetty.server.Server: jetty-11.0.25; built: 2025-03-13T00:15:57.301Z; git: a2e9fae3ad8320f2a713d4fa29bba356a99d1295; jvm 21+35
185+
186+
INFO [2025-06-13 10:00:00,000] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources:
187+
188+
GET /welcome (com.sideshowcoder.resources.WelcomeResource)
189+
...
190+
```
191+
192+
In the output we can see the resource being available under `/welcome` as well as the application listening to `http://0.0.0.0:8080`, where `0.0.0.0` refers to all interfaces.
193+
Using `curl`
194+
we can see the application working.
195+
196+
```shell
197+
$ curl -i http://localhost:8080/welcome
198+
HTTP/1.1 200 OK
199+
Date: Fri, 13 Jun 2025 10:00:00 GMT
200+
Content-Type: text/plain
201+
Vary: Accept-Encoding
202+
Content-Length: 9
203+
204+
Welcome!
205+
```
206+
207+
<WhyDefaultContent/>
208+
209+
### Step 5: Configure flagd as a provider
210+
211+
<FlagdContent/>
212+
213+
Now we configure flagd as the provider in our application
214+
215+
<Tabs groupId="provider">
216+
<TabItem value="sdk" label="sdk-only">
217+
218+
```java
219+
public class OpenFeatureExampleApplication extends Application<OpenFeatureExampleConfiguration> {
220+
221+
public static void main(final String[] args) throws Exception {
222+
new OpenFeatureExampleApplication().run(args);
223+
}
224+
225+
@Override
226+
public String getName() {
227+
return "OpenFeatureExample";
228+
}
229+
230+
@Override
231+
public void initialize(final Bootstrap<OpenFeatureExampleConfiguration> bootstrap) {
232+
// nothing to do here
233+
}
234+
235+
@Override
236+
public void run(final OpenFeatureExampleConfiguration configuration, final Environment environment) {
237+
// diff-add-block-start
238+
// Use flagd as the OpenFeature provider and use default configurations
239+
try {
240+
OpenFeatureAPI.getInstance().setProviderAndWait(new FlagdProvider());
241+
} catch (OpenFeatureError e) {
242+
throw new RuntimeException("Failed to set OpenFeature provider", e);
243+
}
244+
// diff-add-block-end
245+
246+
var client = OpenFeatureAPI.getInstance().getClient("dev.openfeature.OpenFeatureExample");
247+
var welcomeResource = new WelcomeResource(client);
248+
environment.jersey().register(welcomeResource);
249+
}
250+
}
251+
```
252+
253+
</TabItem>
254+
<TabItem value="dropwizard-module" label="dropwizard-openfeature">
255+
256+
Add the bundle configuration to the existing application configuration
257+
258+
```java
259+
// diff-remove
260+
public class OpenFeatureExampleConfiguration extends Configuration {
261+
// diff-add
262+
public class OpenFeatureExampleConfiguration extends Configuration implements OpenFeatureBundleConfiguration {
263+
// diff-remove
264+
// TODO: implement service configuration
265+
// diff-add-block-start
266+
@Valid
267+
@NotNull
268+
@JsonProperty
269+
private OpenFeatureConfiguration openfeature;
270+
271+
@Override
272+
public OpenFeatureConfiguration getOpenFeatureConfiguration() {
273+
return openfeature;
274+
}
275+
// diff-add-block-end
276+
}
277+
```
278+
279+
add the bundle configuration to the `config.yml` file
280+
281+
```yaml
282+
---
283+
logging:
284+
level: INFO
285+
loggers:
286+
dev.openfeature: DEBUG
287+
288+
// diff-add-block-start
289+
openfeature:
290+
provider: flagd
291+
// diff-add-block-end
292+
```
293+
294+
initialize the bundle
295+
296+
```java
297+
public class OpenFeatureExampleApplication extends Application<OpenFeatureExampleConfiguration> {
298+
299+
public static void main(final String[] args) throws Exception {
300+
new OpenFeatureExampleApplication().run(args);
301+
}
302+
303+
@Override
304+
public String getName() {
305+
return "OpenFeatureExample";
306+
}
307+
308+
@Override
309+
public void initialize(final Bootstrap<OpenFeatureExampleConfiguration> bootstrap) {
310+
// diff-add-block-start
311+
bootstrap.addBundle(new OpenFeatureBundle());
312+
// diff-add-block-end
313+
}
314+
315+
@Override
316+
public void run(final OpenFeatureExampleConfiguration configuration, final Environment environment) {
317+
var client = OpenFeatureAPI.getInstance().getClient("dev.openfeature.OpenFeatureExample");
318+
var welcomeResource = new WelcomeResource(client);
319+
environment.jersey().register(welcomeResource);
320+
}
321+
}
322+
```
323+
324+
> NOTE: dropwizard-openfeature not only configures the provider, but also adds a healthcheck and hooks the provider into the application startup and shutdown lifecycle.
325+
326+
</TabItem>
327+
</Tabs>
328+
329+
### Step 6: Rerun the application
330+
331+
We can now rerun the application
332+
333+
```shell
334+
$ mvn clean package
335+
$ java -jar target/openfeature-example-1.0-SNAPSHOT.jar server config.yml
336+
INFO [2025-06-13 10:00:00,000] io.dropwizard.core.server.DefaultServerFactory: Registering jersey handler with root path prefix: /
337+
INFO [2025-06-13 10:00:00,000] io.dropwizard.core.server.DefaultServerFactory: Registering admin handler with root path prefix: /
338+
INFO [2025-06-13 10:00:00,000] io.dropwizard.core.server.ServerFactory: Starting OpenFeatureExample
339+
================================================================================
340+
341+
OpenFeatureExample
342+
343+
================================================================================
344+
345+
INFO [2025-06-13 10:00:00,000] org.eclipse.jetty.setuid.SetUIDListener: Opened application@748e9b20{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
346+
INFO [2025-06-13 10:00:00,000] org.eclipse.jetty.setuid.SetUIDListener: Opened admin@2063c53e{HTTP/1.1, (http/1.1)}{0.0.0.0:8081}
347+
INFO [2025-06-13 10:00:00,000] org.eclipse.jetty.server.Server: jetty-11.0.25; built: 2025-03-13T00:15:57.301Z; git: a2e9fae3ad8320f2a713d4fa29bba356a99d1295; jvm 21+35
348+
349+
INFO [2025-06-13 10:00:00,000] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources:
350+
351+
GET /welcome (com.sideshowcoder.resources.WelcomeResource)
352+
...
353+
```
354+
355+
Using `curl` to again fetch the `/welcome` resource will show again the default message
356+
357+
```shell
358+
$ curl -i http://localhost:8080/welcome
359+
HTTP/1.1 200 OK
360+
Date: Fri, 13 Jun 2025 10:00:00 GMT
361+
Content-Type: text/plain
362+
Vary: Accept-Encoding
363+
Content-Length: 9
364+
365+
Welcome!
366+
```
367+
368+
<FlagdChangeContent/>
369+
370+
fetching `/welcome` now will show the message for `"welcome-message"` being `true`.
371+
372+
```shell
373+
$ curl -i http://localhost:8080/welcome
374+
HTTP/1.1 200 OK
375+
Date: Fri, 13 Jun 2025 10:00:00 GMT
376+
Content-Type: text/plain
377+
Vary: Accept-Encoding
378+
Content-Length: 38
379+
380+
Welcome to OpenFeature in Dropwizard!
381+
```
382+
383+
## Conclusion
384+
385+
In this walkthrough we learned how to integrate OpenFeature into a Dropwizard application, using flagd to provide the feature flags at runtime.
386+
We saw how changing the flags
387+
definition can change the runtime behaviour of our application, without the need to redeploy or restart the application.

0 commit comments

Comments
 (0)