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

changeset 130
7ef369744fd1
parent 109
2e0669e814ff
child 131
67df332e3146
     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)

mercurial