
io.muserver.rest.JaxRSRequest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mu-server Show documentation
Show all versions of mu-server Show documentation
A simple but powerful web server framework
The newest version!
package io.muserver.rest;
import io.muserver.*;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.ClientErrorException;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ResourceInfo;
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.*;
import jakarta.ws.rs.ext.MessageBodyReader;
import jakarta.ws.rs.ext.ReaderInterceptor;
import jakarta.ws.rs.ext.ReaderInterceptorContext;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.*;
class JaxRSRequest implements Request, ContainerRequestContext, ReaderInterceptorContext {
final MuRequest muRequest;
private final MuResponse muResponse;
private InputStream inputStream;
private final String relativePath;
private final JaxRsHttpHeadersAdapter jaxHeaders;
private UriInfo uriInfo;
private RequestMatcher.MatchedMethod matchedMethod;
private SecurityContext securityContext;
private Annotation[] annotations = JaxRSResponse.Builder.EMPTY_ANNOTATIONS;
private Class> type;
private Type genericType;
private int nextReader;
private final List readerInterceptors;
private final EntityProviders entityProviders;
private String httpMethod;
JaxRSRequest(MuRequest muRequest, MuResponse muResponse, InputStream inputStream, String relativePath, SecurityContext securityContext, List readerInterceptors, EntityProviders entityProviders) {
this.muRequest = muRequest;
this.httpMethod = muRequest.method().name();
this.muResponse = muResponse;
this.inputStream = inputStream;
this.relativePath = relativePath;
this.securityContext = securityContext;
this.readerInterceptors = readerInterceptors;
this.entityProviders = entityProviders;
String basePath = muRequest.contextPath();
if (!basePath.endsWith("/")) {
basePath += "/";
}
this.uriInfo = RestHandler.createUriInfo(relativePath, null, muRequest.uri().resolve(basePath), muRequest.uri());
this.jaxHeaders = new JaxRsHttpHeadersAdapter(muRequest.headers(), muRequest.cookies());
}
boolean methodHasAnnotations(List> toCheck) {
if (matchedMethod == null) {
return false;
}
return matchedMethod.resourceMethod.hasAll(toCheck);
}
@Override
public Object getProperty(String name) {
if (MuRuntimeDelegate.MU_REQUEST_PROPERTY.equals(name)) {
return muRequest;
} else if (MuRuntimeDelegate.RESOURCE_INFO_PROPERTY.equals(name)) {
Class> resourceInstanceClass = (matchedMethod == null) ? null : matchedMethod.matchedClass.resourceClass.resourceInstance.getClass();
java.lang.reflect.Method resourceInstanceMethod = (matchedMethod == null) ? null : matchedMethod.resourceMethod.methodHandle;
return new MuResourceInfo(resourceInstanceClass, resourceInstanceMethod);
}
return muRequest.attribute(name);
}
@Override
public Collection getPropertyNames() {
Set copy = new HashSet<>(muRequest.attributes().keySet());
copy.add(MuRuntimeDelegate.MU_REQUEST_PROPERTY);
copy.add(MuRuntimeDelegate.RESOURCE_INFO_PROPERTY);
return Collections.unmodifiableSet(copy);
}
@Override
public void setProperty(String name, Object object) {
muRequest.attribute(name, object);
}
@Override
public void removeProperty(String name) {
muRequest.attribute(name, null);
}
@Override
public Annotation[] getAnnotations() {
return annotations;
}
@Override
public void setAnnotations(Annotation[] annotations) {
if (annotations == null) {
throw new NullPointerException("The 'annotations' parameter must not be null");
}
this.annotations = annotations;
}
@Override
public Class> getType() {
return type;
}
@Override
public void setType(Class> type) {
this.type = type;
}
@Override
public Type getGenericType() {
return genericType;
}
@Override
public void setGenericType(Type genericType) {
this.genericType = genericType;
}
@Override
public UriInfo getUriInfo() {
return uriInfo;
}
@Override
public void setRequestUri(URI requestUri) {
setRequestUri(uriInfo.getBaseUri(), requestUri);
}
@Override
public void setRequestUri(URI baseUri, URI requestUri) {
if (matchedMethod != null) {
throw new IllegalStateException("This method is only valid for @PreMatching filters");
}
URI absoluteUri = baseUri.resolve(requestUri);
this.uriInfo = RestHandler.createUriInfo(baseUri.relativize(requestUri).getRawPath(), null, baseUri, absoluteUri);
}
@Override
public Request getRequest() {
return this;
}
@Override
public String getMethod() {
return httpMethod;
}
@Override
public Variant selectVariant(List variants) {
for (Variant variant : variants) {
if (variant.getMediaType() != null) {
if (!muResponse.headers().contains(HeaderNames.VARY, HeaderNames.ACCEPT, true)) {
muResponse.headers().add(HeaderNames.VARY, HeaderNames.ACCEPT);
}
}
if (variant.getEncoding() != null) {
if (!muResponse.headers().contains(HeaderNames.VARY, HeaderNames.ACCEPT_ENCODING, true)) {
muResponse.headers().add(HeaderNames.VARY, HeaderNames.ACCEPT_ENCODING);
}
}
if (variant.getLanguage() != null) {
if (!muResponse.headers().contains(HeaderNames.VARY, HeaderNames.ACCEPT_LANGUAGE, true)) {
muResponse.headers().add(HeaderNames.VARY, HeaderNames.ACCEPT_LANGUAGE);
}
}
}
String acceptLang = jaxHeaders.getHeaderString("accept-language");
List ranges = Locale.LanguageRange.parse(acceptLang == null ? "*" : acceptLang);
return MuVariantListBuilder.selectVariant(variants, ranges, getAcceptableMediaTypes(), muRequest.headers().acceptEncoding());
}
@Override
public Response.ResponseBuilder evaluatePreconditions(EntityTag eTag) {
if (eTag == null) {
throw new IllegalArgumentException("eTag is null");
}
Response.ResponseBuilder ifMatchBuilder = evaluateIfMatch(eTag);
return ifMatchBuilder != null ? ifMatchBuilder : evaluateIfNoneMatch(eTag);
}
private Response.ResponseBuilder evaluateIfMatch(EntityTag eTag) {
boolean anyMatch = false;
List ifMatches = muRequest.headers().getAll(HeaderNames.IF_MATCH);
if (ifMatches.isEmpty()) {
return null;
}
if (eTag.isWeak()) {
return Response.status(412).entity(new ClientErrorException("Precondition failed: if-match failed due to weak eTag", 412));
}
for (String suppliedEtag : ifMatches) {
EntityTag supplied = EntityTag.valueOf(suppliedEtag);
if (supplied.equals(eTag)) {
anyMatch = true;
break;
}
}
if (anyMatch) {
return null;
}
return Response.status(412).entity(new ClientErrorException("Precondition failed: if-match", 412));
}
private Response.ResponseBuilder evaluateIfNoneMatch(EntityTag eTag) {
List ifNoneMatchTags = muRequest.headers().getAll(HeaderNames.IF_NONE_MATCH);
boolean getOrHead = isGetOrHead();
if (!getOrHead && eTag.isWeak()) {
return Response.status(412).entity(new ClientErrorException("Precondition failed: if-match failed due to weak eTag", 412));
}
boolean noneMatch = true;
for (String suppliedEtag : ifNoneMatchTags) {
EntityTag supplied = EntityTag.valueOf(suppliedEtag);
if (supplied.equals(eTag) || (getOrHead && supplied.getValue().equals(eTag.getValue()))) {
noneMatch = false;
break;
}
}
if (noneMatch) {
return null;
}
return getOrHead ? Response.status(304).tag(eTag) : Response.status(412).entity(new ClientErrorException("Precondition failed: if-match", 412));
}
private boolean isGetOrHead() {
return muRequest.method() == Method.GET || muRequest.method() == Method.HEAD;
}
@Override
public Response.ResponseBuilder evaluatePreconditions(Date lastModified) {
if (lastModified == null) {
throw new IllegalArgumentException("lastModified is null");
}
Response.ResponseBuilder ifUnmodifiedSince = evaluateIfUnmodifiedSince(lastModified);
return ifUnmodifiedSince != null ? ifUnmodifiedSince : evaluateIfModifiedSince(lastModified);
}
private Response.ResponseBuilder evaluateIfModifiedSince(Date lastModified) {
long lastModifiedSeconds = lastModified.getTime() / 1000;
Long ifModifiedMillis = muRequest.headers().getTimeMillis(HeaderNames.IF_MODIFIED_SINCE);
if (ifModifiedMillis == null || lastModifiedSeconds > (ifModifiedMillis / 1000)) {
return null;
} else {
return isGetOrHead() ? Response.notModified() : null;
}
}
private Response.ResponseBuilder evaluateIfUnmodifiedSince(Date lastModified) {
long lastModifiedSeconds = lastModified.getTime() / 1000;
Long ifUnmodifiedSince = muRequest.headers().getTimeMillis(HeaderNames.IF_UNMODIFIED_SINCE);
if (ifUnmodifiedSince == null || lastModifiedSeconds <= (ifUnmodifiedSince / 1000)) {
return null;
} else {
return Response.status(Response.Status.PRECONDITION_FAILED)
.entity(new ClientErrorException("Failed precondition: if-unmodified-since", 412));
}
}
@Override
public Response.ResponseBuilder evaluatePreconditions(Date lastModified, EntityTag eTag) {
if (lastModified == null) {
throw new IllegalArgumentException("lastModified is null");
}
if (eTag == null) {
throw new IllegalArgumentException("eTag is null");
}
Response.ResponseBuilder builder = evaluateIfMatch(eTag);
if (builder != null) {
return builder;
}
builder = evaluateIfUnmodifiedSince(lastModified);
if (builder != null) {
return builder;
}
builder = evaluateIfNoneMatch(eTag);
if (builder != null) {
return builder;
}
builder = evaluateIfModifiedSince(lastModified);
if (builder != null) {
builder.tag(eTag);
}
return builder;
}
@Override
public Response.ResponseBuilder evaluatePreconditions() {
return muRequest.headers().get(HeaderNames.IF_MATCH) == null ? null
: Response.status(Response.Status.PRECONDITION_FAILED).entity(new ClientErrorException("Precondition failed: if-match", 412));
}
Method getMuMethod() {
return Method.valueOf(getMethod());
}
@Override
public void setMethod(String method) {
if (matchedMethod != null) {
throw new IllegalStateException("This method is only valid for @PreMatching filters");
}
Mutils.notNull("method", method);
String upper = method.toUpperCase();
this.httpMethod = Method.valueOf(upper).name();
}
Object executeInterceptors() throws IOException {
this.nextReader = 0;
return proceed();
}
@Override
public Object proceed() throws IOException, WebApplicationException {
if (nextReader < readerInterceptors.size()) {
nextReader++;
ReaderInterceptor nextInterceptor = readerInterceptors.get(nextReader - 1);
List> filterBindings = ResourceClass.getNameBindingAnnotations(nextInterceptor.getClass());
if (methodHasAnnotations(filterBindings)) {
return nextInterceptor.aroundReadFrom(this);
}
}
// read the entity
// Section 4.2.1 - determine message body reader
// 1. Obtain the media type of the request. If the request does not contain a Content-Type header then use application/octet-stream
MediaType requestBodyMediaType = getMediaType();
if (requestBodyMediaType == null) {
requestBodyMediaType = MediaType.APPLICATION_OCTET_STREAM_TYPE;
}
// 2. Identify the Java type of the parameter whose value will be mapped from the entity body.
Class> type = getType();
Type genericType = getGenericType();
Annotation[] annotations = getAnnotations();
// 3 & 4: Select a reader that supports the media type of the request and isReadable
MessageBodyReader messageBodyReader = entityProviders.selectReader(type, genericType, annotations, requestBodyMediaType);
try {
return messageBodyReader.readFrom(type, genericType, annotations, requestBodyMediaType, getHeaders(), getInputStream());
} catch (NoContentException nce) {
throw new BadRequestException("No request body was sent", nce);
}
}
@Override
public InputStream getInputStream() {
return getEntityStream();
}
@Override
public void setInputStream(InputStream is) {
setEntityStream(is);
}
@Override
public MultivaluedMap getHeaders() {
return jaxHeaders.getMutableRequestHeaders();
}
@Override
public String getHeaderString(String name) {
return jaxHeaders.getHeaderString(name);
}
@Override
public Date getDate() {
return jaxHeaders.getDate();
}
@Override
public Locale getLanguage() {
return jaxHeaders.getLanguage();
}
@Override
public int getLength() {
return jaxHeaders.getLength();
}
@Override
public MediaType getMediaType() {
return jaxHeaders.getMediaType();
}
@Override
public void setMediaType(MediaType mediaType) {
if (mediaType == null) {
jaxHeaders.getMutableRequestHeaders().remove("content-type");
} else {
jaxHeaders.getMutableRequestHeaders().putSingle("content-type", MediaTypeParser.toString(mediaType));
}
}
@Override
public List getAcceptableMediaTypes() {
return jaxHeaders.getAcceptableMediaTypes();
}
@Override
public List getAcceptableLanguages() {
return jaxHeaders.getAcceptableLanguages();
}
@Override
public Map getCookies() {
return jaxHeaders.getCookies();
}
@Override
public boolean hasEntity() {
return muRequest.headers().hasBody();
}
@Override
public InputStream getEntityStream() {
return inputStream;
}
@Override
public void setEntityStream(InputStream input) {
this.inputStream = input;
}
@Override
public SecurityContext getSecurityContext() {
return securityContext;
}
@Override
public void setSecurityContext(SecurityContext context) {
this.securityContext = context;
}
@Override
public void abortWith(Response response) {
throw new FilterAbortedException(response);
}
void setMatchedMethod(RequestMatcher.MatchedMethod matchedMethod) {
this.matchedMethod = matchedMethod;
this.uriInfo = RestHandler.createUriInfo(relativePath, matchedMethod, uriInfo.getBaseUri(), uriInfo.getRequestUri());
}
@Override
public String toString() {
return getMethod() + " " + uriInfo;
}
String relativePath() {
return getUriInfo().getPath(false);
}
/**
* This special exception doesn't get mapped by an exception mapper, so that the {@link #abortWith(Response)} method
* can just return the Response passed to it without mapping it.
*/
static class FilterAbortedException extends WebApplicationException {
public FilterAbortedException(Response response) {
super(response);
}
}
private static class MuResourceInfo implements ResourceInfo {
private final Class> clazz;
private final java.lang.reflect.Method method;
private MuResourceInfo(Class> clazz, java.lang.reflect.Method method) {
this.clazz = clazz;
this.method = method;
}
@Override
public java.lang.reflect.Method getResourceMethod() {
return method;
}
@Override
public Class> getResourceClass() {
return clazz;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy