Efficiently managing a pool of HttpClients - java

Currently, I am trying to create an utility class which is to serve as the calling point of any HTTP request executed throughout the application. My implementation currently looks somewhat like this:
public class HttpHelper {
private final static HttpClientConnectionManager poolingConnManager = new PoolingHttpClientConnectionManager();
private final static CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(poolingConnManager).build();
public static String GET(String endpoint, HashMap<String, String> headers) {
/* httpClient.execute(...) */
}
}
Thus, HTTP requests can be smoothly executed from anywhere in the application through:
String result = HttpHelper.GET(URL, headers);
Would this be an appropriate way of doing it? Is the pool of connections managed efficiently in such a class?

Related

Batch operations in JAX-RS

Context
I am currently working on a JavaEE project with a lot of existing resource based JAX-RS services. For this project we would like to have batch processing to prevent a lot of separate calls and, most importantly, to execute these different methods in a transactional context for rollback purposes with the native MongoDB driver. We want to avoid manually creating new methods for all possible combinations. I could not find any solution to this issue on Stack Overflow so I started analyzing the implementation of RESTEasy and I came up with the following solution.
Below a simplified/pseudo version of my code:
JAX-RS method
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("execute")
public Response executeBatch(BatchRequestWrapper batchRequestWrapper) throws UnsupportedEncodingException
{
// Retrieve information from context
HttpServletRequest httpServletRequest = ResteasyProviderFactory.getContextData(HttpServletRequest.class);
HttpServletResponse httpServletResponse = ResteasyProviderFactory.getContextData(HttpServletResponse.class);
ServletContext servletContext = ResteasyProviderFactory.getContextData(ServletContext.class);
HttpResponse httpResponse = ResteasyProviderFactory.getContextData(HttpResponse.class);
SynchronousDispatcher dispatcher = (SynchronousDispatcher) ResteasyProviderFactory.getContextData(Dispatcher.class);
ResteasyHttpHeaders httpHeaders = (ResteasyHttpHeaders) ResteasyProviderFactory.getContextData(HttpHeaders.class);
ResteasyUriInfo uriInfo = (ResteasyUriInfo) ResteasyProviderFactory.getContextData(UriInfo.class);
// Create Mongo Client Session object and save it in a Singleton which contains a ThreadLocal object so that DAO layer can reuse the client session object for all methods.
// Iterate over all the methods and invoke dispatcher
for (BatchRequest batchRequest : batchRequestWrapper.getBatchRequests())
{
// Update URI based on specific endpoint
uriInfo.setRequestUri(URI.create(batchRequest.getUri()));
// Temporary use mock response for the response
MockHttpResponse response = new MockHttpResponse();
// Create httpservletinput message from RESTEasy lib to pass to the dispatcher. It will automatically resolve all parameters/methods etc.
HttpServletInputMessage request = new HttpServletInputMessage(httpServletRequest, httpServletResponse, servletContext, httpResponse, httpHeaders, uriInfo, batchRequest.getHttpMethod(), dispatcher);
// Set body in input stream if body is specified. This will inject the correct 'body' parameters in the methods. Query and Path parameters are already resolved in the method above.
if(!Strings.isNullOrEmpty(batchRequest.getBody()))
{
InputStream targetStream = new ByteArrayInputStream(batchRequest.getBody().getBytes(StandardCharsets.UTF_8));
request.setInputStream(targetStream);
}
// Actual invoke
dispatcher.invoke(request, response);
// Do something with response object
}
// Clean or abort session based on invoke result
return Response.ok().entity(null).build();
}
Request Object
public class BatchRequestWrapper
{
private List<BatchRequest> batchRequests;
public List<BatchRequest> getBatchRequests()
{
return batchRequests;
}
public void setBatchRequests(List<BatchRequest> batchRequests)
{
this.batchRequests = batchRequests;
}
}
public class BatchRequest
{
private String uri;
private String httpMethod;
private String body;
public String getUri()
{
return uri;
}
public void setUri(String uri)
{
this.uri = uri;
}
public String getHttpMethod()
{
return httpMethod;
}
public void setHttpMethod(String httpMethod)
{
this.httpMethod = httpMethod;
}
public String getBody()
{
return body;
}
public void setBody(String body)
{
this.body = body;
}
}
My solution works with one new REST method and let's me reuse all the existing JAX-RS annotated methods in the project. Before I actually fully implement this and bring it to production, I would like to know if this is the way to actually do this or are there better alternatives? I am not a big fan of the hard dependency on RESTEasy though.

