src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java

changeset 130
7ef369744fd1
parent 109
2e0669e814ff
child 131
67df332e3146
--- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Thu Oct 15 14:01:49 2020 +0200
+++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Thu Oct 15 18:36:05 2020 +0200
@@ -84,7 +84,7 @@
      * The reason for this is the different handling of empty paths in
      * {@link HttpServletRequest#getPathInfo()}.
      */
-    private final Map<HttpMethod, Map<String, Method>> mappings = new HashMap<>();
+    private final Map<HttpMethod, Map<PathPattern, Method>> mappings = new HashMap<>();
 
     /**
      * Returns the name of the resource bundle associated with this servlet.
@@ -108,7 +108,9 @@
         throw new AssertionError("Non-exhaustive if-else - this is a bug.");
     }
 
-    private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
+    private ResponseType invokeMapping(Map.Entry<PathPattern, Method> mapping, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
+        final var pathPattern = mapping.getKey();
+        final var method = mapping.getValue();
         try {
             LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName());
             final var paramTypes = method.getParameterTypes();
@@ -122,6 +124,9 @@
                 if (paramTypes[i].isAssignableFrom(DataAccessObjects.class)) {
                     paramValues[i] = dao;
                 }
+                if (paramTypes[i].isAssignableFrom(PathParameters.class)) {
+                    paramValues[i] = pathPattern.obtainPathParameters(sanitizeRequestPath(req));
+                }
             }
             return (ResponseType) method.invoke(this, paramValues);
         } catch (InvocationTargetException ex) {
@@ -152,6 +157,13 @@
             for (Method method : methods) {
                 Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class));
                 if (mapping.isPresent()) {
+                    if (mapping.get().requestPath().isBlank()) {
+                        LOG.warn("{} is annotated with {} but request path is empty",
+                                method.getName(), RequestMapping.class.getSimpleName()
+                        );
+                        continue;
+                    }
+
                     if (!Modifier.isPublic(method.getModifiers())) {
                         LOG.warn("{} is annotated with {} but is not public",
                                 method.getName(), RequestMapping.class.getSimpleName()
@@ -175,28 +187,35 @@
                     for (var param : method.getParameterTypes()) {
                         paramsInjectible &= HttpServletRequest.class.isAssignableFrom(param)
                                 || HttpServletResponse.class.isAssignableFrom(param)
+                                || PathParameters.class.isAssignableFrom(param)
                                 || DataAccessObjects.class.isAssignableFrom(param);
                     }
                     if (paramsInjectible) {
-                        String requestPath = "/" + mapping.get().requestPath();
+                        try {
+                            PathPattern pathPattern = new PathPattern(mapping.get().requestPath());
 
-                        if (mappings
-                                .computeIfAbsent(mapping.get().method(), k -> new HashMap<>())
-                                .putIfAbsent(requestPath, method) != null) {
-                            LOG.warn("{} {} has multiple mappings",
+                            if (mappings
+                                    .computeIfAbsent(mapping.get().method(), k -> new HashMap<>())
+                                    .putIfAbsent(pathPattern, method) != null) {
+                                LOG.warn("{} {} has multiple mappings",
+                                        mapping.get().method(),
+                                        mapping.get().requestPath()
+                                );
+                            }
+
+                            LOG.debug("{} {} maps to {}::{}",
                                     mapping.get().method(),
-                                    mapping.get().requestPath()
+                                    mapping.get().requestPath(),
+                                    getClass().getSimpleName(),
+                                    method.getName()
+                            );
+                        } catch (IllegalArgumentException ex) {
+                            LOG.warn("Request mapping for {} failed: path pattern '{}' is syntactically invalid",
+                                    method.getName(), mapping.get().requestPath()
                             );
                         }
-
-                        LOG.debug("{} {} maps to {}::{}",
-                                mapping.get().method(),
-                                requestPath,
-                                getClass().getSimpleName(),
-                                method.getName()
-                        );
                     } else {
-                        LOG.warn("{} is annotated with {} but has the wrong parameters - only HttpServletRequest. HttpServletResponse, and DataAccessObjects are allowed",
+                        LOG.warn("{} is annotated with {} but has the wrong parameters - only HttpServletRequest, HttpServletResponse, PathParameters, and DataAccessObjects are allowed",
                                 method.getName(), RequestMapping.class.getSimpleName()
                         );
                     }
@@ -373,8 +392,12 @@
         return Optional.ofNullable(req.getPathInfo()).orElse("/");
     }
 
-    private Optional<Method> findMapping(HttpMethod method, HttpServletRequest req) {
-        return Optional.ofNullable(mappings.get(method)).map(rm -> rm.get(sanitizeRequestPath(req)));
+    private Optional<Map.Entry<PathPattern, Method>> findMapping(HttpMethod method, HttpServletRequest req) {
+        return Optional.ofNullable(mappings.get(method)).flatMap(rm ->
+                rm.entrySet().stream().filter(
+                        kv -> kv.getKey().matches(sanitizeRequestPath(req))
+                ).findAny()
+        );
     }
 
     private void forwardAsSpecified(ResponseType type, HttpServletRequest req, HttpServletResponse resp)

mercurial