1.1 --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Thu Oct 15 14:01:49 2020 +0200 1.2 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Thu Oct 15 18:36:05 2020 +0200 1.3 @@ -84,7 +84,7 @@ 1.4 * The reason for this is the different handling of empty paths in 1.5 * {@link HttpServletRequest#getPathInfo()}. 1.6 */ 1.7 - private final Map<HttpMethod, Map<String, Method>> mappings = new HashMap<>(); 1.8 + private final Map<HttpMethod, Map<PathPattern, Method>> mappings = new HashMap<>(); 1.9 1.10 /** 1.11 * Returns the name of the resource bundle associated with this servlet. 1.12 @@ -108,7 +108,9 @@ 1.13 throw new AssertionError("Non-exhaustive if-else - this is a bug."); 1.14 } 1.15 1.16 - private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { 1.17 + private ResponseType invokeMapping(Map.Entry<PathPattern, Method> mapping, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { 1.18 + final var pathPattern = mapping.getKey(); 1.19 + final var method = mapping.getValue(); 1.20 try { 1.21 LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName()); 1.22 final var paramTypes = method.getParameterTypes(); 1.23 @@ -122,6 +124,9 @@ 1.24 if (paramTypes[i].isAssignableFrom(DataAccessObjects.class)) { 1.25 paramValues[i] = dao; 1.26 } 1.27 + if (paramTypes[i].isAssignableFrom(PathParameters.class)) { 1.28 + paramValues[i] = pathPattern.obtainPathParameters(sanitizeRequestPath(req)); 1.29 + } 1.30 } 1.31 return (ResponseType) method.invoke(this, paramValues); 1.32 } catch (InvocationTargetException ex) { 1.33 @@ -152,6 +157,13 @@ 1.34 for (Method method : methods) { 1.35 Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class)); 1.36 if (mapping.isPresent()) { 1.37 + if (mapping.get().requestPath().isBlank()) { 1.38 + LOG.warn("{} is annotated with {} but request path is empty", 1.39 + method.getName(), RequestMapping.class.getSimpleName() 1.40 + ); 1.41 + continue; 1.42 + } 1.43 + 1.44 if (!Modifier.isPublic(method.getModifiers())) { 1.45 LOG.warn("{} is annotated with {} but is not public", 1.46 method.getName(), RequestMapping.class.getSimpleName() 1.47 @@ -175,28 +187,35 @@ 1.48 for (var param : method.getParameterTypes()) { 1.49 paramsInjectible &= HttpServletRequest.class.isAssignableFrom(param) 1.50 || HttpServletResponse.class.isAssignableFrom(param) 1.51 + || PathParameters.class.isAssignableFrom(param) 1.52 || DataAccessObjects.class.isAssignableFrom(param); 1.53 } 1.54 if (paramsInjectible) { 1.55 - String requestPath = "/" + mapping.get().requestPath(); 1.56 + try { 1.57 + PathPattern pathPattern = new PathPattern(mapping.get().requestPath()); 1.58 1.59 - if (mappings 1.60 - .computeIfAbsent(mapping.get().method(), k -> new HashMap<>()) 1.61 - .putIfAbsent(requestPath, method) != null) { 1.62 - LOG.warn("{} {} has multiple mappings", 1.63 + if (mappings 1.64 + .computeIfAbsent(mapping.get().method(), k -> new HashMap<>()) 1.65 + .putIfAbsent(pathPattern, method) != null) { 1.66 + LOG.warn("{} {} has multiple mappings", 1.67 + mapping.get().method(), 1.68 + mapping.get().requestPath() 1.69 + ); 1.70 + } 1.71 + 1.72 + LOG.debug("{} {} maps to {}::{}", 1.73 mapping.get().method(), 1.74 - mapping.get().requestPath() 1.75 + mapping.get().requestPath(), 1.76 + getClass().getSimpleName(), 1.77 + method.getName() 1.78 + ); 1.79 + } catch (IllegalArgumentException ex) { 1.80 + LOG.warn("Request mapping for {} failed: path pattern '{}' is syntactically invalid", 1.81 + method.getName(), mapping.get().requestPath() 1.82 ); 1.83 } 1.84 - 1.85 - LOG.debug("{} {} maps to {}::{}", 1.86 - mapping.get().method(), 1.87 - requestPath, 1.88 - getClass().getSimpleName(), 1.89 - method.getName() 1.90 - ); 1.91 } else { 1.92 - LOG.warn("{} is annotated with {} but has the wrong parameters - only HttpServletRequest. HttpServletResponse, and DataAccessObjects are allowed", 1.93 + LOG.warn("{} is annotated with {} but has the wrong parameters - only HttpServletRequest, HttpServletResponse, PathParameters, and DataAccessObjects are allowed", 1.94 method.getName(), RequestMapping.class.getSimpleName() 1.95 ); 1.96 } 1.97 @@ -373,8 +392,12 @@ 1.98 return Optional.ofNullable(req.getPathInfo()).orElse("/"); 1.99 } 1.100 1.101 - private Optional<Method> findMapping(HttpMethod method, HttpServletRequest req) { 1.102 - return Optional.ofNullable(mappings.get(method)).map(rm -> rm.get(sanitizeRequestPath(req))); 1.103 + private Optional<Map.Entry<PathPattern, Method>> findMapping(HttpMethod method, HttpServletRequest req) { 1.104 + return Optional.ofNullable(mappings.get(method)).flatMap(rm -> 1.105 + rm.entrySet().stream().filter( 1.106 + kv -> kv.getKey().matches(sanitizeRequestPath(req)) 1.107 + ).findAny() 1.108 + ); 1.109 } 1.110 1.111 private void forwardAsSpecified(ResponseType type, HttpServletRequest req, HttpServletResponse resp)