Closing connection in GET request using Jersey Client 2.22.1 - jersey

I am using Jersey client for REST calls from Java code:
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.1</version>
</dependency>
In my GET request,
javax.ws.rs.client.Invocation.Builder builder = ClientBuilder.newClient().target(url).request();
builder.get().readEntity(String.class);
the client will be closed automatically after calling readEntity(String.class).
If I use,
builder.get(String.class);
I get the same output.
Is the connection closed automatically or do I need to close it manually in this case?

Short answer
Consider the following code:
Client client = ClientBuilder.newClient();
String result = client.target(url).request().get(String.class);
Under the hood, Jersey invokes Response#readEntity(Class<T>) if the request has succeeded and the connection will be closed for you. So the connection doesn't need to be closed manually in this situation.
Now consider the following code:
Client client = ClientBuilder.newClient();
Response response = client.target(url).request().get();
For this situation, you need to invoke Response#close() to close the connection. Or invoke Response#readEntity(Class<T>) to make Jersey close the connection for you.
Long answer
As stated in the documentation, if you don't read the entity, then you need to close the response manually by invoking Response#close().
For more details, have a look at Jersey's documentation about how to close connections:
5.7. Closing connections
The underlying connections are opened for each request and closed
after the response is received and entity is processed (entity is
read). See the following example:
final WebTarget target = ... some web target
Response response = target.path("resource").request().get();
System.out.println("Connection is still open.");
System.out.println("string response: " + response.readEntity(String.class));
System.out.println("Now the connection is closed.");
If you don't read the entity, then you need to close the response
manually by response.close().
Also if the entity is read into an
InputStream (by response.readEntity(InputStream.class)), the
connection stays open until you finish reading from the InputStream.
In that case, the InputStream or the Response should be closed
manually at the end of reading from InputStream.
Additionally, have a look at JerseyInvocation source. The most important parts are quoted below.
In the translate(ClientResponse, RequestScope, Class<T>) method you'll see that response.readEntity(Class<T>) is invoked.
JerseyInvocation.Builder#get(Class<T>)
Invoke HTTP GET method for the current request synchronously.
#Override
public <T> T get(final Class<T> responseType)
throws ProcessingException, WebApplicationException {
return method("GET", responseType);
}
JerseyInvocation.Builder#method(String, Class<T>)
Invoke an arbitrary method for the current request synchronously.
#Override
public <T> T method(final String name, final Class<T> responseType)
throws ProcessingException, WebApplicationException {
// responseType null check omitted for brevity
requestContext.setMethod(name);
return new JerseyInvocation(this).invoke(responseType);
}
JerseyInvocation#invoke(Class<T>)
Synchronously invoke the request and receive a response of the specified type back.
#Override
public <T> T invoke(final Class<T> responseType)
throws ProcessingException, WebApplicationException {
// responseType null check omitted for brevity
final ClientRuntime runtime = request().getClientRuntime();
final RequestScope requestScope = runtime.getRequestScope();
return requestScope.runInScope(new Producer<T>() {
#Override
public T call() throws ProcessingException {
try {
return translate(runtime.invoke(requestForCall(requestContext)),
requestScope, responseType);
} catch (final ProcessingException ex) {
// Exception handling omitted for brevity
}
}
});
}
JerseyInvocation#translate(ClientResponse, RequestScope, Class<T>)
If the request suceeded, the response entity is read as an instance of specified Java type using Response#readEntity(Class<T>):
private <T> T translate(final ClientResponse response, final RequestScope scope,
final Class<T> responseType) throws ProcessingException {
if (responseType == Response.class) {
return responseType.cast(new InboundJaxrsResponse(response, scope));
}
if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
try {
return response.readEntity(responseType);
} catch (final ProcessingException ex) {
// Exception handling omitted for brevity
}
} else {
throw convertToException(new InboundJaxrsResponse(response, scope));
}
}

Related

How to pass and handle Exceptions through HTTP responses in Spring?

