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

changeset 12
005d27918b57
parent 11
737ab27e37b3
child 13
f4608ad6c947
     1.1 --- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sun Dec 17 01:45:28 2017 +0100
     1.2 +++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat Dec 23 17:28:19 2017 +0100
     1.3 @@ -34,7 +34,6 @@
     1.4  import java.util.HashMap;
     1.5  import java.util.Map;
     1.6  import java.util.Optional;
     1.7 -import java.util.function.BiConsumer;
     1.8  import javax.servlet.ServletException;
     1.9  import javax.servlet.http.HttpServlet;
    1.10  import javax.servlet.http.HttpServletRequest;
    1.11 @@ -60,10 +59,16 @@
    1.12       */
    1.13      private Optional<LightPITModule.ELProxy> moduleInfoELProxy = Optional.empty();
    1.14      
    1.15 +    
    1.16 +    @FunctionalInterface
    1.17 +    private static interface HandlerMethod {
    1.18 +        ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException, ServletException;
    1.19 +    }
    1.20 +    
    1.21      /**
    1.22       * Invocation mapping gathered from the {@link RequestMapping} annotations.
    1.23       */
    1.24 -    private final Map<HttpMethod, Map<String, BiConsumer<HttpServletRequest, HttpServletResponse>>> mappings = new HashMap<>();
    1.25 +    private final Map<HttpMethod, Map<String, HandlerMethod>> mappings = new HashMap<>();
    1.26  
    1.27      /**
    1.28       * Gives implementing modules access to the {@link ModuleManager}.
    1.29 @@ -73,12 +78,15 @@
    1.30          return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME);
    1.31      }
    1.32      
    1.33 -    private void invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) {
    1.34 +    private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp)
    1.35 +            throws IOException, ServletException {
    1.36          try {
    1.37              LOG.debug("invoke {}", method.getName());
    1.38 -            method.invoke(this, req, resp);
    1.39 -        } catch (ReflectiveOperationException ex) {
    1.40 +            return (ResponseType) method.invoke(this, req, resp);
    1.41 +        } catch (ReflectiveOperationException | ClassCastException ex) {
    1.42              LOG.error(String.format("invocation of method %s failed", method.getName()), ex);
    1.43 +            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    1.44 +            return ResponseType.NONE;
    1.45          }
    1.46      }
    1.47  
    1.48 @@ -88,6 +96,14 @@
    1.49          moduleInfoELProxy = moduleInfo.map(LightPITModule.ELProxy::convert);
    1.50          
    1.51          if (moduleInfo.isPresent()) {
    1.52 +            scanForRequestMappings();
    1.53 +        }
    1.54 +        
    1.55 +        LOG.trace("{} initialized", getServletName());
    1.56 +    }
    1.57 +
    1.58 +    private void scanForRequestMappings() {
    1.59 +        try {
    1.60              Method[] methods = getClass().getDeclaredMethods();
    1.61              for (Method method : methods) {
    1.62                  Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class));
    1.63 @@ -104,12 +120,18 @@
    1.64                          );
    1.65                          continue;
    1.66                      }
    1.67 -                    
    1.68 +                    if (!ResponseType.class.isAssignableFrom(method.getReturnType())) {
    1.69 +                        LOG.warn("{} is annotated with {} but has the wrong return type - 'ResponseType' required",
    1.70 +                                method.getName(), RequestMapping.class.getSimpleName()
    1.71 +                        );
    1.72 +                        continue;
    1.73 +                    }
    1.74 +
    1.75                      Class<?>[] params = method.getParameterTypes();
    1.76                      if (params.length == 2
    1.77                              && HttpServletRequest.class.isAssignableFrom(params[0])
    1.78                              && HttpServletResponse.class.isAssignableFrom(params[1])) {
    1.79 -                        
    1.80 +
    1.81                          if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
    1.82                                  putIfAbsent(mapping.get().requestPath(),
    1.83                                          (req, resp) -> invokeMapping(method, req, resp)) != null) {
    1.84 @@ -118,22 +140,22 @@
    1.85                                      mapping.get().requestPath()
    1.86                              );
    1.87                          }
    1.88 -                        
    1.89 +
    1.90                          LOG.info("{} {} maps to {}",
    1.91                                  mapping.get().method(),
    1.92                                  mapping.get().requestPath(),
    1.93                                  method.getName()
    1.94                          );
    1.95                      } else {
    1.96 -                        LOG.warn("{} is annotated with {} but has the wrong signature - (HttpServletRequest,HttpServletResponse) required",
    1.97 +                        LOG.warn("{} is annotated with {} but has the wrong parameters - (HttpServletRequest,HttpServletResponse) required",
    1.98                                  method.getName(), RequestMapping.class.getSimpleName()
    1.99                          );
   1.100                      }
   1.101                  }
   1.102              }
   1.103 +        } catch (SecurityException ex) {
   1.104 +            LOG.error("Scan for request mappings on declared methods failed.", ex);
   1.105          }
   1.106 -        
   1.107 -        LOG.trace("{} initialized", getServletName());
   1.108      }
   1.109  
   1.110      @Override
   1.111 @@ -167,29 +189,46 @@
   1.112          req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp);
   1.113      }
   1.114      
   1.115 -    private Optional<BiConsumer<HttpServletRequest, HttpServletResponse>> findMapping(HttpMethod method, HttpServletRequest req) {
   1.116 +    private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) {
   1.117          return Optional.ofNullable(mappings.get(method)).map(
   1.118                  (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse(""))
   1.119          );
   1.120      }
   1.121      
   1.122 +    private void forwardAsSepcified(ResponseType type, HttpServletRequest req, HttpServletResponse resp)
   1.123 +            throws ServletException, IOException {
   1.124 +        switch (type) {
   1.125 +            case NONE: return;
   1.126 +            case HTML_FULL:
   1.127 +                forwardToFullView(req, resp);
   1.128 +                return;
   1.129 +            // TODO: implement remaining response types
   1.130 +            default:
   1.131 +                // this code should be unreachable
   1.132 +                LOG.error("ResponseType switch is not exhaustive - this is a bug!");
   1.133 +                throw new UnsupportedOperationException();
   1.134 +        }
   1.135 +    }
   1.136 +    
   1.137 +    private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp)
   1.138 +            throws ServletException, IOException {
   1.139 +        Optional<HandlerMethod> mapping = findMapping(method, req);
   1.140 +        if (mapping.isPresent()) {
   1.141 +            forwardAsSepcified(mapping.get().apply(req, resp), req, resp);
   1.142 +        } else {
   1.143 +            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
   1.144 +        }
   1.145 +    }
   1.146 +    
   1.147      @Override
   1.148      protected final void doGet(HttpServletRequest req, HttpServletResponse resp)
   1.149              throws ServletException, IOException {
   1.150 -        
   1.151 -        findMapping(HttpMethod.GET, req).ifPresent((consumer) -> consumer.accept(req, resp));
   1.152 -        
   1.153 -        // TODO: let the invoked handler decide (signature must be changed from a BiConsumer to a BiFunction)
   1.154 -        // TODO: we should call a default handler, if no specific mapping could be found
   1.155 -        forwardToFullView(req, resp);
   1.156 +        doProcess(HttpMethod.GET, req, resp);
   1.157      }
   1.158  
   1.159      @Override
   1.160      protected final void doPost(HttpServletRequest req, HttpServletResponse resp)
   1.161              throws ServletException, IOException {
   1.162 -        
   1.163 -        findMapping(HttpMethod.POST, req).ifPresent((consumer) -> consumer.accept(req, resp));
   1.164 -        
   1.165 -        forwardToFullView(req, resp);
   1.166 +        doProcess(HttpMethod.POST, req, resp);
   1.167      }
   1.168  }

mercurial