Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offer the Client (partial) response in ProcessingException. #4460

Merged
merged 3 commits into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -34,6 +34,7 @@

import javax.inject.Provider;

import org.glassfish.jersey.client.internal.ClientResponseProcessingException;
import org.glassfish.jersey.client.internal.LocalizationMessages;
import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
import org.glassfish.jersey.client.spi.Connector;
Expand Down Expand Up @@ -299,6 +300,9 @@ public ClientResponse invoke(final ClientRequest request) {
}

response = Stages.process(response, responseProcessingRoot);
} catch (final ClientResponseProcessingException crpe) {
processingException = crpe;
response = crpe.getClientResponse();
} catch (final ProcessingException pe) {
processingException = pe;
} catch (final Throwable t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.BiFunction;
import java.util.logging.Logger;

import javax.ws.rs.BadRequestException;
Expand Down Expand Up @@ -57,6 +58,7 @@
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.client.internal.ClientResponseProcessingException;
import org.glassfish.jersey.client.internal.LocalizationMessages;
import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.internal.inject.Providers;
Expand Down Expand Up @@ -612,9 +614,10 @@ private ClientRequest requestForCall(final ClientRequest requestContext) {
public Response invoke() throws ProcessingException, WebApplicationException {
final ClientRuntime runtime = request().getClientRuntime();
final RequestScope requestScope = runtime.getRequestScope();
return requestScope.runInScope(
(Producer<Response>) () -> new InboundJaxrsResponse(runtime.invoke(requestForCall(requestContext)),
requestScope));

return runInScope(((Producer<Response>) () ->
new InboundJaxrsResponse(runtime.invoke(requestForCall(requestContext)), requestScope)),
requestScope);
}

@Override
Expand All @@ -624,17 +627,9 @@ public <T> T invoke(final Class<T> responseType) throws ProcessingException, Web
}
final ClientRuntime runtime = request().getClientRuntime();
final RequestScope requestScope = runtime.getRequestScope();
//noinspection Duplicates
return requestScope.runInScope(() -> {
try {
return translate(runtime.invoke(requestForCall(requestContext)), requestScope, responseType);
} catch (final ProcessingException ex) {
if (ex.getCause() instanceof WebApplicationException) {
throw (WebApplicationException) ex.getCause();
}
throw ex;
}
});

return runInScope(() ->
translate(runtime.invoke(requestForCall(requestContext)), requestScope, responseType), requestScope);
}

@Override
Expand All @@ -644,41 +639,37 @@ public <T> T invoke(final GenericType<T> responseType) throws ProcessingExceptio
}
final ClientRuntime runtime = request().getClientRuntime();
final RequestScope requestScope = runtime.getRequestScope();
//noinspection Duplicates
return requestScope.runInScope(() -> {
try {
return translate(runtime.invoke(requestForCall(requestContext)), requestScope, responseType);
} catch (final ProcessingException ex) {
if (ex.getCause() instanceof WebApplicationException) {
throw (WebApplicationException) ex.getCause();
}
throw ex;

return runInScope(() ->
translate(runtime.invoke(requestForCall(requestContext)), requestScope, responseType), requestScope);
}

private <T> T runInScope(Producer<T> producer, RequestScope scope) throws ProcessingException, WebApplicationException {
return scope.runInScope(() -> call(producer, scope));
}

private <T> T call(Producer<T> producer, RequestScope scope)
throws ProcessingException, WebApplicationException {
try {
return producer.call();
} catch (final ClientResponseProcessingException crpe) {
throw new ResponseProcessingException(
translate(crpe.getClientResponse(), scope, Response.class), crpe.getCause()
);
} catch (final ProcessingException ex) {
if (WebApplicationException.class.isInstance(ex.getCause())) {
throw (WebApplicationException) ex.getCause();
}
});
throw ex;
}
}

@Override
public Future<Response> submit() {
final CompletableFuture<Response> responseFuture = new CompletableFuture<>();
final ClientRuntime runtime = request().getClientRuntime();
runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() {

@Override
public void completed(final ClientResponse response, final RequestScope scope) {
if (!responseFuture.isCancelled()) {
responseFuture.complete(new InboundJaxrsResponse(response, scope));
} else {
response.close();
}
}

@Override
public void failed(final ProcessingException error) {
if (!responseFuture.isCancelled()) {
responseFuture.completeExceptionally(error);
}
}
}));
runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext),
new InvocationResponseCallback<>(responseFuture, (request, scope) -> translate(request, scope, Response.class))));

return responseFuture;
}
Expand All @@ -689,35 +680,10 @@ public <T> Future<T> submit(final Class<T> responseType) {
throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL());
}
final CompletableFuture<T> responseFuture = new CompletableFuture<>();
//noinspection Duplicates
final ClientRuntime runtime = request().getClientRuntime();
runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() {

@Override
public void completed(final ClientResponse response, final RequestScope scope) {
if (responseFuture.isCancelled()) {
response.close();
return;
}
try {
responseFuture.complete(translate(response, scope, responseType));
} catch (final ProcessingException ex) {
failed(ex);
}
}

@Override
public void failed(final ProcessingException error) {
if (responseFuture.isCancelled()) {
return;
}
if (error.getCause() instanceof WebApplicationException) {
responseFuture.completeExceptionally(error.getCause());
} else {
responseFuture.completeExceptionally(error);
}
}
}));
runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext),
new InvocationResponseCallback<T>(responseFuture, (request, scope) -> translate(request, scope, responseType))));

