package org.jboss.resteasy.core.interception;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.core.interception.jaxrs.SuspendableContainerResponseContext;
import org.jboss.resteasy.core.ServerResponseWriter.RunnableWithIOException;
import org.jboss.resteasy.core.SynchronousDispatcher;
import org.jboss.resteasy.resteasy_jaxrs.i18n.LogMessages;
import org.jboss.resteasy.specimpl.BuiltResponse;
import org.jboss.resteasy.spi.ApplicationException;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyAsynchronousResponse;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
public class ContainerResponseContextImpl implements SuspendableContainerResponseContext
{
protected final HttpRequest request;
protected final HttpResponse httpResponse;
protected final BuiltResponse jaxrsResponse;
private ResponseContainerRequestContext requestContext;
private ContainerResponseFilter[] responseFilters;
private RunnableWithIOException continuation;
private int currentFilter;
private boolean suspended;
private boolean filterReturnIsMeaningful = true;
private Map<Class<?>, Object> contextDataMap;
private boolean inFilter;
private Throwable throwable;
private Consumer<Throwable> onComplete;
private boolean weSuspended;
@Deprecated
public ContainerResponseContextImpl(HttpRequest request, HttpResponse httpResponse, BuiltResponse serverResponse)
{
this(request, httpResponse, serverResponse, null, new ContainerResponseFilter[]{}, t -> {}, null);
}
public ContainerResponseContextImpl(HttpRequest request, HttpResponse httpResponse, BuiltResponse serverResponse,
ResponseContainerRequestContext requestContext, ContainerResponseFilter[] responseFilters,
Consumer<Throwable> onComplete, RunnableWithIOException continuation)
{
this.request = request;
this.httpResponse = httpResponse;
this.jaxrsResponse = serverResponse;
this.requestContext = requestContext;
this.responseFilters = responseFilters;
this.continuation = continuation;
this.onComplete = onComplete;
contextDataMap = ResteasyProviderFactory.getContextDataMap();
}
public BuiltResponse getJaxrsResponse()
{
return jaxrsResponse;
}
public HttpResponse getHttpResponse()
{
return httpResponse;
}
@Override
public int getStatus()
{
return jaxrsResponse.getStatus();
}
@Override
public void setStatus(int code)
{
httpResponse.setStatus(code);
jaxrsResponse.setStatus(code);
}
@Override
public Response.StatusType getStatusInfo()
{
return jaxrsResponse.getStatusInfo();
}
@Override
public void setStatusInfo(Response.StatusType statusInfo)
{
httpResponse.setStatus(statusInfo.getStatusCode());
jaxrsResponse.setStatus(statusInfo.getStatusCode());
}
@Override
public Class<?> getEntityClass()
{
return jaxrsResponse.getEntityClass();
}
@Override
public Type getEntityType()
{
return jaxrsResponse.getGenericType();
}
@Override
public void setEntity(Object entity)
{
jaxrsResponse.setEntity(entity);
getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
}
@Override
public void setEntity(Object entity, Annotation[] annotations, MediaType mediaType)
{
jaxrsResponse.setEntity(entity);
jaxrsResponse.setAnnotations(annotations);
jaxrsResponse.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, mediaType);
getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
}
@Override
public MultivaluedMap<String, Object> ()
{
return jaxrsResponse.getMetadata();
}
@Override
public Set<String> getAllowedMethods()
{
return jaxrsResponse.getAllowedMethods();
}
@Override
public Date getDate()
{
return jaxrsResponse.getDate();
}
@Override
public Locale getLanguage()
{
return jaxrsResponse.getLanguage();
}
@Override
public int getLength()
{
return jaxrsResponse.getLength();
}
@Override
public MediaType getMediaType()
{
return jaxrsResponse.getMediaType();
}
@Override
public Map<String, NewCookie> getCookies()
{
return jaxrsResponse.getCookies();
}
@Override
public EntityTag getEntityTag()
{
return jaxrsResponse.getEntityTag();
}
@Override
public Date getLastModified()
{
return jaxrsResponse.getLastModified();
}
@Override
public URI getLocation()
{
return jaxrsResponse.getLocation();
}
@Override
public Set<Link> getLinks()
{
return jaxrsResponse.getLinks();
}
@Override
public boolean hasLink(String relation)
{
return jaxrsResponse.hasLink(relation);
}
@Override
public Link getLink(String relation)
{
return jaxrsResponse.getLink(relation);
}
@Override
public Link.Builder getLinkBuilder(String relation)
{
return jaxrsResponse.getLinkBuilder(relation);
}
@Override
public boolean hasEntity()
{
return jaxrsResponse.hasEntity();
}
@Override
public Object getEntity()
{
return jaxrsResponse.getEntity();
}
@Override
public OutputStream getEntityStream()
{
try
{
return httpResponse.getOutputStream();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
public void setEntityStream(OutputStream entityStream)
{
httpResponse.setOutputStream(entityStream);
}
@Override
public Annotation[] getEntityAnnotations()
{
return jaxrsResponse.getAnnotations();
}
@Override
public MultivaluedMap<String, String> ()
{
return jaxrsResponse.getStringHeaders();
}
@Override
public String (String name)
{
return jaxrsResponse.getHeaderString(name);
}
@Override
public synchronized void suspend() {
if(continuation == null)
throw new RuntimeException("Suspend not supported yet");
suspended = true;
}
@Override
public synchronized void resume() {
if(!suspended)
throw new RuntimeException("Cannot resume: not suspended");
if(inFilter)
{
suspended = false;
return;
}
ResteasyProviderFactory.pushContextDataMap(contextDataMap);
try {
filter();
}catch(Throwable t) {
writeException(t);
}
}
@Override
public synchronized void resume(Throwable t) {
if(!suspended)
throw new RuntimeException("Cannot resume: not suspended");
if(inFilter)
{
throwable = t;
suspended = false;
}
else
{
ResteasyProviderFactory.pushContextDataMap(contextDataMap);
writeException(t);
}
}
private void writeException(Throwable t)
{
HttpResponse httpResponse = (HttpResponse) contextDataMap.get(HttpResponse.class);
SynchronousDispatcher dispatcher = (SynchronousDispatcher) contextDataMap.get(Dispatcher.class);
ResteasyAsynchronousResponse asyncResponse = request.getAsyncContext().getAsyncResponse();
dispatcher.unhandledAsynchronousException(httpResponse, t);
onComplete.accept(t);
asyncResponse.complete();
asyncResponse.completionCallbacks(t);
}
public synchronized void filter() throws IOException
{
while(currentFilter < responseFilters.length)
{
ContainerResponseFilter filter = responseFilters[currentFilter++];
try
{
suspended = false;
throwable = null;
inFilter = true;
filter.filter(requestContext, this);
}
catch (IOException e)
{
throw new ApplicationException(e);
}
finally
{
inFilter = false;
}
if(suspended) {
if(!request.getAsyncContext().isSuspended())
{
request.getAsyncContext().suspend();
weSuspended = true;
}
filterReturnIsMeaningful = false;
return;
}
if (throwable != null)
{
if(filterReturnIsMeaningful)
SynchronousDispatcher.rethrow(throwable);
else
{
writeException(throwable);
return;
}
}
}
if(continuation == null)
return;
if(filterReturnIsMeaningful) {
continuation.run();
onComplete.accept(null);
return;
}
try
{
continuation.run();
onComplete.accept(null);
if(weSuspended)
{
HttpServletRequest httpServletRequest = (HttpServletRequest) contextDataMap.get(HttpServletRequest.class);
httpServletRequest.getAsyncContext().complete();
}
} catch (IOException e)
{
LogMessages.LOGGER.unknownException(request.getHttpMethod(), request.getUri().getPath(), e);
}
}
}