Skip to content

Commit a959e9d

Browse files
committed
object stored in shared state, implementing AutoClosable, would be closed on application shutdown;
add SharedConfigurationState.lookupOrCreate method to simplify static state usage
1 parent c35f365 commit a959e9d

3 files changed

Lines changed: 100 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
- Add `useApacheClient()` (shortcut) method into extension and generic builders
88
* @StubRest RestClient:
99
- Add patch method shortcuts
10+
* Shared state:
11+
- State objects, implementing AutoClosable, now would be closed on application shutdown
12+
- Add SharedConfigurationState.lookupOrCreate method to simplify static state usage
1013

1114
### 7.2.1 (2025-05-12)
1215
* Fix NoClassDefFoundError on guicey startup due to junit classes leak into core (#428)

dropwizard-guicey/src/main/java/ru/vyarus/dropwizard/guice/module/context/SharedConfigurationState.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@
5555
* Shared value could be set only once (to prevent complex situations with state substitutions). It is advised
5656
* to initialize shared value only in initialization phase (to avoid potential static access errors).
5757
* <p>
58+
* If stored state object implements {@link AutoCloseable} it will be automatically closed on application shutdown
59+
* (which is closed in most cases under tests). This is mostly useful to guarantee resource cleanup after the test
60+
* (when it can't be managed directly).
61+
* <p>
5862
* Objects available in shared state by default: {@link io.dropwizard.core.setup.Bootstrap},
5963
* {@link io.dropwizard.core.Configuration}, {@link Environment},
6064
* {@link ru.vyarus.dropwizard.guice.module.yaml.ConfigurationTree}, {@link com.google.inject.Injector},
@@ -312,7 +316,17 @@ public Set<String> getKeys() {
312316
*/
313317
@VisibleForTesting
314318
public void shutdown() {
315-
STATE.remove(application);
319+
getKeys().forEach(key -> {
320+
Object res = get(key);
321+
if (res instanceof AutoCloseable) {
322+
try {
323+
((AutoCloseable) res).close();
324+
} catch (Exception e) {}
325+
}
326+
});
327+
if (application != null) {
328+
STATE.remove(application);
329+
}
316330
}
317331

318332
@Override
@@ -434,6 +448,38 @@ public static <V> V lookupOrFail(final Environment environment,
434448
.orElseThrow(() -> new IllegalStateException(Strings.lenientFormat(message, args)));
435449
}
436450

451+
/**
452+
* Shortcut for {@link #lookup(Application, Class)} to get ot initialize shared state value.
453+
*
454+
* @param application application instance
455+
* @param key shared object key
456+
* @param defSupplier default value supplier (used when state is not exists to initialize it)
457+
* @param <V> shared object type
458+
* @return value (never null)
459+
* @throws IllegalStateException if value not available
460+
*/
461+
public static <V> V lookupOrCreate(final Application application,
462+
final Class<V> key,
463+
final Supplier<V> defSupplier) {
464+
return getOrFail(application, "State is not available yet").get(key, defSupplier);
465+
}
466+
467+
/**
468+
* Shortcut for {@link #lookup(Environment, Class)} to get ot initialize shared state value.
469+
*
470+
* @param environment environment instance
471+
* @param key shared object key
472+
* @param defSupplier default value supplier (used when state is not exists to initialize it)
473+
* @param <V> shared object type
474+
* @return value (never null)
475+
* @throws IllegalStateException if value not available
476+
*/
477+
public static <V> V lookupOrCreate(final Environment environment,
478+
final Class<V> key,
479+
final Supplier<V> defSupplier) {
480+
return getOrFail(environment, "State is not available yet").get(key, defSupplier);
481+
}
482+
437483
/**
438484
* Static lookup for entire application registry.
439485
*
@@ -501,6 +547,7 @@ public static SharedConfigurationState getOrFail(final Environment environment,
501547
*/
502548
@VisibleForTesting
503549
public static void clear() {
550+
STATE.values().forEach(SharedConfigurationState::shutdown);
504551
STATE.clear();
505552
}
506553

dropwizard-guicey/src/test/groovy/ru/vyarus/dropwizard/guice/config/sharedstate/SharedStateTest.groovy

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,14 @@ class SharedStateTest extends Specification {
7474
SharedConfigurationState.lookup(app, App).get() == app
7575
SharedConfigurationState.lookupOrFail(app, App, "2") == app
7676

77+
and: "access or create work"
78+
SharedConfigurationState.lookupOrCreate(app, CustomState, () -> new CustomState())
79+
SharedConfigurationState.lookupOrFail(app, CustomState, "err")
80+
7781
when: "to string state"
7882
res = state.toString()
7983
then: "ok"
80-
res == "Shared state with 1 objects: $App.name"
84+
res == "Shared state with 2 objects: $CustomState.name, $App.name"
8185

8286
}
8387

@@ -159,10 +163,43 @@ class SharedStateTest extends Specification {
159163
SharedConfigurationState.lookup(environment, App).get() == app
160164
SharedConfigurationState.lookupOrFail(environment, App, "2") == app
161165

166+
and: "access and create"
167+
SharedConfigurationState.lookupOrCreate(environment, CustomState, () -> new CustomState())
168+
SharedConfigurationState.lookupOrFail(environment, CustomState, "err")
169+
162170
when: "to string state"
163171
res = state.toString()
164172
then: "ok"
165-
res == "Shared state with 1 objects: $App.name"
173+
res == "Shared state with 2 objects: $CustomState.name, $App.name"
174+
}
175+
176+
def "Check auto closable support on shutdown"() {
177+
178+
setup: "prepare state"
179+
SharedConfigurationState state = new SharedConfigurationState()
180+
181+
when: "register closable object"
182+
CustomState val = new CustomState()
183+
state.put(CustomState, val)
184+
state.shutdown()
185+
186+
then: "cleanup called"
187+
val.called
188+
}
189+
190+
def "Check auto closable support on clean"() {
191+
192+
setup: "prepare state"
193+
SharedConfigurationState state = new SharedConfigurationState()
194+
state.assignTo(new App())
195+
196+
when: "register closable object"
197+
CustomState val = new CustomState()
198+
state.put(CustomState, val)
199+
SharedConfigurationState.clear()
200+
201+
then: "cleanup called"
202+
val.called
166203
}
167204

168205
static class App extends Application<Configuration> {
@@ -175,4 +212,14 @@ class SharedStateTest extends Specification {
175212
void run(Configuration configuration, Environment environment) throws Exception {
176213
}
177214
}
215+
216+
static class CustomState implements AutoCloseable {
217+
218+
boolean called
219+
220+
@Override
221+
void close() throws Exception {
222+
called = true
223+
}
224+
}
178225
}

0 commit comments

Comments
 (0)