return responseFuture;
}
Expand Down Expand Up @@ -753,36 +719,10 @@ public <T> Future<T> submit(final GenericType<T> responseType) {
throw new IllegalArgumentException(LocalizationMessages.RESPONSE_TYPE_IS_NULL());
}
final CompletableFuture<T> responseFuture = new CompletableFuture<>();
//noinspection Duplicates
final ClientRuntime runtime = request().getClientRuntime();
runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext), new ResponseCallback() {

@Override
public void completed(final ClientResponse response, final RequestScope scope) {
if (responseFuture.isCancelled()) {
response.close();
return;
}

try {
responseFuture.complete(translate(response, scope, responseType));
} catch (final ProcessingException ex) {
failed(ex);
}
}

@Override
public void failed(final ProcessingException error) {
if (responseFuture.isCancelled()) {
return;
}
if (error.getCause() instanceof WebApplicationException) {
responseFuture.completeExceptionally(error.getCause());
} else {
responseFuture.completeExceptionally(error);
}
}
}));
runtime.submit(runtime.createRunnableForAsyncProcessing(requestForCall(requestContext),
new InvocationResponseCallback<T>(responseFuture, (request, scope) -> translate(request, scope, responseType))));

return responseFuture;
}
Expand Down Expand Up @@ -883,14 +823,24 @@ public void completed(final ClientResponse response, final RequestScope scope) {

@Override
public void failed(final ProcessingException error) {
Exception called = null;
try {
if (error.getCause() instanceof WebApplicationException) {
responseFuture.completeExceptionally(error.getCause());
} else if (!responseFuture.isCancelled()) {
responseFuture.completeExceptionally(error);
try {
call(() -> { throw error; }, null);
} catch (Exception ex) {
called = ex;
responseFuture.completeExceptionally(ex);
}
}
} finally {
callback.failed(error.getCause() instanceof CancellationException ? error.getCause() : error);
callback.failed(
error.getCause() instanceof CancellationException
? error.getCause()
: called != null ? called : error
);
}
}
};
Expand All @@ -899,7 +849,10 @@ public void failed(final ProcessingException error) {
} catch (final Throwable error) {
final ProcessingException ce;
//noinspection ChainOfInstanceofChecks
if (error instanceof ProcessingException) {
if (error instanceof ClientResponseProcessingException) {
ce = new ProcessingException(error.getCause());
responseFuture.completeExceptionally(ce);
} else if (error instanceof ProcessingException) {
ce = (ProcessingException) error;
responseFuture.completeExceptionally(ce);
} else if (error instanceof WebApplicationException) {
Expand Down Expand Up @@ -1006,4 +959,45 @@ ClientRequest request() {
public String toString() {
return "JerseyInvocation [" + request().getMethod() + ' ' + request().getUri() + "]";
}

private class InvocationResponseCallback<R> implements ResponseCallback {
private final CompletableFuture<R> responseFuture;
private final BiFunction<ClientResponse, RequestScope, R> producer;

private InvocationResponseCallback(CompletableFuture<R> responseFuture,
BiFunction<ClientResponse, RequestScope, R> producer) {
this.responseFuture = responseFuture;
this.producer = producer;
}

@Override
public void completed(final ClientResponse response, final RequestScope scope) {
if (responseFuture.isCancelled()) {
response.close();
return;
}


try {
responseFuture.complete(producer.apply(response, scope));
} catch (final ProcessingException ex) {
failed(ex);
}
}

@Override
public void failed(final ProcessingException error) {
if (responseFuture.isCancelled()) {
return;
}

try {
call(() -> {
throw error;
}, null);
} catch (Exception exception) {
responseFuture.completeExceptionally(exception);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.client.internal;

import org.glassfish.jersey.client.ClientResponse;

import javax.ws.rs.ProcessingException;

/**
* This is a representation of a @{link ProcessingException} containing a @{link ClientResponse} instance.
* This exception is meant to be converted to a {@code ResponseProcessingException} at a point where
* {@link ClientResponse} is converted to a {@code Response} before it is delivered to a user.
* @since 2.31
*/
public class ClientResponseProcessingException extends ProcessingException {
private static final long serialVersionUID = 3389677946623416847L;
private final ClientResponse clientResponse;

/**
* An instance of {@code ClientResponseProcessingException} containing {@link ClientResponse} and cause {@link Throwable}.
* @param clientResponse a {@link ClientResponse} to be converted to {@code Response}.
* @param cause a cause of the exception.
*/
public ClientResponseProcessingException(ClientResponse clientResponse, Throwable cause) {
super(cause);
this.clientResponse = clientResponse;
}

/**
* Return a {@link ClientResponse} to be converted to {@code Response} to be put to a {@code ResponseProcessingException}.
* @return a {@link ClientResponse} to be converted to {@code Response}.
*/
public ClientResponse getClientResponse() {
return clientResponse;
}
}
Loading