Unable to get response from API using Micronaut

I am trying to make a POST request using the Micronaut framework with Java. I have created a 'client' class that is called and makes the request. Here is the code:
#Singleton
public class ApiClient {
private final HttpClient httpClient;
private final URI uri;
public ApiClient(#Client(URL) HttpClient httpClient) {
this.httpClient = httpClient;
uri = UriBuilder.of(API_URL).build();
}
Mono<List<ApiResponse>> fetchResponse() {
HttpRequest<?> request = HttpRequest.POST(uri, BODY)
.header("Authorization", API_KEY);
return Mono.from(httpClient.retrieve(request, Argument.listOf(ApiResponse.class)));
}
}
My problem is that I have no idea what the response from API is. As far as I can tell, the call is made. But because the data returns in a Flux object, I can't interrogate the object to find the response. My suspicion is that the POJO I'm trying to store the response in isn't working. I can't tell if this is the case though.
You need to subscribe to the publisher to actually make the request and get a response. There are several subscribe methods depending on what you want to do

Apache HttpClient: how to get redirect locations when using Fluent API?

I am using Apache HttpClient 4.5's Fluent API, the following way:
CloseableHttpClient client = HttpClientBuilder.create().build();
Executor executor = Executor.newInstance(client);
Response resp = executor.execute(Request.Get(url));
Unfortunately, I can't find a proper way of getting redirect locations (the RedirectLocation class).
They are normally stored in a HttpContext object; but when using the Fluent API, its instance is created locally in Executor.execute(...) and never exposed:
public Response execute(final Request request) {
final HttpClientContext localContext = HttpClientContext.create();
/* ... */
return new Response(request.internalExecute(this.httpclient, localContext));
}
I've tried to override Executor.execute(...) method by creating a decorator/proxy class; by creating a child class; even by copy-pasting its source into my own package.
None of these solutions were feasible (for one, Executor invokes package-local methods of other classes).
The only workaround I've managed to find so far was to implement my own RedirectStrategy and pass it to HttpClient:
public class MyRedirectStrategy extends DefaultRedirectStrategy {
private HttpContext context;
public RedirectLocations getRedirectLocations() {
return (RedirectLocations) context.getAttribute(REDIRECT_LOCATIONS);
}
#Override
public URI getLocationURI(final HttpRequest request, final HttpResponse response, final HttpContext context) {
this.context = context; // to keep the HttpContext!
return super.getLocationURI(request, response, context);
}
}
/* ... */
RedirectStrategy stra = new MyRedirectStrategy();
CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(stra).build();
Executor executor = Executor.newInstance(client);
Response resp = executor.execute(Request.Get(url));
for (final String redirectedUri : stra.getRedirectLocations()) {
/* process redirectedUri's */
}
However, I don't think it is a proper solution. To my knowledge, RedirectStrategies were intended to be immutable, stateless classes, as they are passed to the HttpClient, which can be shared by multiple threads/connections.
In other words: logically, the HttpContext is not a property of a RedirectStrategy.
Any ideas?
Thanks!
You cannot. HC fluent API hides HttpContext instance way from the consumer. Consider using HttpClient APIs directly.

How to use HttpComponentsClientHttpRequestFactory with RestTemplate efficiently?

I am using RestTemplate along with its factory HttpComponentsClientHttpRequestFactory in one of my projects. In this project, I need to make a Http url call to my server which is running a restful service which returns back the response as a JSON String.
Below is my code -
public class GetUserClientData {
public String getData(KeyHolder keys) {
return new HTTPRequestAccess(keys).makeHttpRequest();
}
}
Below is my class which wraps the HttpClient part -
public class HTTPRequestAccess {
// should this be static?
private RestTemplate restTemplate;
private KeyHolder keys;
private int timeout;
public HTTPRequestAccess(KeyHolder keys){
this(keys.getTimeoutValue()); // setting timeout to RestTemplate
this.keys = keys;
}
public HTTPRequestAccess(int timeout) {
this.timeout = timeout;
restTemplate = new RestTemplate(clientHttpRequestFactory());
}
private ClientHttpRequestFactory clientHttpRequestFactory() {
// is this not expensive that every time we are creating this new object?
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(timeout);
factory.setConnectTimeout(timeout);
return factory;
}
public String makeHttpRequest() {
String response = null;
try {
// some logic here
String url = generateURL();
response = restTemplate.getForObject(url, String.class);
// some logic here
} catch (RestClientException ex) {
// log exception and do some stuff
} catch (Exception ex) {
// log exception
}
return response;
}
}
Should RestTemplate and HttpComponentsClientHttpRequestFactory be static here in my HTTPRequestAccess class as if I see it correctly, I am recreating the whole connection pool for each request in RestTemplate which is not the right way I guess because each factory has connection and thread pool and they are pretty heavy object I guess.
In general what is the best way to use RestTemplate along with its factory HttpComponentsClientHttpRequestFactory in a multithreading environment? I guess RestTemplate is thread safe but I don't think HttpComponentsClientHttpRequestFactory is thread safe. Correct me if I am wrong? I will be running this library under heavy load.
I am using spring-web-3.2.8.RELEASE version.
In one of my projects, I had created a static instance of HttpComponentsClientHttpRequestFactory and passed it to every RestTemplate.
Though, in here, it is suggested to also have a global instance of RestTemplate.
Maybe irrelevant, but one important point is to pass HttpClients.createDefault() to your HttpComponentsClientHttpRequestFactory while constructing it since by default, this factory uses system properties to create HttpClient for your factory and that may cause a lot of pain in production environment.
You may also pass your custom HttpClient.

