org.glassfish.jersey.client.InvocationInterceptorStages Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaxrs-ri Show documentation
Show all versions of jaxrs-ri Show documentation
A bundle project producing JAX-RS RI bundles. The primary artifact is an "all-in-one" OSGi-fied JAX-RS RI bundle
(jaxrs-ri.jar).
Attached to that are two compressed JAX-RS RI archives. The first archive (jaxrs-ri.zip) consists of binary RI bits and
contains the API jar (under "api" directory), RI libraries (under "lib" directory) as well as all external
RI dependencies (under "ext" directory). The secondary archive (jaxrs-ri-src.zip) contains buildable JAX-RS RI source
bundle and contains the API jar (under "api" directory), RI sources (under "src" directory) as well as all external
RI dependencies (under "ext" directory). The second archive also contains "build.xml" ANT script that builds the RI
sources. To build the JAX-RS RI simply unzip the archive, cd to the created jaxrs-ri directory and invoke "ant" from
the command line.
/*
* Copyright (c) 2019, 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;
import org.glassfish.jersey.client.internal.LocalizationMessages;
import org.glassfish.jersey.client.internal.routing.ClientResponseMediaTypeDeterminer;
import org.glassfish.jersey.client.spi.PostInvocationInterceptor;
import org.glassfish.jersey.client.spi.PreInvocationInterceptor;
import org.glassfish.jersey.internal.inject.InjectionManager;
import org.glassfish.jersey.internal.inject.Providers;
import org.glassfish.jersey.model.internal.RankedComparator;
import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.client.ClientRequestFilter;
import jakarta.ws.rs.client.ClientResponseContext;
import jakarta.ws.rs.core.Configuration;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.EntityTag;
import jakarta.ws.rs.core.Link;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Collection;
import java.util.Date;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Utility class for {@link PreInvocationInterceptor} and {@link PostInvocationInterceptor} execution.
*
* @since 2.30
*/
class InvocationInterceptorStages {
private static final Logger LOGGER = Logger.getLogger(InvocationInterceptorStages.class.getName());
private InvocationInterceptorStages() {
// prevent instantiation
}
/**
* Create a {@link PreInvocationInterceptorStage} executing all {@link PreInvocationInterceptor PreInvocationInterceptors}.
*
* @param injectionManager the injection manager providing the registered {@code PreInvocationInterceptors}.
* @return {@code PreInvocationInterceptorStage} class to execute all the {@code PreInvocationInterceptors}.
*/
static PreInvocationInterceptorStage createPreInvocationInterceptorStage(InjectionManager injectionManager) {
return new PreInvocationInterceptorStage(injectionManager);
}
/**
* Create a {@link PostInvocationInterceptorStage} executing all {@link PostInvocationInterceptor PostInvocationInterceptors}.
*
* @param injectionManager the injection manager providing the registered {@code PostInvocationInterceptors}.
* @return {@code PostInvocationInterceptorStage} class to execute all the {@code PostInvocationInterceptors}.
*/
static PostInvocationInterceptorStage createPostInvocationInterceptorStage(InjectionManager injectionManager) {
return new PostInvocationInterceptorStage(injectionManager);
}
/**
* The stage to execute all the {@link PreInvocationInterceptor PreInvocationInterceptors}.
*/
static class PreInvocationInterceptorStage {
private Iterable preInvocationInterceptors;
private PreInvocationInterceptorStage(InjectionManager injectionManager) {
final RankedComparator comparator =
new RankedComparator<>(RankedComparator.Order.DESCENDING);
preInvocationInterceptors = Providers.getAllProviders(injectionManager, PreInvocationInterceptor.class, comparator);
}
/**
* Returns {@code true} if there is a {@link PreInvocationInterceptor} registered not yet executed in the request.
*
* @return {@code true} if there is a {@link PreInvocationInterceptor} yet to be executed.
*/
boolean hasPreInvocationInterceptors() {
return preInvocationInterceptors.iterator().hasNext();
}
/**
* Execute the {@link PreInvocationInterceptor PreInvocationInterceptors}.
*
* @param request {@link jakarta.ws.rs.client.ClientRequestContext} to be passed to {@code PreInvocationInterceptor}.
*/
void beforeRequest(ClientRequest request) {
final LinkedList throwables = new LinkedList<>();
final ClientRequestContext requestContext = new InvocationInterceptorRequestContext(request);
final Iterator preInvocationInterceptorIterator = preInvocationInterceptors.iterator();
while (preInvocationInterceptorIterator.hasNext()) {
try {
preInvocationInterceptorIterator.next().beforeRequest(requestContext);
} catch (Throwable throwable) {
LOGGER.log(Level.FINE, LocalizationMessages.PREINVOCATION_INTERCEPTOR_EXCEPTION(), throwable);
throwables.add(throwable);
}
}
if (!throwables.isEmpty()) {
throw suppressExceptions(throwables);
}
}
/**
* Create an empty {@link ClientRequestFilter} to executed the first after all {@code PreInvocationInterceptors}
* for the runtime to handle {@link ClientRequestContext#abortWith(Response)} utilization.
*
* @return an empty {@link ClientRequestFilter}.
*/
ClientRequestFilter createPreInvocationInterceptorFilter() {
return new ClientRequestFilter() {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
// do nothing, the filtering stage will handle requestContext#abortWith
// set in the PreInvocationInterceptor
}
};
}
}
/**
* The stage to execute all the {@link PostInvocationInterceptor PostInvocationInterceptors}.
*/
static class PostInvocationInterceptorStage {
private final Iterable postInvocationInterceptors;
private PostInvocationInterceptorStage(InjectionManager injectionManager) {
final RankedComparator comparator =
new RankedComparator<>(RankedComparator.Order.ASCENDING);
postInvocationInterceptors
= Providers.getAllProviders(injectionManager, PostInvocationInterceptor.class, comparator);
}
/**
* Returns {@code true} if there is a {@link PostInvocationInterceptor} registered not yet executed in the request.
*
* @return {@code true} if there is a {@link PostInvocationInterceptor} yet to be executed.
*/
boolean hasPostInvocationInterceptor() {
return postInvocationInterceptors.iterator().hasNext();
}
private ClientResponse afterRequestWithoutException(Iterator postInvocationInterceptors,
InvocationInterceptorRequestContext requestContext,
PostInvocationExceptionContext exceptionContext) {
boolean withoutException = true;
if (postInvocationInterceptors.hasNext()) {
final PostInvocationInterceptor postInvocationInterceptor = postInvocationInterceptors.next();
try {
postInvocationInterceptor.afterRequest(requestContext, exceptionContext.getResponseContext().get());
} catch (Throwable throwable) {
LOGGER.log(Level.FINE, LocalizationMessages.POSTINVOCATION_INTERCEPTOR_EXCEPTION(), throwable);
withoutException = false;
exceptionContext.throwables.add(throwable);
} finally {
return withoutException
? afterRequestWithoutException(postInvocationInterceptors, requestContext, exceptionContext)
: afterRequestWithException(postInvocationInterceptors, requestContext, exceptionContext);
}
} else {
return exceptionContext.responseContext;
}
}
private ClientResponse afterRequestWithException(Iterator postInvocationInterceptors,
InvocationInterceptorRequestContext requestContext,
PostInvocationExceptionContext exceptionContext) {
Throwable caught = null;
if (postInvocationInterceptors.hasNext()) {
final PostInvocationInterceptor postInvocationInterceptor = postInvocationInterceptors.next();
try {
postInvocationInterceptor.onException(requestContext, exceptionContext);
} catch (Throwable throwable) {
LOGGER.log(Level.FINE, LocalizationMessages.POSTINVOCATION_INTERCEPTOR_EXCEPTION(), throwable);
caught = throwable; // keep this if handleResponse clears the Throwables
}
try {
resolveResponse(requestContext, exceptionContext);
} catch (Throwable throwable) {
LOGGER.log(Level.FINE, LocalizationMessages.POSTINVOCATION_INTERCEPTOR_EXCEPTION(), throwable);
exceptionContext.throwables.add(throwable);
} finally {
if (caught != null) {
exceptionContext.throwables.add(caught);
}
}
return exceptionContext.throwables.isEmpty() && exceptionContext.responseContext != null
? afterRequestWithoutException(postInvocationInterceptors, requestContext, exceptionContext)
: afterRequestWithException(postInvocationInterceptors, requestContext, exceptionContext);
} else {
throw suppressExceptions(exceptionContext.throwables);
}
}
/**
* Execute all {@link PostInvocationInterceptor PostInvocationInterceptors}.
*
* @param request {@link ClientRequestContext}
* @param response {@link ClientResponseContext}
* @param previousException Any possible {@code Throwable} caught from executing the previous parts of the Client Request
* chain.
* @return the actual {@link ClientResponseContext} provided by the previous parts of the Client Request chain or by
* {@link PostInvocationInterceptor.ExceptionContext#resolve(Response)}.
* @throws {@code RuntimeException} if {@code previousException} or any new {@code Exception} was not
* {@link PostInvocationInterceptor.ExceptionContext#resolve(Response) resolved}.
*/
ClientResponse afterRequest(ClientRequest request, ClientResponse response, Throwable previousException) {
final PostInvocationExceptionContext exceptionContext
= new PostInvocationExceptionContext(response, previousException);
final InvocationInterceptorRequestContext requestContext = new InvocationInterceptorRequestContext(request);
return previousException != null
? afterRequestWithException(postInvocationInterceptors.iterator(), requestContext, exceptionContext)
: afterRequestWithoutException(postInvocationInterceptors.iterator(), requestContext, exceptionContext);
}
private static boolean resolveResponse(InvocationInterceptorRequestContext requestContext,
PostInvocationExceptionContext exceptionContext) {
if (exceptionContext.response != null) {
exceptionContext.throwables.clear();
final ClientResponseMediaTypeDeterminer determiner = new ClientResponseMediaTypeDeterminer(
requestContext.clientRequest.getWorkers());
determiner.setResponseMediaTypeIfNotSet(exceptionContext.response, requestContext.getConfiguration());
final ClientResponse response = new ClientResponse(requestContext.clientRequest, exceptionContext.response);
exceptionContext.responseContext = response;
exceptionContext.response = null;
return true;
} else {
return false;
}
}
}
private static class InvocationInterceptorRequestContext implements ClientRequestContext {
private final ClientRequest clientRequest;
private InvocationInterceptorRequestContext(ClientRequest clientRequestContext) {
this.clientRequest = clientRequestContext;
}
@Override
public Object getProperty(String name) {
return clientRequest.getProperty(name);
}
@Override
public Collection getPropertyNames() {
return clientRequest.getPropertyNames();
}
@Override
public void setProperty(String name, Object object) {
clientRequest.setProperty(name, object);
}
@Override
public void removeProperty(String name) {
clientRequest.removeProperty(name);
}
@Override
public URI getUri() {
return clientRequest.getUri();
}
@Override
public void setUri(URI uri) {
clientRequest.setUri(uri);
}
@Override
public String getMethod() {
return clientRequest.getMethod();
}
@Override
public void setMethod(String method) {
clientRequest.setMethod(method);
}
@Override
public MultivaluedMap getHeaders() {
return clientRequest.getHeaders();
}
@Override
public MultivaluedMap getStringHeaders() {
return clientRequest.getStringHeaders();
}
@Override
public String getHeaderString(String name) {
return clientRequest.getHeaderString(name);
}
@Override
public Date getDate() {
return clientRequest.getDate();
}
@Override
public Locale getLanguage() {
return clientRequest.getLanguage();
}
@Override
public MediaType getMediaType() {
return clientRequest.getMediaType();
}
@Override
public List getAcceptableMediaTypes() {
return clientRequest.getAcceptableMediaTypes();
}
@Override
public List getAcceptableLanguages() {
return clientRequest.getAcceptableLanguages();
}
@Override
public Map getCookies() {
return clientRequest.getCookies();
}
@Override
public boolean hasEntity() {
return clientRequest.hasEntity();
}
@Override
public Object getEntity() {
return clientRequest.getEntity();
}
@Override
public Class getEntityClass() {
return clientRequest.getEntityClass();
}
@Override
public Type getEntityType() {
return clientRequest.getEntityType();
}
@Override
public void setEntity(Object entity) {
clientRequest.setEntity(entity);
}
@Override
public void setEntity(Object entity, Annotation[] annotations, MediaType mediaType) {
clientRequest.setEntity(entity, annotations, mediaType);
}
@Override
public Annotation[] getEntityAnnotations() {
return clientRequest.getEntityAnnotations();
}
@Override
public OutputStream getEntityStream() {
return clientRequest.getEntityStream();
}
@Override
public void setEntityStream(OutputStream outputStream) {
clientRequest.setEntityStream(outputStream);
}
@Override
public Client getClient() {
return clientRequest.getClient();
}
@Override
public Configuration getConfiguration() {
return clientRequest.getConfiguration();
}
@Override
public void abortWith(Response response) {
if (clientRequest.getAbortResponse() != null) {
LOGGER.warning(LocalizationMessages.PREINVOCATION_INTERCEPTOR_MULTIPLE_ABORTIONS());
throw new IllegalStateException(LocalizationMessages.PREINVOCATION_INTERCEPTOR_MULTIPLE_ABORTIONS());
}
LOGGER.finer(LocalizationMessages.PREINVOCATION_INTERCEPTOR_ABORT_WITH());
clientRequest.abortWith(response);
}
}
private static class PostInvocationExceptionContext implements PostInvocationInterceptor.ExceptionContext {
private ClientResponse responseContext; // responseContext instance can be changed by PostInvocationInterceptor
private LinkedList throwables;
private Response response = null;
private PostInvocationExceptionContext(ClientResponse responseContext, Throwable throwable) {
this.responseContext = responseContext;
this.throwables = new LinkedList<>();
if (throwable != null) {
if (InvocationInterceptorException.class.isInstance(throwable)) { // from PreInvocationInterceptor
for (Throwable t : throwable.getSuppressed()) {
throwables.add(t);
}
} else {
throwables.add(throwable);
}
}
}
@Override
public Optional getResponseContext() {
return responseContext == null
? Optional.empty()
: Optional.of(new InvocationInterceptorResponseContext(responseContext));
}
@Override
public Deque getThrowables() {
return throwables;
}
@Override
public void resolve(Response response) {
if (this.response != null) {
LOGGER.warning(LocalizationMessages.POSTINVOCATION_INTERCEPTOR_MULTIPLE_RESOLVES());
throw new IllegalStateException(LocalizationMessages.POSTINVOCATION_INTERCEPTOR_MULTIPLE_RESOLVES());
}
LOGGER.finer(LocalizationMessages.POSTINVOCATION_INTERCEPTOR_RESOLVE());
this.response = response;
}
}
private static class InvocationInterceptorResponseContext implements ClientResponseContext {
private final ClientResponse clientResponse;
private InvocationInterceptorResponseContext(ClientResponse clientResponse) {
this.clientResponse = clientResponse;
}
@Override
public int getStatus() {
return clientResponse.getStatus();
}
@Override
public void setStatus(int code) {
clientResponse.setStatus(code);
}
@Override
public Response.StatusType getStatusInfo() {
return clientResponse.getStatusInfo();
}
@Override
public void setStatusInfo(Response.StatusType statusInfo) {
clientResponse.setStatusInfo(statusInfo);
}
@Override
public MultivaluedMap getHeaders() {
return clientResponse.getHeaders();
}
@Override
public String getHeaderString(String name) {
return clientResponse.getHeaderString(name);
}
@Override
public Set getAllowedMethods() {
return clientResponse.getAllowedMethods();
}
@Override
public Date getDate() {
return clientResponse.getDate();
}
@Override
public Locale getLanguage() {
return clientResponse.getLanguage();
}
@Override
public int getLength() {
return clientResponse.getLength();
}
@Override
public MediaType getMediaType() {
return clientResponse.getMediaType();
}
@Override
public Map getCookies() {
return clientResponse.getCookies();
}
@Override
public EntityTag getEntityTag() {
return clientResponse.getEntityTag();
}
@Override
public Date getLastModified() {
return clientResponse.getLastModified();
}
@Override
public URI getLocation() {
return clientResponse.getLocation();
}
@Override
public Set getLinks() {
return clientResponse.getLinks();
}
@Override
public boolean hasLink(String relation) {
return clientResponse.hasLink(relation);
}
@Override
public Link getLink(String relation) {
return clientResponse.getLink(relation);
}
@Override
public Link.Builder getLinkBuilder(String relation) {
return clientResponse.getLinkBuilder(relation);
}
@Override
public boolean hasEntity() {
return clientResponse.hasEntity();
}
@Override
public InputStream getEntityStream() {
return clientResponse.getEntityStream();
}
@Override
public void setEntityStream(InputStream input) {
clientResponse.setEntityStream(input);
}
}
private static ProcessingException createProcessingException(Throwable t) {
final ProcessingException processingException = createProcessingException(LocalizationMessages.EXCEPTION_SUPPRESSED());
processingException.addSuppressed(t);
return processingException;
}
private static ProcessingException createProcessingException(String message) {
return new InvocationInterceptorException(message);
}
private static class InvocationInterceptorException extends ProcessingException {
private InvocationInterceptorException(String message) {
super(message);
}
}
private static RuntimeException suppressExceptions(Deque throwables) {
if (throwables.size() == 1 && RuntimeException.class.isInstance(throwables.getFirst())) {
throw (RuntimeException) throwables.getFirst();
}
final ProcessingException processingException = createProcessingException(LocalizationMessages.EXCEPTION_SUPPRESSED());
for (Throwable throwable : throwables) {
// The first throwable is also marked as the cause for visibility in logs
if (processingException.getCause() == null) {
processingException.initCause(throwable);
}
processingException.addSuppressed(throwable);
}
return processingException;
}
}