/**
* Copyright 2014 Jordan Zimmerman
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.soabase.client;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import io.dropwizard.client.HttpClientBuilder;
import io.dropwizard.client.HttpClientConfiguration;
import io.dropwizard.client.JerseyClientBuilder;
import io.dropwizard.client.JerseyClientConfiguration;
import io.dropwizard.jersey.setup.JerseyEnvironment;
import io.dropwizard.setup.Environment;
import io.soabase.client.apache.WrappedHttpClient;
import io.soabase.client.jersey.JerseyRetryConnectorProvider;
import io.soabase.core.SoaBundle;
import io.soabase.core.SoaFeatures;
import io.soabase.core.features.client.RetryComponents;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.protocol.HttpContext;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.client.spi.ConnectorProvider;
import javax.ws.rs.client.Client;
import java.io.IOException;
import java.util.List;
public class ClientBuilder
{
private final RetryComponents retryComponents;
private final Environment environment;
private final List<Class<?>> providerClasses = Lists.newArrayList();
private final List<Object> providers = Lists.newArrayList();
private ConnectorProvider connectorProvider = null;
private static final int DEFAULT_MAX_RETRIES = 3;
private static final boolean DEFAULT_RETRY_500s = true;
public ClientBuilder(Environment environment)
{
this(environment, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_500s);
}
public ClientBuilder(Environment environment, int maxRetries)
{
this(environment, maxRetries, DEFAULT_RETRY_500s);
}
public ClientBuilder(Environment environment, int maxRetries, boolean retry500s)
{
this.environment = environment;
final SoaFeatures features = SoaBundle.getFeatures(environment);
retryComponents = new RetryComponents(features.getDiscovery(), maxRetries, retry500s, features.getExecutorBuilder());
if ( environment.getApplicationContext().getAttributes().getAttribute(ClientBuilder.class.getName()) == null )
{
AbstractBinder binder = new AbstractBinder()
{
@Override
protected void configure()
{
for ( String name : features.getNames(Client.class) )
{
bind(features.getNamed(Client.class, name)).named(name).to(Client.class);
}
for ( String name : features.getNames(HttpClient.class) )
{
bind(features.getNamed(HttpClient.class, name)).named(name).to(HttpClient.class);
}
}
};
environment.jersey().register(binder);
JerseyEnvironment adminJerseyEnvironment = features.getNamed(JerseyEnvironment.class, SoaFeatures.ADMIN_NAME);
if ( adminJerseyEnvironment != null )
{
adminJerseyEnvironment.register(binder);
}
environment.getApplicationContext().getAttributes().setAttribute(ClientBuilder.class.getName(), "");
}
}
public RetryComponents getRetryComponents()
{
return retryComponents;
}
public ClientBuilder withProvider(Class<?> klass) {
providerClasses.add(klass);
return this;
}
public ClientBuilder withProvider(Object provider) {
providers.add(provider);
return this;
}
public ClientBuilder withConnectorProvider(ConnectorProvider connectorProvider) {
this.connectorProvider = connectorProvider;
return this;
}
public Client buildJerseyClient(JerseyClientConfiguration configuration, String clientName)
{
ConnectorProvider localConnectorProvider;
if ( connectorProvider != null )
{
localConnectorProvider = connectorProvider;
}
else
{
HttpClientBuilder apacheHttpClientBuilder = new HttpClientBuilder(environment).using(configuration);
CloseableHttpClient closeableHttpClient = apacheHttpClientBuilder.build(clientName);
localConnectorProvider = new JerseyRetryConnectorProvider(retryComponents, closeableHttpClient, configuration.isChunkedEncodingEnabled());
}
JerseyClientBuilder builder = new JerseyClientBuilder(environment)
.using(configuration)
.using(localConnectorProvider);
for ( Class<?> klass : providerClasses )
{
builder = builder.withProvider(klass);
}
for ( Object provider : providers )
{
builder = builder.withProvider(provider);
}
Client client = builder
.build(clientName);
SoaBundle.getFeatures(environment).putNamed(client, Client.class, clientName);
return client;
}
public HttpClient buildHttpClient(HttpClientConfiguration configuration, String clientName)
{
Preconditions.checkState(providers.size() == 0, "HttpClient does not support providers");
Preconditions.checkState(providerClasses.size() == 0, "HttpClient does not support providers");
Preconditions.checkState(connectorProvider == null, "HttpClient does not support ConnectorProvider");
HttpRequestRetryHandler nullRetry = new HttpRequestRetryHandler()
{
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context)
{
return false;
}
};
HttpClient httpClient = new HttpClientBuilder(environment)
.using(configuration)
.using(nullRetry) // Apache's retry mechanism does not allow changing hosts. Do retries manually
.build(clientName);
HttpClient client = new WrappedHttpClient(httpClient, retryComponents);
SoaBundle.getFeatures(environment).putNamed(client, HttpClient.class, clientName);
return client;
}
}