I have a Client and Server module in my Spring project running on separate ports. The Client module makes a POST request to the Server via a RestTemplate. The Server-Module throws a custom Exception with a custom error-message. Currently, in my Project, the Server has a RestControllerAdvice Class that handles such exceptions as follows:
#RestControllerAdvice
public class AppRestControllerAdvice {
#ExceptionHandler(ApiException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public MessageData handle(ApiException e) {
MessageData data = new MessageData();
data.setMessage(e.getMessage());
return data;
}
}
On the Client side, the following method catches the Response from the Server.
#RestControllerAdvice
public class AppRestControllerAdvice {
#ExceptionHandler(ApiException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public MessageData handle(ApiException e) {
MessageData data = new MessageData();
data.setMessage(e.getMessage());
return data;
}
#ExceptionHandler(Throwable.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public MessageData handle(Throwable e) {
MessageData data = new MessageData();
data.setMessage("UNKNOWN ERROR- " + e.getMessage());
e.printStackTrace();
return data;
}
}
Whenever the Exception is thrown on the server, here is what I receive on the Client
{
"message": "UNKNOWN ERROR- org.springframework.web.client.HttpClientErrorException: 400 Bad Request"
}
My question is, how do I retrieve the Custom Exception message that originated on the Server?
Also, why isn't the correct RestControllerAdvice module on the Client side picking up the error? (The INTERNAL_SERVER_ERROR method catches the error instead of the BAD_REQUEST method.)
My question is, how do I retrieve the Custom Exception message that originated on the Server?
To retrieve the orignal exception message you have to use dedicated ResponseErrorHandler that is capable of extracting that information, rather than using the default one (DefaultResponseErrorHandler - which I assume you use because of the message you got - org.springframework.web.client.HttpClientErrorException: 400 Bad Request).
Create:
public class CustomerResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse httpResponse) throws IOException {
// here you have access to the response's body which potentially contains the exception message you are interested in
// simply extract it if possible and throw an exception with that message
// in other case you can simply call `super.handlerError()` - do whatever suits you
}
}
Then use it with your RestTemplate:
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
          .errorHandler(new CustomerResponseErrorHandler())
          .build();
}
}
Also, why isn't the correct RestControllerAdvice module on the Client side picking up the error? (The INTERNAL_SERVER_ERROR method catches the error instead of the BAD_REQUEST method.)
The correct method is executed - your RestTemplate at the moment is throwing HttpClientErrorException which is not an ApiException. It is a Throwable though.

Media Type not acceptable exception when using SseEmitter

Following this tutorial, I am trying to set up a Sse Emitter. When I open the html page I get a
Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
On client side (javascript) it sais it cannot connect to the server. I have tried various other tutorials, but I am clueless on why my code isnt working.
I set up a clean test project containing only and exactly the tutorial code.
I Was in the middle of doing something else when I got the same issue.
The code below fixed it.
Simply put Mismatch Media type.
#GetMapping(value = "/api/push/notification",headers = "Accept=*/*", consumes = MediaType.ALL_VALUE, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public String doNotify(#RequestParam("authToken") String token, #RequestParam("clientId") String clientId, HttpServletResponse response) throws InterruptedException, IOException {
response.addHeader("charset","UTF-8");
final SseEmitter emitter = new SseEmitter(30000l);
service.addEmitter(clientId,emitter);
service.sendConnectedNotification(clientId);
emitter.onCompletion(() -> service.removeEmitter(clientId));
emitter.onTimeout(() -> service.removeEmitter(clientId));
return "Connected OK";
}
any my event handler
#Async
public void doNotify(String clientId, Object data) {
SseEmitter emitter= emitters.get(clientId);
if(emitter!=null) {
try {
emitter .send(SseEmitter.event() .reconnectTime(30000)
.data(data,MediaType.APPLICATION_JSON)
.id(UUID.randomUUID().toString())
.name("Notification")
.comment("Client connection notification")
);
} catch (Exception e) {
emitters.remove(clientId);
}
}
}

Retrotif2 + RxJava sending POST request failed

