@Test public void doesNotDefaultExceptionMappers() throws Exception { http.setRegisterDefaultExceptionMappers(false); assertThat(http.getRegisterDefaultExceptionMappers()).isFalse(); Environment environment = new Environment( "test", Jackson.newObjectMapper(), Validators.newValidator(), new MetricRegistry(), ClassLoader.getSystemClassLoader()); http.build(environment); for (Object singleton : environment.jersey().getResourceConfig().getSingletons()) { assertThat(singleton).isNotInstanceOf(ExceptionMapper.class); } }
@Test public void registersDefaultExceptionMappers() throws Exception { assertThat(http.getRegisterDefaultExceptionMappers()).isTrue(); Environment environment = new Environment( "test", Jackson.newObjectMapper(), Validators.newValidator(), new MetricRegistry(), ClassLoader.getSystemClassLoader()); http.build(environment); Set<Object> singletons = environment.jersey().getResourceConfig().getSingletons(); assertThat(singletons).hasAtLeastOneElementOfType(LoggingExceptionMapper.class); assertThat(singletons).hasAtLeastOneElementOfType(JsonProcessingExceptionMapper.class); assertThat(singletons).hasAtLeastOneElementOfType(EarlyEofExceptionMapper.class); assertThat(singletons).hasAtLeastOneElementOfType(JerseyViolationExceptionMapper.class); }
@Before public void setup() throws Exception { JerseyClientConfiguration clientConfiguration = new JerseyClientConfiguration(); clientConfiguration.setConnectionTimeout(Duration.milliseconds(SLEEP_TIME_IN_MILLIS / 2)); clientConfiguration.setTimeout(Duration.milliseconds(DEFAULT_CONNECT_TIMEOUT_IN_MILLIS)); environment = new Environment( "test-dropwizard-apache-connector", Jackson.newObjectMapper(), Validators.newValidator(), new MetricRegistry(), getClass().getClassLoader()); client = (JerseyClient) new JerseyClientBuilder(environment).using(clientConfiguration).build("test"); for (LifeCycle lifeCycle : environment.lifecycle().getManagedObjects()) { lifeCycle.start(); } }
private DataSourceFactory getDataSourceFactory(String resourceName) throws Exception { return new ConfigurationFactory<>( DataSourceFactory.class, Validators.newValidator(), Jackson.newObjectMapper(), "dw") .build(new File(Resources.getResource(resourceName).toURI())); }
/** * A convenience class for building {@link Client} instances. * * <p>Among other things, * * <ul> * <li>Backed by Apache HttpClient * <li>Disables stale connection checks * <li>Disables Nagle's algorithm * <li>Disables cookie management by default * <li>Compress requests and decompress responses using GZIP * <li>Supports parsing and generating JSON data using Jackson * </ul> * * @see HttpClientBuilder */ public class JerseyClientBuilder { private final List<Object> singletons = new ArrayList<>(); private final List<Class<?>> providers = new ArrayList<>(); private final Map<String, Object> properties = new LinkedHashMap<>(); private JerseyClientConfiguration configuration = new JerseyClientConfiguration(); private HttpClientBuilder apacheHttpClientBuilder; private Validator validator = Validators.newValidator(); private Environment environment; private ObjectMapper objectMapper; private ExecutorService executorService; private ConnectorProvider connectorProvider; public JerseyClientBuilder(Environment environment) { this.apacheHttpClientBuilder = new HttpClientBuilder(environment); this.environment = environment; } public JerseyClientBuilder(MetricRegistry metricRegistry) { this.apacheHttpClientBuilder = new HttpClientBuilder(metricRegistry); } @VisibleForTesting public void setApacheHttpClientBuilder(HttpClientBuilder apacheHttpClientBuilder) { this.apacheHttpClientBuilder = apacheHttpClientBuilder; } /** * Adds the given object as a Jersey provider. * * @param provider a Jersey provider * @return {@code this} */ public JerseyClientBuilder withProvider(Object provider) { singletons.add(requireNonNull(provider)); return this; } /** * Adds the given class as a Jersey provider. * * <p><b>N.B.:</b> This class must either have a no-args constructor or use Jersey's built-in * dependency injection. * * @param klass a Jersey provider class * @return {@code this} */ public JerseyClientBuilder withProvider(Class<?> klass) { providers.add(requireNonNull(klass)); return this; } /** * Sets the state of the given Jersey property. * * <p> * * <p><b>WARNING:</b> The default connector ignores Jersey properties. Use {@link * JerseyClientConfiguration} instead. * * @param propertyName the name of the Jersey property * @param propertyValue the state of the Jersey property * @return {@code this} */ public JerseyClientBuilder withProperty(String propertyName, Object propertyValue) { properties.put(propertyName, propertyValue); return this; } /** * Uses the given {@link JerseyClientConfiguration}. * * @param configuration a configuration object * @return {@code this} */ public JerseyClientBuilder using(JerseyClientConfiguration configuration) { this.configuration = configuration; apacheHttpClientBuilder.using(configuration); return this; } /** * Uses the given {@link Environment}. * * @param environment a Dropwizard {@link Environment} * @return {@code this} * @see #using(java.util.concurrent.ExecutorService, com.fasterxml.jackson.databind.ObjectMapper) */ public JerseyClientBuilder using(Environment environment) { this.environment = environment; return this; } /** * Use the given {@link Validator} instance. * * @param validator a {@link Validator} instance * @return {@code this} */ public JerseyClientBuilder using(Validator validator) { this.validator = validator; return this; } /** * Uses the given {@link ExecutorService} and {@link ObjectMapper}. * * @param executorService a thread pool * @param objectMapper an object mapper * @return {@code this} * @see #using(io.dropwizard.setup.Environment) */ public JerseyClientBuilder using(ExecutorService executorService, ObjectMapper objectMapper) { this.executorService = executorService; this.objectMapper = objectMapper; return this; } /** * Uses the given {@link ExecutorService}. * * @param executorService a thread pool * @return {@code this} * @see #using(io.dropwizard.setup.Environment) */ public JerseyClientBuilder using(ExecutorService executorService) { this.executorService = executorService; return this; } /** * Uses the given {@link ObjectMapper}. * * @param objectMapper an object mapper * @return {@code this} * @see #using(io.dropwizard.setup.Environment) */ public JerseyClientBuilder using(ObjectMapper objectMapper) { this.objectMapper = objectMapper; return this; } /** * Use the given {@link ConnectorProvider} instance. * * <p><b>WARNING:</b> Use it with a caution. Most of features will not work in a custom connection * provider. * * @param connectorProvider a {@link ConnectorProvider} instance * @return {@code this} */ public JerseyClientBuilder using(ConnectorProvider connectorProvider) { this.connectorProvider = connectorProvider; return this; } /** * Uses the {@link org.apache.http.client.HttpRequestRetryHandler} for handling request retries. * * @param httpRequestRetryHandler a HttpRequestRetryHandler * @return {@code this} */ public JerseyClientBuilder using(HttpRequestRetryHandler httpRequestRetryHandler) { apacheHttpClientBuilder.using(httpRequestRetryHandler); return this; } /** * Use the given {@link DnsResolver} instance. * * @param resolver a {@link DnsResolver} instance * @return {@code this} */ public JerseyClientBuilder using(DnsResolver resolver) { apacheHttpClientBuilder.using(resolver); return this; } /** * Use the given {@link Registry} instance of connection socket factories. * * @param registry a {@link Registry} instance of connection socket factories * @return {@code this} */ public JerseyClientBuilder using(Registry<ConnectionSocketFactory> registry) { apacheHttpClientBuilder.using(registry); return this; } /** * Use the given {@link HttpClientMetricNameStrategy} instance. * * @param metricNameStrategy a {@link HttpClientMetricNameStrategy} instance * @return {@code this} */ public JerseyClientBuilder using(HttpClientMetricNameStrategy metricNameStrategy) { apacheHttpClientBuilder.using(metricNameStrategy); return this; } /** * Use the given environment name. This is used in the user agent. * * @param environmentName an environment name to use in the user agent. * @return {@code this} */ public JerseyClientBuilder name(String environmentName) { apacheHttpClientBuilder.name(environmentName); return this; } /** * Use the given {@link HttpRoutePlanner} instance. * * @param routePlanner a {@link HttpRoutePlanner} instance * @return {@code this} */ public JerseyClientBuilder using(HttpRoutePlanner routePlanner) { apacheHttpClientBuilder.using(routePlanner); return this; } /** * Use the given {@link CredentialsProvider} instance. * * @param credentialsProvider a {@link CredentialsProvider} instance * @return {@code this} */ public JerseyClientBuilder using(CredentialsProvider credentialsProvider) { apacheHttpClientBuilder.using(credentialsProvider); return this; } /** * Builds the {@link Client} instance. * * @return a fully-configured {@link Client} */ public Client build(String name) { if ((environment == null) && ((executorService == null) || (objectMapper == null))) { throw new IllegalStateException( "Must have either an environment or both " + "an executor service and an object mapper"); } if (executorService == null) { executorService = environment .lifecycle() .executorService("jersey-client-" + name + "-%d") .minThreads(configuration.getMinThreads()) .maxThreads(configuration.getMaxThreads()) .workQueue(new ArrayBlockingQueue<>(configuration.getWorkQueueSize())) .build(); } if (objectMapper == null) { objectMapper = environment.getObjectMapper(); } if (environment != null) { validator = environment.getValidator(); } return build(name, executorService, objectMapper, validator); } private Client build( String name, ExecutorService threadPool, ObjectMapper objectMapper, Validator validator) { if (!configuration.isGzipEnabled()) { apacheHttpClientBuilder.disableContentCompression(true); } final Client client = ClientBuilder.newClient(buildConfig(name, threadPool, objectMapper, validator)); client.register(new JerseyIgnoreRequestUserAgentHeaderFilter()); // Tie the client to server lifecycle if (environment != null) { environment .lifecycle() .manage( new Managed() { @Override public void start() throws Exception {} @Override public void stop() throws Exception { client.close(); } }); } if (configuration.isGzipEnabled()) { client.register(new GZipDecoder()); client.register(new ConfiguredGZipEncoder(configuration.isGzipEnabledForRequests())); } return client; } private Configuration buildConfig( final String name, final ExecutorService threadPool, final ObjectMapper objectMapper, final Validator validator) { final ClientConfig config = new ClientConfig(); for (Object singleton : this.singletons) { config.register(singleton); } for (Class<?> provider : this.providers) { config.register(provider); } config.register(new JacksonMessageBodyProvider(objectMapper)); config.register(new HibernateValidationFeature(validator)); for (Map.Entry<String, Object> property : this.properties.entrySet()) { config.property(property.getKey(), property.getValue()); } config.register(new DropwizardExecutorProvider(threadPool)); if (connectorProvider == null) { final ConfiguredCloseableHttpClient apacheHttpClient = apacheHttpClientBuilder.buildWithDefaultRequestConfiguration(name); connectorProvider = (client, runtimeConfig) -> new DropwizardApacheConnector( apacheHttpClient.getClient(), apacheHttpClient.getDefaultRequestConfig(), configuration.isChunkedEncodingEnabled()); } config.connectorProvider(connectorProvider); return config; } }
@Test public void testGracefulShutdown() throws Exception { ObjectMapper objectMapper = Jackson.newObjectMapper(); Validator validator = Validators.newValidator(); MetricRegistry metricRegistry = new MetricRegistry(); Environment environment = new Environment( "test", objectMapper, validator, metricRegistry, ClassLoader.getSystemClassLoader()); CountDownLatch requestReceived = new CountDownLatch(1); CountDownLatch shutdownInvoked = new CountDownLatch(1); environment.jersey().register(new TestResource(requestReceived, shutdownInvoked)); final ScheduledExecutorService executor = Executors.newScheduledThreadPool(3); final Server server = http.build(environment); ((AbstractNetworkConnector) server.getConnectors()[0]).setPort(0); ScheduledFuture<Void> cleanup = executor.schedule( new Callable<Void>() { @Override public Void call() throws Exception { if (!server.isStopped()) { server.stop(); } executor.shutdownNow(); return null; } }, 5, TimeUnit.SECONDS); server.start(); final int port = ((AbstractNetworkConnector) server.getConnectors()[0]).getLocalPort(); Future<String> futureResult = executor.submit( new Callable<String>() { @Override public String call() throws Exception { URL url = new URL("http://localhost:" + port + "/app/test"); URLConnection connection = url.openConnection(); connection.connect(); return CharStreams.toString(new InputStreamReader(connection.getInputStream())); } }); requestReceived.await(); Future<Void> serverStopped = executor.submit( new Callable<Void>() { @Override public Void call() throws Exception { server.stop(); return null; } }); Connector[] connectors = server.getConnectors(); assertThat(connectors).isNotEmpty(); assertThat(connectors[0]).isInstanceOf(NetworkConnector.class); NetworkConnector connector = (NetworkConnector) connectors[0]; // wait for server to close the connectors while (true) { if (!connector.isOpen()) { shutdownInvoked.countDown(); break; } Thread.sleep(5); } String result = futureResult.get(); assertThat(result).isEqualTo("test"); serverStopped.get(); // cancel the cleanup future since everything succeeded cleanup.cancel(false); executor.shutdownNow(); }