Skip to content

Commit 70348ac

Browse files
committed
customizable DefaultTestClientFactory: it is now possible to use default implementation with customizations (override configure method);
add ApacheTestClientFactory: useful to support PATCH methods on jdk > 16; add `useApacheClient` (shortcut) configuration into `@TestGuiceyApp` and `@TestDropwizardApp` to simplify usage of ApacheTestClientFactory with annotations; add `useApacheClient()` (shortcut) method into extension and generic builders
1 parent 648dc51 commit 70348ac

14 files changed

Lines changed: 265 additions & 12 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
* Update to dropwizard 5 (requires java 17)
2+
* Customizable DefaultTestClientFactory: it is now possible to use default implementation
3+
with customizations (override `configure` method)
4+
- Add ApacheTestClientFactory: useful to support PATCH methods on jdk > 16
5+
- Add `useApacheClient` (shortcut) configuration into `@TestGuiceyApp` and `@TestDropwizardApp`
6+
to simplify usage of ApacheTestClientFactory with annotations
7+
- Add `useApacheClient()` (shortcut) method into extension and generic builders
28

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

dropwizard-guicey/src/main/java/ru/vyarus/dropwizard/guice/test/builder/TestSupportBuilder.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import jakarta.annotation.Nullable;
1111
import ru.vyarus.dropwizard.guice.test.GuiceyTestSupport;
1212
import ru.vyarus.dropwizard.guice.test.TestSupport;
13+
import ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory;
1314
import ru.vyarus.dropwizard.guice.test.client.DefaultTestClientFactory;
1415
import ru.vyarus.dropwizard.guice.test.client.TestClientFactory;
1516
import ru.vyarus.dropwizard.guice.test.util.ConfigOverrideUtils;
@@ -86,6 +87,18 @@ public TestSupportBuilder<C> clientFactory(final TestClientFactory factory) {
8687
return this;
8788
}
8889

90+
/**
91+
* Shortcut for {@link #clientFactory(ru.vyarus.dropwizard.guice.test.client.TestClientFactory)} to configure
92+
* {@link ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory}. The default
93+
* {@link org.glassfish.jersey.client.HttpUrlConnectorProvider} supports only HTTP 1.1 methods and have
94+
* problem with PATCH method usage on jdk > 16.
95+
*
96+
* @return builder instance for chained calls
97+
*/
98+
public TestSupportBuilder<C> useApacheClient() {
99+
return clientFactory(new ApacheTestClientFactory());
100+
}
101+
89102
/**
90103
* Listener used ONLY when builder run methods used! Listener may be used to perform additional initialization
91104
* or cleanup before/after application execution.
@@ -104,7 +117,9 @@ public TestSupportBuilder<C> listen(final TestListener<C> listener) {
104117
* creation. Prefer direct run ({@link #runCore()}) method usage (used support object could be easily obtained
105118
* with {@link ru.vyarus.dropwizard.guice.test.TestSupport#getContext()} in any place).
106119
* <p>
107-
* IMPORTANT: listeners could not be used (because they are implemented as a custom run callback)
120+
* IMPORTANT: listeners could not be used (because they are implemented as a custom run callback).
121+
* Custom {@link ru.vyarus.dropwizard.guice.test.client.TestClientFactory} would also be lost! Use direct run
122+
* methods to not lose them.
108123
*
109124
* @return guicey test support implementation
110125
*/
@@ -121,7 +136,9 @@ public GuiceyTestSupport<C> buildCore() {
121136
* creation. Prefer direct run ({@link #runWeb()}) method usage (used support object could be easily obtained
122137
* with {@link ru.vyarus.dropwizard.guice.test.TestSupport#getContext()} in any place).
123138
* <p>
124-
* IMPORTANT: listeners could not be used (because they are implemented as a custom run callback)
139+
* IMPORTANT: listeners could not be used (because they are implemented as a custom run callback).
140+
* Custom {@link ru.vyarus.dropwizard.guice.test.client.TestClientFactory} would also be lost! Use direct run
141+
* methods to not lose them.
125142
*
126143
* @return dropwizard test support implementation
127144
*/
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package ru.vyarus.dropwizard.guice.test.client;
2+
3+
import io.dropwizard.testing.DropwizardTestSupport;
4+
import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
5+
import org.glassfish.jersey.client.JerseyClientBuilder;
6+
7+
/**
8+
* Use {@link Apache5ConnectorProvider} instead of default {@link org.glassfish.jersey.client.HttpUrlConnectorProvider}.
9+
*
10+
* @author Vyacheslav Rusakov
11+
* @since 12.09.2025
12+
*/
13+
public class ApacheTestClientFactory extends DefaultTestClientFactory {
14+
15+
@Override
16+
protected void configure(final JerseyClientBuilder builder, final DropwizardTestSupport<?> support) {
17+
builder.getConfiguration().connectorProvider(new Apache5ConnectorProvider());
18+
}
19+
}

dropwizard-guicey/src/main/java/ru/vyarus/dropwizard/guice/test/client/DefaultTestClientFactory.java

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.dropwizard.jersey.jackson.JacksonFeature;
44
import io.dropwizard.testing.DropwizardTestSupport;
5+
import jakarta.ws.rs.core.Feature;
56
import org.glassfish.jersey.client.ClientProperties;
67
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
78
import org.glassfish.jersey.client.JerseyClient;
@@ -19,8 +20,16 @@
1920
* <p>
2021
* By default, log all requests and responses into system out (console). This could be disabled with
2122
* {@link #disableConsoleLog()} method (system property).
23+
* <p>
24+
* NOTE: default {@link org.glassfish.jersey.client.HttpUrlConnectorProvider} does not support PATCH method on
25+
* jdk > 16 (requires additional --add-opens). To workaround it, use apache or connection provider.
26+
* <p>
27+
* If client customization is required, extend this class and override
28+
* {@link #configure(org.glassfish.jersey.client.JerseyClientBuilder,
29+
* io.dropwizard.testing.DropwizardTestSupport)} method.
2230
*
2331
* @author Vyacheslav Rusakov
32+
* @see ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory
2433
* @since 15.11.2023
2534
*/
2635
public class DefaultTestClientFactory implements TestClientFactory {
@@ -50,24 +59,53 @@ public JerseyClient create(final DropwizardTestSupport<?> support) {
5059
final JerseyClientBuilder builder = new JerseyClientBuilder()
5160
.register(new JacksonFeature(support.getEnvironment().getObjectMapper()))
5261
// log everything to simplify debug
53-
.register(LoggingFeature.builder()
54-
.withLogger(System.getProperty(USE_LOGGER) != null
55-
// use console log by default
56-
? Logger.getLogger(ClientSupport.class.getName()) : new ConsoleLogger())
57-
.verbosity(LoggingFeature.Verbosity.PAYLOAD_TEXT)
58-
.level(Level.INFO)
59-
.build())
62+
.register(createLogger())
6063
.property(ClientProperties.CONNECT_TIMEOUT, 1000)
6164
.property(ClientProperties.READ_TIMEOUT, 5000)
6265
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
66+
applyMultiPartSupport(builder);
67+
configure(builder, support);
68+
return builder.build();
69+
}
70+
71+
/**
72+
* Configure logging feature.
73+
*
74+
* @return logging feature
75+
*/
76+
protected Feature createLogger() {
77+
return LoggingFeature.builder()
78+
.withLogger(System.getProperty(USE_LOGGER) != null
79+
// use console log by default
80+
? Logger.getLogger(ClientSupport.class.getName()) : new ConsoleLogger())
81+
.verbosity(LoggingFeature.Verbosity.PAYLOAD_TEXT)
82+
.level(Level.INFO)
83+
.build();
84+
}
85+
86+
/**
87+
* Apply multipart support, if multipart feature is present in classpath.
88+
*
89+
* @param builder client builder
90+
*/
91+
protected void applyMultiPartSupport(final JerseyClientBuilder builder) {
6392
try {
6493
// when dropwizard-forms used automatically register multipart feature
6594
final Class<?> cls = Class.forName("org.glassfish.jersey.media.multipart.MultiPartFeature");
6695
builder.register(cls);
6796
} catch (Exception ignored) {
6897
// do nothing - no multipart feature available
6998
}
70-
return builder.build();
99+
}
100+
101+
/**
102+
* Provides ability to customize default client in extending class.
103+
*
104+
* @param builder client builder (pre-configured)
105+
* @param support dropwizard support instance (for accessing environment and configuration)
106+
*/
107+
protected void configure(final JerseyClientBuilder builder, final DropwizardTestSupport<?> support) {
108+
// empty
71109
}
72110

73111
/**

dropwizard-guicey/src/main/java/ru/vyarus/dropwizard/guice/test/jupiter/TestDropwizardApp.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,22 @@
230230
/**
231231
* Custom client factory for {@link ru.vyarus.dropwizard.guice.test.ClientSupport} object. Custom factory
232232
* may be required in case when custom client configuration is required for test.
233+
* <p>
234+
* Note: value is ignored when {@link #useApacheClient()} set to true
233235
*
234236
* @return client factory class
235237
*/
236238
Class<? extends TestClientFactory> clientFactory() default DefaultTestClientFactory.class;
239+
240+
/**
241+
* Shortcut for {@link #clientFactory()} to configure
242+
* {@link ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory}. The default
243+
* {@link org.glassfish.jersey.client.HttpUrlConnectorProvider} supports only HTTP 1.1 methods and have
244+
* problem with PATCH method usage on jdk > 16.
245+
* <p>
246+
* Note: {@link #clientFactory()} value is ignored when set to true,
247+
*
248+
* @return true to use apache connection provider in jersey client
249+
*/
250+
boolean useApacheClient() default false;
237251
}

dropwizard-guicey/src/main/java/ru/vyarus/dropwizard/guice/test/jupiter/TestGuiceyApp.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,25 @@
204204
* may be required in case when custom client configuration is required for test.
205205
* <p>
206206
* Core test does not start dropwizard web services, but client still could be used to call external services.
207+
* <p>
208+
* Note: value is ignored when {@link #useApacheClient()} set to true
207209
*
208210
* @return client factory class
209211
*/
210212
Class<? extends TestClientFactory> clientFactory() default DefaultTestClientFactory.class;
211213

214+
/**
215+
* Shortcut for {@link #clientFactory()} to configure
216+
* {@link ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory}. The default
217+
* {@link org.glassfish.jersey.client.HttpUrlConnectorProvider} supports only HTTP 1.1 methods and have
218+
* problem with PATCH method usage on jdk > 16.
219+
* <p>
220+
* Note: {@link #clientFactory()} value is ignored when set to true,
221+
*
222+
* @return true to use apache connection provider in jersey client
223+
*/
224+
boolean useApacheClient() default false;
225+
212226
/**
213227
* By default, guicey simulates {@link io.dropwizard.lifecycle.Managed} objects lifecycle.
214228
* <p>

dropwizard-guicey/src/main/java/ru/vyarus/dropwizard/guice/test/jupiter/ext/TestDropwizardAppExtension.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.junit.jupiter.api.extension.RegisterExtension;
1414
import org.junit.platform.commons.support.AnnotationSupport;
1515
import ru.vyarus.dropwizard.guice.hook.GuiceyConfigurationHook;
16+
import ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory;
1617
import ru.vyarus.dropwizard.guice.test.jupiter.TestDropwizardApp;
1718
import ru.vyarus.dropwizard.guice.test.jupiter.env.TestEnvironmentSetup;
1819
import ru.vyarus.dropwizard.guice.test.jupiter.ext.conf.ExtensionBuilder;
@@ -364,7 +365,11 @@ static Config parse(final TestDropwizardApp ann, final TestExtensionsTracker tra
364365
res.tracker.debug = ann.debug();
365366
res.reuseApp = ann.reuseApplication();
366367
res.defaultExtensionsEnabled = ann.useDefaultExtensions();
367-
res.clientFactory(ann.clientFactory());
368+
if (ann.useApacheClient()) {
369+
res.clientFactory(ApacheTestClientFactory.class);
370+
} else {
371+
res.clientFactory(ann.clientFactory());
372+
}
368373
return res;
369374
}
370375
}

dropwizard-guicey/src/main/java/ru/vyarus/dropwizard/guice/test/jupiter/ext/TestGuiceyAppExtension.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.junit.platform.commons.support.AnnotationSupport;
1212
import ru.vyarus.dropwizard.guice.hook.GuiceyConfigurationHook;
1313
import ru.vyarus.dropwizard.guice.test.GuiceyTestSupport;
14+
import ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory;
1415
import ru.vyarus.dropwizard.guice.test.jupiter.TestGuiceyApp;
1516
import ru.vyarus.dropwizard.guice.test.jupiter.env.TestEnvironmentSetup;
1617
import ru.vyarus.dropwizard.guice.test.jupiter.ext.conf.ExtensionBuilder;
@@ -333,7 +334,11 @@ static Config parse(final TestGuiceyApp ann, final TestExtensionsTracker tracker
333334
res.tracker.debug = ann.debug();
334335
res.reuseApp = ann.reuseApplication();
335336
res.defaultExtensionsEnabled = ann.useDefaultExtensions();
336-
res.clientFactory(ann.clientFactory());
337+
if (ann.useApacheClient()) {
338+
res.clientFactory(ApacheTestClientFactory.class);
339+
} else {
340+
res.clientFactory(ann.clientFactory());
341+
}
337342
res.managedLifecycle = ann.managedLifecycle();
338343
return res;
339344
}

dropwizard-guicey/src/main/java/ru/vyarus/dropwizard/guice/test/jupiter/ext/conf/ExtensionBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.junit.jupiter.api.function.ThrowingConsumer;
88
import org.junit.jupiter.api.function.ThrowingSupplier;
99
import ru.vyarus.dropwizard.guice.hook.GuiceyConfigurationHook;
10+
import ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory;
1011
import ru.vyarus.dropwizard.guice.test.client.TestClientFactory;
1112
import ru.vyarus.dropwizard.guice.test.util.ConfigModifier;
1213
import ru.vyarus.dropwizard.guice.test.util.ConfigOverrideExtensionValue;
@@ -370,6 +371,18 @@ public T clientFactory(final TestClientFactory factory) {
370371
return self();
371372
}
372373

374+
/**
375+
* Shortcut for {@link #clientFactory(ru.vyarus.dropwizard.guice.test.client.TestClientFactory)} to configure
376+
* {@link ru.vyarus.dropwizard.guice.test.client.ApacheTestClientFactory}. The default
377+
* {@link org.glassfish.jersey.client.HttpUrlConnectorProvider} supports only HTTP 1.1 methods and have
378+
* problem with PATCH method usage on jdk > 16.
379+
*
380+
* @return builder instance for chained calls
381+
*/
382+
public T useApacheClient() {
383+
return clientFactory(new ApacheTestClientFactory());
384+
}
385+
373386
@SuppressWarnings("unchecked")
374387
private T self() {
375388
return (T) this;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package ru.vyarus.dropwizard.guice.test.general;
2+
3+
import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
4+
import org.junit.jupiter.api.Assertions;
5+
import org.junit.jupiter.api.Test;
6+
import ru.vyarus.dropwizard.guice.support.AutoScanApplication;
7+
import ru.vyarus.dropwizard.guice.test.TestSupport;
8+
9+
/**
10+
* @author Vyacheslav Rusakov
11+
* @since 12.09.2025
12+
*/
13+
public class ApacheFactoryTest {
14+
15+
@Test
16+
void testApacheFactoryShortcut() throws Exception{
17+
Class<?> cls = TestSupport.build(AutoScanApplication.class)
18+
.useApacheClient()
19+
.runCore(injector -> TestSupport
20+
.getContextClient().getClient().getConfiguration().getConnectorProvider().getClass());
21+
Assertions.assertEquals(Apache5ConnectorProvider.class, cls);
22+
}
23+
}

0 commit comments

Comments
 (0)