I want to send POST request with Retrofit + RxJava, but it is failing and I don't know the reason. In one activity it's working, in another - don't want to work:
private void sendMerchantInfo() {
try {
String advertiserOriginalDeepLink = "https://mywebsite.com/main-1?param1=value1&param2=value2";
String urlGetParams = LinkParser.getUrlGETParams(advertiserOriginalDeepLink);
Map<Object, Object> merchantInfo = LinkParser.parseUrlGetParams(urlGetParams);
String merchantInfoJson = new Gson().toJson(merchantInfo); //{"param1":"value1","param2":"value2"}
String url = "https://api.endpoint.com/v1/system/merchant/process";
userService = this.serviceGenerator.createService(UserService.class, true);
final Observable observable = userService.sendUserInfo(
url, new RetrofitMapBody(merchantInfo))
.doOnNext(new Consumer<ResponseBody>() {
#Override
public void accept(ResponseBody responseBody) throws Exception {
//handle 200 OK.
}
})
.onErrorResumeNext((ObservableSource<? extends ResponseBody>) v ->
Crashlytics.log("Send user info attempt failed."))
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribe());
}
} catch (Exception exception) {
Crashlytics.log("Send user info attempt failed. " + exception.getMessage());
}
}
I suspect that problem in this part, I am trying to send request in OnCreate() method:
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
Tried to use this, but no effect:
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
What I am doing wrong? It always call onErrorResumeNext() It's probably something with threads because one time I got exception: networkonmainthreadexception. Please help.
Try using RxJava2 Adapter, it will save you a lot!
Step 1: Retrofit client setup
private Retrofit getRetrofitClient() {
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //option 1
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.newThread())) //option 2
.build();
}
Step 2: APIService interface (Example)
#GET("endpoint")
Single<ResponseModel> fetch();
Step 3: Usage
Single<ResponseModel> fetch() {
return getRetrofitClient()
.create(APIService.class)
.fetch();
}
Any non-2xx HTTP response will be wrapped in HttpException from which you can extract the status code, the status message and the full HTTP response.
Any connection errors will be wrapped in IOException
And that is all you need to do to wrap your network call in any RxJava stream.

Why this externa web service call go into error only when the call is performed using Spring RestTemplate?

I am working on a Spring project implementing a simple console application that have to call an external REST web service passing to it a parameter and obtaining a response from it.
The call to this webservice is:
http://5.249.148.180:8280/GLIS_Registration/6
where 6 is the specified ID. If you open this address in the browser (or by cURL tool) you will obtain the expected error message:
<?xml version="1.0" encoding="UTF-8"?>
<response>
<sampleid>IRGC 100000</sampleid>
<genus>Oryza</genus>
<error>PGRFA sampleid [IRGC 100000], genus [Oryza] already registered for this owner</error>
</response>
This error message is the expected response for this request and I correctly obtain it also using cURL tool to perform the request.
So I have to perform this GET request from my Spring application.
To do it I create this getResponse() method into a RestClient class:
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RestClient {
RestTemplate restTemplate;
String uriResourceRegistrationApi;
public RestClient() {
super();
restTemplate = new RestTemplate();
uriResourceRegistrationApi = "http://5.249.148.180:8280/GLIS_Registration/7";
}
public ResponseEntity<String> getResponse() {
ResponseEntity<String> response = restTemplate.getForEntity(uriResourceRegistrationApi, String.class);
return response;
}
}
Then I call this method from this test method:
#Test
public void singleResourceRestTest() {
System.out.println("singleResourceRestTest() START");
ResponseEntity<String> result = restClient.getResponse();
System.out.println("singleResourceRestTest() END");
}
But I am experiencing a very strange behavior, what it happens is:
1)The call to my external web service seems that happens (I saw it from the web services log).
2) The web service retrieve the parameter having value 7 but then it seems that can't use it as done without problem performing the request from the browser or by the shell statment:
curl -v http://5.249.148.180:8280/GLIS_Registration/7
But now, calling in this way, my webservice (I can't post the code because it is a WSO2 ESB flow) give me this error message:
<200 OK,<?xml version="1.0" encoding="UTF-8"?>
<response>
<error>Location information not correct</error>
<error>At least one between <genus> and <cropname> is required</error>
<error>Sample ID is required</error>
<error>Date is required</error>
<error>Creation method is required</error>
</response>,{Vary=[Accept-Encoding], Content-Type=[text/html; charset=UTF-8], Date=[Fri, 05 May 2017 14:07:09 GMT], Transfer-Encoding=[chunked], Connection=[keep-alive]}>
Looking the web service log it seems that performing the call using RestTemplate it have some problem to use the retrieved ID=7 to perform a database query.
I know it looks terribly strange and you can see: "The problem is of your web service and not of the Spring RestTemplate". This is only partially true because I implemented this custom method that perform a low level Http GET call, this callWsOldStyle() (putted into the previous RestClient class):
public void callWsOldStyle() {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL restAPIUrl = new URL("http://5.249.148.180:8280/GLIS_Registration/7");
connection = (HttpURLConnection) restAPIUrl.openConnection();
connection.setRequestMethod("GET");
// Read the response
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder jsonData = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
jsonData.append(line);
}
System.out.println(jsonData.toString());
}catch(Exception e) {
e.printStackTrace();
}
finally {
// Clean up
IOUtils.closeQuietly(reader);
if(connection != null)
connection.disconnect();
}
}
Using this method instead the RestTemplate one it works fine and this line:
System.out.println(jsonData.toString());
print the expected result:
<?xml version="1.0" encoding="UTF-8"?><response><sampleid>IRGC 100005</sampleid><genus>Oryza</genus><error>PGRFA sampleid [IRGC 100005], genus [Oryza] already registered for this owner</error></response>
To summarize:
Calling my WS from the browser it works.
Calling my WS using cURL it works.
Calling my WS using my callWsOldStyle() method it works.
Calling my WS using the method that use RestTemplate it go into error when my WS receive and try to handle the request.
So, what can be the cause of this issue? What am I missing? Maybe can depend by some wrong header or something like this?
As Pete said you are receiving an internal server error (status code 500) so you should check the server side of this rest service.
In any case you can do the following for the resttemplate
create an org.springframework.web.client.RequestCallback object if
you need to do something in the request
create an org.springframework.web.client.ResponseExtractor<String>
object in order to extract your data
use the resttemplate
org.springframework.web.client.RequestCallback
public class SampleRequestCallBack implements RequestCallback
{
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException
{
}
}
org.springframework.web.client.ResponseExtractor
public class CustomResponseExtractor implements ResponseExtractor<String>
{
private static final Logger logger = LoggerFactory.getLogger(CustomResponseExtractor.class.getName());
#Override
public String extractData(ClientHttpResponse response) throws IOException
{
try
{
String result = org.apache.commons.io.IOUtils.toString(response.getBody(), Charset.forName("UTF8"));
if( logger.isInfoEnabled() )
{
logger.info("Response received.\nStatus code: {}\n Result: {}",response.getStatusCode().value(), result);
}
return result;
}
catch (Exception e)
{
throw new IOException(e);
}
}
}
REST TEMPLATE CALL
#Test
public void testStack()
{
try
{
String url = "http://5.249.148.180:8280/GLIS_Registration/6";
String response = restTemplate.execute(url, HttpMethod.GET, new SampleRequestCallBack(), new CustomResponseExtractor());;
logger.info(response);
}
catch (Exception e)
{
logger.error("Errore", e);
}
}
Angelo

