Skip to content

Commit

Permalink
#2 : Context-annotation should support javax.ws.rs.core.HttpHeaders
Browse files Browse the repository at this point in the history
  • Loading branch information
Gmugra committed Feb 11, 2021
1 parent e124e97 commit 94039f6
Show file tree
Hide file tree
Showing 24 changed files with 899 additions and 99 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,12 @@ public class Application {
public static void main(String... args) {

ComponentProvider myComponentProvider = new MyComponentProvider(...);
Collection<Class<?>> entryPoints = ...
Collection<Class<?>> resources = ...

RoutingConfig config =
RoutingConfig.builder(myComponentProvider)
.addResource(MyEntryPoint.class)
.addResource(entryPoints)
.addResource(MyResource.class)
.addResource(resources)
.addBodyWriter(new SimpleGsonBodyWriter<>())
.addBodyReader(new SimpleGsonBodyReader<>())
.addBodyWriter(new SimpleThymeleafBodyWriter("/thymeleaf/"))
Expand Down Expand Up @@ -138,6 +138,7 @@ The type of the annotated parameter must either:
* javax.servlet.http.HttpServletResponse
* javax.servlet.ServletContext
* javax.ws.rs.core.SecurityContext
* javax.ws.rs.core.HttpHeaders;
1. `javax.ws.rs.core.Response`

### Extensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import net.cactusthorn.routing.invoke.MethodInvoker.ReturnObjectInfo;
import net.cactusthorn.routing.resource.ResourceScanner;
import net.cactusthorn.routing.resource.ResourceScanner.Resource;
import net.cactusthorn.routing.util.Headers;
import net.cactusthorn.routing.util.Http;
import net.cactusthorn.routing.PathTemplate.PathValues;

Expand Down Expand Up @@ -112,7 +113,7 @@ private void process(HttpServletRequest req, HttpServletResponse resp, List<Reso
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Not Found");
return;
}
List<MediaType> accept = Http.parseAccept(req.getHeaders(HttpHeaders.ACCEPT));
List<MediaType> accept = Headers.parseAccept(req.getHeader(HttpHeaders.ACCEPT));
boolean matchContentTypeFail = false;
boolean matchAcceptFail = false;
for (Resource resource : resources) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package net.cactusthorn.routing.delegate;

import java.util.Locale;

import javax.ws.rs.ext.RuntimeDelegate.HeaderDelegate;

import net.cactusthorn.routing.util.Headers;
import net.cactusthorn.routing.util.Language;

public class LanguageHeaderDelegate implements HeaderDelegate<Language> {

@Override //
public Language fromString(String languageTag) {
if (languageTag == null) {
throw new IllegalArgumentException("languageTag can not be null");
}
String[] parts = languageTag.split(";");
String localeStr = parts[0].trim();
Locale locale = null;
if ("*".equals(localeStr)) {
locale = Locale.forLanguageTag(localeStr);
} else {
locale = new Locale.Builder().setLanguageTag(localeStr).build();
}
String q = null;
if (parts.length > 1) {
String[] subParts = Headers.getSubParts(parts[1]);
if ("q".equals(subParts[0])) {
q = subParts[1];
}
}
return new Language(locale, q);
}

@Override //
public String toString(Language language) {
if (language == null) {
throw new IllegalArgumentException("language can not be null");
}
return language.getLocale().toLanguageTag() + ";q=" + language.getQ();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import javax.ws.rs.core.Variant.VariantListBuilder;
import javax.ws.rs.ext.RuntimeDelegate;

import net.cactusthorn.routing.util.Language;

public class RuntimeDelegateImpl extends RuntimeDelegate {

private final Map<Class<?>, HeaderDelegate<?>> headerDelegates = new HashMap<>();
Expand All @@ -32,6 +34,7 @@ public RuntimeDelegateImpl() {
headerDelegates.put(NewCookie.class, new NewCookieHeaderDelegate());
headerDelegates.put(CacheControl.class, new CacheControlHeaderDelegate());
headerDelegates.put(Cookie.class, new CookieHeaderDelegate());
headerDelegates.put(Language.class, new LanguageHeaderDelegate());
}

@Override //
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package net.cactusthorn.routing.invoke;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringJoiner;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.RuntimeDelegate;

import net.cactusthorn.routing.PathTemplate.PathValues;
import net.cactusthorn.routing.util.CaseInsensitiveMultivaluedMap;
import net.cactusthorn.routing.util.Headers;
import net.cactusthorn.routing.util.UnmodifiableMultivaluedMap;

public class HttpHeadersParameter extends MethodParameter {

public HttpHeadersParameter(Method method, Parameter parameter, Type genericType, int position) {
super(method, parameter, genericType, position);
}

@Override //
public HttpHeaders findValue(HttpServletRequest req, HttpServletResponse res, ServletContext con, PathValues pathValues)
throws Exception {
return new HttpHeadersImpl(req);
}

static final class HttpHeadersImpl implements HttpHeaders {

private static final MultivaluedMap<String, String> EMPTY = new UnmodifiableMultivaluedMap<>(new MultivaluedHashMap<>());

private MultivaluedMap<String, String> map;

private Locale locale;
private MediaType mediaType;
private Date date;
private List<MediaType> accept;
private Map<String, Cookie> cookies;
private List<Locale> acceptLanguage;

HttpHeadersImpl(HttpServletRequest req) {
Enumeration<String> headerNames = req.getHeaderNames();
if (headerNames == null || !headerNames.hasMoreElements()) {
map = EMPTY;
} else {
MultivaluedMap<String, String> headers = new CaseInsensitiveMultivaluedMap<String>();
for (Enumeration<String> names = headerNames; names.hasMoreElements();) {
String name = names.nextElement();
headers.addAll(name, Collections.list(req.getHeaders(name)));
}
map = new UnmodifiableMultivaluedMap<>(headers);
}
locale = parseLocale();
mediaType = parseMediaType();
date = parseDate();
accept = Headers.parseAccept(map.getFirst(ACCEPT));
cookies = parseCookies();
acceptLanguage = Headers.parseAcceptLanguage(map.getFirst(ACCEPT_LANGUAGE));
}

@Override public List<String> getRequestHeader(String name) {
return map.get(name);
}

@Override public String getHeaderString(String name) {
List<String> values = map.get(name);
if (values == null) {
return null;
}
StringJoiner joiner = new StringJoiner(",");
for (String value : values) {
joiner.add(value);
}
return joiner.toString();
}

@Override public MultivaluedMap<String, String> getRequestHeaders() {
return map;
}

@Override public List<MediaType> getAcceptableMediaTypes() {
return accept;
}

@Override public List<Locale> getAcceptableLanguages() {
return acceptLanguage;
}

@Override public MediaType getMediaType() {
return mediaType;
}

@Override public Locale getLanguage() {
return locale;
}

@Override public Map<String, Cookie> getCookies() {
return cookies;
}

@Override public Date getDate() {
return date;
}

@Override public int getLength() {
String header = map.getFirst(CONTENT_LENGTH);
if (header == null) {
return -1;
}
return Integer.parseInt(header);
}

private Locale parseLocale() {
String header = map.getFirst(CONTENT_LANGUAGE);
if (header == null) {
return null;
}
return RuntimeDelegate.getInstance().createHeaderDelegate(Locale.class).fromString(header);
}

private MediaType parseMediaType() {
String header = map.getFirst(CONTENT_TYPE);
if (header == null) {
return null;
}
return MediaType.valueOf(header);
}

private Date parseDate() {
String header = map.getFirst(DATE);
if (header == null) {
return null;
}
return RuntimeDelegate.getInstance().createHeaderDelegate(Date.class).fromString(header);
}

private Map<String, Cookie> parseCookies() {
String header = map.getFirst(COOKIE);
if (header == null) {
return Collections.emptyMap();
}
Map<String, Cookie> result = new HashMap<>();
List<Cookie> list = Headers.parseCookies(header);
for (Cookie cookie : list) {
result.putIfAbsent(cookie.getName(), cookie);
}
return Collections.unmodifiableMap(result);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import javax.ws.rs.PUT;
import javax.ws.rs.PATCH;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;

import net.cactusthorn.routing.RoutingConfig;
import net.cactusthorn.routing.RoutingInitializationException;
Expand Down Expand Up @@ -75,6 +76,9 @@ static MethodParameter create(Method method, Parameter parameter, Type genericTy
if (SecurityContext.class == parameter.getType()) {
return new SecurityContextParameter(method, parameter, genericType, position);
}
if (HttpHeaders.class == parameter.getType()) {
return new HttpHeadersParameter(method, parameter, genericType, position);
}
throw new RoutingInitializationException(CONTEXT_NOT_SUPPORTED, parameter.getType(), method);
}
if (method.getAnnotation(POST.class) != null || method.getAnnotation(PUT.class) != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import net.cactusthorn.routing.util.Http;
import net.cactusthorn.routing.util.Headers;

public final class ProducesParser {

Expand Down Expand Up @@ -38,7 +38,7 @@ private List<MediaType> parseProduces(String[] consumes) {
mediaTypes.add(new MediaType(parts[0], parts[1]));
}
}
Collections.sort(mediaTypes, Http.ACCEPT_COMPARATOR);
Collections.sort(mediaTypes, Headers.ACCEPT_COMPARATOR);
return mediaTypes;
}
}
Loading

0 comments on commit 94039f6

Please sign in to comment.