package org.jboss.resteasy.core;
import org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext;
import org.jboss.resteasy.plugins.server.Cleanable;
import org.jboss.resteasy.plugins.server.Cleanables;
import org.jboss.resteasy.resteasy_jaxrs.i18n.LogMessages;
import org.jboss.resteasy.resteasy_jaxrs.i18n.Messages;
import org.jboss.resteasy.specimpl.BuiltResponse;
import org.jboss.resteasy.specimpl.RequestImpl;
import org.jboss.resteasy.spi.Dispatcher;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpRequestPreprocessor;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.InternalServerErrorException;
import org.jboss.resteasy.spi.Registry;
import org.jboss.resteasy.spi.ResourceInvoker;
import org.jboss.resteasy.spi.ResteasyAsynchronousContext;
import org.jboss.resteasy.spi.ResteasyConfiguration;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.spi.UnhandledException;
import org.jboss.resteasy.tracing.RESTEasyTracingLogger;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceContext;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletionException;
import java.util.function.Consumer;
public class SynchronousDispatcher implements Dispatcher
{
protected ResteasyProviderFactory providerFactory;
protected Registry registry;
protected List<HttpRequestPreprocessor> requestPreprocessors = new ArrayList<HttpRequestPreprocessor>();
@SuppressWarnings("rawtypes")
protected Map<Class, Object> defaultContextObjects = new HashMap<Class, Object>();
protected Set<String> unwrappedExceptions = new HashSet<String>();
protected boolean bufferExceptionEntityRead = false;
protected boolean bufferExceptionEntity = true;
{
@SuppressWarnings("unused")
LogMessages preload = LogMessages.LOGGER;
}
public SynchronousDispatcher(final ResteasyProviderFactory providerFactory)
{
this.providerFactory = providerFactory;
this.registry = new ResourceMethodRegistry(providerFactory);
defaultContextObjects.put(Providers.class, providerFactory);
defaultContextObjects.put(Registry.class, registry);
defaultContextObjects.put(Dispatcher.class, this);
defaultContextObjects.put(InternalDispatcher.class, InternalDispatcher.getInstance());
}
public SynchronousDispatcher(final ResteasyProviderFactory providerFactory, final ResourceMethodRegistry registry)
{
this(providerFactory);
this.registry = registry;
defaultContextObjects.put(Registry.class, registry);
}
public ResteasyProviderFactory getProviderFactory()
{
return providerFactory;
}
public Registry getRegistry()
{
return registry;
}
public Map<Class, Object> getDefaultContextObjects()
{
return defaultContextObjects;
}
public Set<String> getUnwrappedExceptions()
{
return unwrappedExceptions;
}
public Response preprocess(HttpRequest request) {
RESTEasyTracingLogger.initTracingSupport(providerFactory, request);
Response aborted = null;
RESTEasyTracingLogger tracingLogger = RESTEasyTracingLogger.getInstance(request);
try {
final long totalTimestamp = tracingLogger.timestamp("PRE_MATCH_SUMMARY");
for (HttpRequestPreprocessor preprocessor : this.requestPreprocessors) {
final long timestamp = tracingLogger.timestamp("PRE_MATCH");
preprocessor.preProcess(request);
tracingLogger.logDuration("PRE_MATCH", timestamp, preprocessor.getClass().toString());
}
tracingLogger.logDuration("PRE_MATCH_SUMMARY", totalTimestamp, this.requestPreprocessors.size());
ContainerRequestFilter[] requestFilters = providerFactory.getContainerRequestFilterRegistry().preMatch();
PreMatchContainerRequestContext requestContext = new PreMatchContainerRequestContext(request, requestFilters, null);
aborted = requestContext.filter();
} catch (Exception e) {
aborted = new ExceptionHandler(providerFactory, unwrappedExceptions).handleException(request, e);
}
return aborted;
}
protected void preprocess(HttpRequest request, HttpResponse response, Runnable continuation) {
Response aborted = null;
PreMatchContainerRequestContext requestContext = null;
RESTEasyTracingLogger tracingLogger = RESTEasyTracingLogger.getInstance(request);
try {
final long totalTimestamp = tracingLogger.timestamp("PRE_MATCH_SUMMARY");
for (HttpRequestPreprocessor preprocessor : this.requestPreprocessors) {
final long timestamp = tracingLogger.timestamp("PRE_MATCH");
preprocessor.preProcess(request);
tracingLogger.logDuration("PRE_MATCH", timestamp, preprocessor.getClass().toString());
}
tracingLogger.logDuration("PRE_MATCH_SUMMARY", totalTimestamp, this.requestPreprocessors.size());
ContainerRequestFilter[] requestFilters = providerFactory.getContainerRequestFilterRegistry().preMatch();
requestContext = new PreMatchContainerRequestContext(request, requestFilters,
() -> {
continuation.run();
return null;
});
aborted = requestContext.filter();
} catch (Exception e) {
if (requestContext == null || !requestContext.startedContinuation()) {
writeException(request, response, e, t -> {
});
return;
} else {
rethrow(e);
}
}
if (aborted != null) {
tracingLogger.log("FINISHED", response.getStatus());
tracingLogger.flush(response.getOutputHeaders());
writeResponse(request, response, aborted);
return;
}
}
@SuppressWarnings("unchecked")
public static <T extends Throwable> void rethrow(Throwable t) throws T
{
throw (T)t;
}
@Deprecated
public void writeException(HttpRequest request, HttpResponse response, Throwable e)
{
writeException(request, response, e, t -> {});
}
public void writeException(HttpRequest request, HttpResponse response, Throwable e, Consumer<Throwable> onComplete)
{
if (!bufferExceptionEntityRead)
{
bufferExceptionEntityRead = true;
ResteasyConfiguration context = ResteasyContext.getContextData(ResteasyConfiguration.class);
if (context != null)
{
String s = context.getParameter("resteasy.buffer.exception.entity");
if (s != null)
{
bufferExceptionEntity = Boolean.parseBoolean(s);
}
}
}
if (response.isCommitted() && response.suppressExceptionDuringChunkedTransfer())
{
LogMessages.LOGGER.debug(Messages.MESSAGES.responseIsCommitted());
onComplete.accept(null);
return;
}
Response handledResponse = new ExceptionHandler(providerFactory, unwrappedExceptions).handleException(request, e);
if (handledResponse == null) throw new UnhandledException(e);
if (!bufferExceptionEntity)
{
response.getOutputHeaders().add("resteasy.buffer.exception.entity", "false");
}
try
{
ServerResponseWriter.writeNomapResponse(((BuiltResponse) handledResponse), request, response, providerFactory, onComplete);
}
catch (Exception e1)
{
throw new UnhandledException(e1);
} finally {
RESTEasyTracingLogger tracingLogger = RESTEasyTracingLogger.getInstance(request);
tracingLogger.log("FINISHED", response.getStatus());
tracingLogger.flush(response.getOutputHeaders());
}
}
public void invoke(HttpRequest request, HttpResponse response)
{
RESTEasyTracingLogger.initTracingSupport(providerFactory, request);
RESTEasyTracingLogger.logStart(request);
try
{
pushContextObjects(request, response);
preprocess(request, response, () -> {
ResourceInvoker invoker = null;
try
{
try
{
invoker = getInvoker(request);
}
catch (Exception exception)
{
writeException(request, response, exception, t -> {});
return;
}
invoke(request, response, invoker);
}
finally
{
clearContextData();
}
});
}
finally
{
clearContextData();
}
}
public void invokePropagateNotFound(HttpRequest request, HttpResponse response) throws NotFoundException
{
try
{
pushContextObjects(request, response);
preprocess(request, response, () -> {
ResourceInvoker invoker = null;
try
{
try
{
invoker = getInvoker(request);
}
catch (Exception failure)
{
if (failure instanceof NotFoundException)
{
throw ((NotFoundException) failure);
}
else
{
writeException(request, response, failure, t->{});
return;
}
}
invoke(request, response, invoker);
}
finally
{
clearContextData();
}
});
}
finally
{
clearContextData();
}
}
public ResourceInvoker getInvoker(HttpRequest request)
throws Failure
{
LogMessages.LOGGER.pathInfo(request.getUri().getPath());
if (!request.isInitial())
{
throw new InternalServerErrorException(Messages.MESSAGES.isNotInitialRequest(request.getUri().getPath()));
}
ResourceInvoker invoker = registry.getResourceInvoker(request);
if (invoker == null)
{
throw new NotFoundException(Messages.MESSAGES.unableToFindJaxRsResource(request.getUri().getPath()));
}
RESTEasyTracingLogger logger = RESTEasyTracingLogger.getInstance(request);
logger.log("MATCH_RESOURCE", invoker);
logger.log("MATCH_RESOURCE_METHOD", invoker.getMethod());
return invoker;
}
@SuppressWarnings("unchecked")
public void pushContextObjects(final HttpRequest request, final HttpResponse response)
{
@SuppressWarnings("rawtypes")
Map contextDataMap = ResteasyContext.getContextDataMap();
contextDataMap.put(HttpRequest.class, request);
contextDataMap.put(HttpResponse.class, response);
contextDataMap.put(HttpHeaders.class, request.getHttpHeaders());
contextDataMap.put(UriInfo.class, request.getUri());
contextDataMap.put(Request.class, new RequestImpl(request, response));
contextDataMap.put(ResteasyAsynchronousContext.class, request.getAsyncContext());
ResourceContext resourceContext = new ResourceContext()
{
@Override
public <T> T getResource(Class<T> resourceClass)
{
return providerFactory.injectedInstance(resourceClass, request, response);
}
@Override
public <T> T initResource(T resource)
{
providerFactory.injectProperties(resource, request, response);
return resource;
}
};
contextDataMap.put(ResourceContext.class, resourceContext);
contextDataMap.putAll(defaultContextObjects);
contextDataMap.put(Cleanables.class, new Cleanables());
contextDataMap.put(PostResourceMethodInvokers.class, new PostResourceMethodInvokers());
}
public Response internalInvocation(HttpRequest request, HttpResponse response, Object entity)
{
ResteasyContext.addContextDataLevel();
boolean pushedBody = false;
try
{
MessageBodyParameterInjector.pushBody(entity);
pushedBody = true;
ResourceInvoker invoker = getInvoker(request);
if (invoker != null)
{
pushContextObjects(request, response);
return execute(request, response, invoker);
}
return null;
}
finally
{
ResteasyContext.removeContextDataLevel();
if (pushedBody)
{
MessageBodyParameterInjector.popBody();
}
}
}
public void clearContextData()
{
Map<Class<?>, Object> map = ResteasyContext.getContextDataMap(false);
Cleanables cleanables = map != null ? (Cleanables) map.get(Cleanables.class) : null;
if (cleanables != null)
{
for (Iterator<Cleanable> it = cleanables.getCleanables().iterator(); it.hasNext(); )
{
try
{
it.next().clean();
}
catch(Exception e)
{
}
}
ResteasyContext.clearContextData();
}
MessageBodyParameterInjector.clearBodies();
}
public Response execute(HttpRequest request, HttpResponse response, ResourceInvoker invoker)
{
Response jaxrsResponse = null;
try
{
RESTEasyTracingLogger logger = RESTEasyTracingLogger.getInstance(request);
logger.log("DISPATCH_RESPONSE", jaxrsResponse);
request.getAsyncContext().initialRequestStarted();
jaxrsResponse = invoker.invoke(request, response);
request.getAsyncContext().initialRequestEnded();
if (request.getAsyncContext().isSuspended())
{
request.getAsyncContext().getAsyncResponse().initialRequestThreadFinished();
jaxrsResponse = null;
}
}
catch (CompletionException e)
{
jaxrsResponse = new ExceptionHandler(providerFactory, unwrappedExceptions).handleException(request, e.getCause());
if (jaxrsResponse == null) throw new UnhandledException(e.getCause());
}
catch (Exception e)
{
jaxrsResponse = new ExceptionHandler(providerFactory, unwrappedExceptions).handleException(request, e);
if (jaxrsResponse == null) throw new UnhandledException(e);
}
return jaxrsResponse;
}
public void invoke(HttpRequest request, HttpResponse response, ResourceInvoker invoker)
{
RESTEasyTracingLogger tracingLogger = RESTEasyTracingLogger.getInstance(request);
Response jaxrsResponse = null;
try
{
request.getAsyncContext().initialRequestStarted();
jaxrsResponse = invoker.invoke(request, response);
request.getAsyncContext().initialRequestEnded();
tracingLogger.log("DISPATCH_RESPONSE", jaxrsResponse);
if (request.getAsyncContext().isSuspended())
{
request.getAsyncContext().getAsyncResponse().initialRequestThreadFinished();
jaxrsResponse = null;
}
}
catch (CompletionException e)
{
writeException(request, response, e.getCause(), t->{});
return;
}
catch (Exception e)
{
invoker.getMethodStatisticsLogger().incFailureCnt();
writeException(request, response, e, t->{});
return;
}
if (jaxrsResponse != null) {
writeResponse(request, response, jaxrsResponse);
}
}
@Deprecated
public void asynchronousDelivery(HttpRequest request, HttpResponse response, Response jaxrsResponse) throws IOException
{
asynchronousDelivery(request, response, jaxrsResponse, t -> {});
}
public void asynchronousDelivery(HttpRequest request, HttpResponse response, Response jaxrsResponse, Consumer<Throwable> onComplete) throws IOException
{
if (jaxrsResponse == null) return;
try
{
pushContextObjects(request, response);
ServerResponseWriter.writeNomapResponse((BuiltResponse) jaxrsResponse, request, response, providerFactory, onComplete);
}
finally
{
ResteasyContext.removeContextDataLevel();
}
}
public void unhandledAsynchronousException(HttpResponse response, Throwable ex) {
LogMessages.LOGGER.unhandledAsynchronousException(ex);
if (!response.isCommitted()) {
try
{
response.reset();
response.sendError(500);
}
catch (IOException e)
{
}
}
}
@Deprecated
public void asynchronousExceptionDelivery(HttpRequest request, HttpResponse response, Throwable exception)
{
asynchronousExceptionDelivery(request, response, exception, t -> {});
}
public void asynchronousExceptionDelivery(HttpRequest request, HttpResponse response, Throwable exception, Consumer<Throwable> onComplete)
{
try
{
pushContextObjects(request, response);
writeException(request, response, exception, t -> {
if(t != null)
unhandledAsynchronousException(response, t);
onComplete.accept(null);
ResteasyContext.removeContextDataLevel();
});
}
catch (Throwable ex)
{
unhandledAsynchronousException(response, ex);
onComplete.accept(ex);
}
}
protected void writeResponse(HttpRequest request, HttpResponse response, Response jaxrsResponse)
{
try
{
ServerResponseWriter.writeNomapResponse((BuiltResponse) jaxrsResponse, request, response, providerFactory,
t -> {
if(t != null) {
if(request.getAsyncContext().isSuspended()
&& !request.getAsyncContext().isOnInitialRequest()) {
try {
writeException(request, response, t, t2 -> {});
}catch(Throwable ex) {
unhandledAsynchronousException(response, ex);
}
} else {
rethrow(t);
}
}
});
}
catch (Exception e)
{
writeException(request, response, e, t -> {});
}
finally {
RESTEasyTracingLogger tracingLogger = RESTEasyTracingLogger.getInstance(request);
tracingLogger.log("FINISHED", response.getStatus());
tracingLogger.flush(response.getOutputHeaders());
}
}
public void addHttpPreprocessor(HttpRequestPreprocessor httpPreprocessor)
{
requestPreprocessors.add(httpPreprocessor);
}
}