Asynchronous variation of the service activator EIP?

We have the following Camel route in our application:
from(webServiceUri).routeId("webServiceRoute")
.unmarshal(jaxb)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
final Message in = exchange.getIn();
final DataRequest body = in.getBody(DataRequest.class);
final DataRequest.Items items = body.getItems();
itemValidator.validate(items.getItem());
getContext().createProducerTemplate().sendBody(importUri, body);
DataResponse response = new DataResponse();
response.setReturnCode(ReturnCode.SUCCESS);
in.setBody(response);
}
})
.marshal(jaxb);
We want the "webServiceRoute" to return the response user as soon as the processor has validated the data and forwarded the message to the "importUri". But right now it seems like the response is not returned to the caller until the "importUri" exchange is completed. So my question is what is the "correct" way to asynchronously forward the received request to another queue? There will not be any reply from the "importUri" exchange (i.e. it should be InOnly).
You can replace .sendBody(importUri, body) by .asyncSendBody(importUri, body).
Nevertheless I find your route looks strange to me, why do you use a processor to forward your message. I would write something like:
DataResponse successResponse = new DataResponse();
response.setReturnCode(ReturnCode.SUCCESS);
from(webServiceUri).routeId("webServiceRoute")
.unmarshal(jaxb)
.bean(WebServiceRouteHelper.class,"validate")
.to(importUri)
.setBody(constant(sucessResponse))
.marshal(jaxb);
class WebServiceRouteHelper {
public DataRequest validate(DataRequest dataRequest) throws Exception {
final DataRequest.Items items = body.getItems();
itemValidator.validate(items.getItem());
return dataRequest;
}
}

Resources