How to use CXF client in thread safe way

I have created the client stub for below service using apache-cxf's wsdl2java command.
http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL
Then I invoke the getWeatherInformation() method as below.
Weather weatherService = new Weather();
WeatherSoap weatherSoap = weatherService.getWeatherSoap();
ArrayOfWeatherDescription result = weatherSoap.getWeatherInformation();
I have read that cxf clients are thread safe. But I have a doubt whether it is safe to use the same WeatherSoap instance accross multiple threads? Or instead should/can I use an instance of Weather class, accross multiple threads?
Thanks.
EDIT:
What I do is I have exposed a RESTful API to public and if somebody calls that rest service I call another SOAP service. Above code is used to call the SOAP service. What I want to know is should I execute all the above lines for each rest request or can I reuse an instance of Weather or WeatherSoap to serve all the REST requests.
Yes CXF is thread safe, you can use single instance/singleton for Weather and WeatherSoap, you can think of cxf as similar to servlet engine which handles all the infrastructure for you such as transport, databinding for you. I had similar usecase, where I had a front end presentation layer and number of network servers, to interact between these I had a rest for presentation and SOAP which implements business logic as well as interacts with servers. Hence I implemented a soap client in rest layer. I had requirement were I needed split rest request and invoke parallel soap calls which had time delays 800ms. I performance tested the entire setup and did not run-up into any thread issues.
So coming into to client implementation
Pure Java
public class MySoapClient{
private static WeatherSoap weatherSoap;
private MySoapClient(){
}
public static WeatherSoap getClient(){
if(weatherSoap==null){
Weather weatherService = new Weather();
weatherSoap= weatherService.getWeatherSoap();
}
return weatherSoap;
}
}
And I would modify the Weather class to get SOAP url from properties file.
#WebServiceClient(name = "Weather",
wsdlLocation = "classpath:weather.wsdl",
targetNamespace = "http://ws.cdyne.com/WeatherWS/")
public class Weather extends Service {
private static final Logger LOG = LoggerFactory.getLogger(Weather.class);
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://ws.cdyne.com/WeatherWS/", "Weather");
public final static QName WeatherHttpPost = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherHttpPost");
public final static QName WeatherHttpGet = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherHttpGet");
public final static QName WeatherSoap12 = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherSoap12");
public final static QName WeatherSoap = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherSoap");
static {
URL url = null;
try {
url = new URL(MyPropertiesUtil.getProperty("app.weather.url"));
} catch (MalformedURLException e) {
LOG.error(e.getMessage(), e);
}
if (url == null) {
LOG.error("an issue with your url");
}
WSDL_LOCATION = url;
}
public Weather(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
}
public Weather(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public Weather() {
super(WSDL_LOCATION, SERVICE);
}
//All the other interface methods
}
Using Spring
if you are using spring you can make things even simpler, you can eliminate Weather.java class by using configuration file as shown below and let cxf generate proxy for you.
<jaxws:client id="weatherSoap" serviceClass="com.cdyne.ws.weatherws.WeatherSoap" address="${app.weather.url}" />
And Business Class would look like below.
#Component
MyBusinessLogic{
#Autowired
private WeatherSoap weatherSoap;
public ArrayOfWeatherDescription getOutput(){
return weatherSoap.getWeatherInformation();
}
}

Categories