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

changeset 12
005d27918b57
parent 11
737ab27e37b3
child 13
f4608ad6c947
--- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sun Dec 17 01:45:28 2017 +0100
+++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat Dec 23 17:28:19 2017 +0100
@@ -34,7 +34,6 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
-import java.util.function.BiConsumer;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -60,10 +59,16 @@
      */
     private Optional<LightPITModule.ELProxy> moduleInfoELProxy = Optional.empty();
     
+    
+    @FunctionalInterface
+    private static interface HandlerMethod {
+        ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException, ServletException;
+    }
+    
     /**
      * Invocation mapping gathered from the {@link RequestMapping} annotations.
      */
-    private final Map<HttpMethod, Map<String, BiConsumer<HttpServletRequest, HttpServletResponse>>> mappings = new HashMap<>();
+    private final Map<HttpMethod, Map<String, HandlerMethod>> mappings = new HashMap<>();
 
     /**
      * Gives implementing modules access to the {@link ModuleManager}.
@@ -73,12 +78,15 @@
         return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME);
     }
     
-    private void invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) {
+    private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp)
+            throws IOException, ServletException {
         try {
             LOG.debug("invoke {}", method.getName());
-            method.invoke(this, req, resp);
-        } catch (ReflectiveOperationException ex) {
+            return (ResponseType) method.invoke(this, req, resp);
+        } catch (ReflectiveOperationException | ClassCastException ex) {
             LOG.error(String.format("invocation of method %s failed", method.getName()), ex);
+            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            return ResponseType.NONE;
         }
     }
 
@@ -88,6 +96,14 @@
         moduleInfoELProxy = moduleInfo.map(LightPITModule.ELProxy::convert);
         
         if (moduleInfo.isPresent()) {
+            scanForRequestMappings();
+        }
+        
+        LOG.trace("{} initialized", getServletName());
+    }
+
+    private void scanForRequestMappings() {
+        try {
             Method[] methods = getClass().getDeclaredMethods();
             for (Method method : methods) {
                 Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class));
@@ -104,12 +120,18 @@
                         );
                         continue;
                     }
-                    
+                    if (!ResponseType.class.isAssignableFrom(method.getReturnType())) {
+                        LOG.warn("{} is annotated with {} but has the wrong return type - 'ResponseType' required",
+                                method.getName(), RequestMapping.class.getSimpleName()
+                        );
+                        continue;
+                    }
+
                     Class<?>[] params = method.getParameterTypes();
                     if (params.length == 2
                             && HttpServletRequest.class.isAssignableFrom(params[0])
                             && HttpServletResponse.class.isAssignableFrom(params[1])) {
-                        
+
                         if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
                                 putIfAbsent(mapping.get().requestPath(),
                                         (req, resp) -> invokeMapping(method, req, resp)) != null) {
@@ -118,22 +140,22 @@
                                     mapping.get().requestPath()
                             );
                         }
-                        
+
                         LOG.info("{} {} maps to {}",
                                 mapping.get().method(),
                                 mapping.get().requestPath(),
                                 method.getName()
                         );
                     } else {
-                        LOG.warn("{} is annotated with {} but has the wrong signature - (HttpServletRequest,HttpServletResponse) required",
+                        LOG.warn("{} is annotated with {} but has the wrong parameters - (HttpServletRequest,HttpServletResponse) required",
                                 method.getName(), RequestMapping.class.getSimpleName()
                         );
                     }
                 }
             }
+        } catch (SecurityException ex) {
+            LOG.error("Scan for request mappings on declared methods failed.", ex);
         }
-        
-        LOG.trace("{} initialized", getServletName());
     }
 
     @Override
@@ -167,29 +189,46 @@
         req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp);
     }
     
-    private Optional<BiConsumer<HttpServletRequest, HttpServletResponse>> findMapping(HttpMethod method, HttpServletRequest req) {
+    private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) {
         return Optional.ofNullable(mappings.get(method)).map(
                 (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse(""))
         );
     }
     
+    private void forwardAsSepcified(ResponseType type, HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        switch (type) {
+            case NONE: return;
+            case HTML_FULL:
+                forwardToFullView(req, resp);
+                return;
+            // TODO: implement remaining response types
+            default:
+                // this code should be unreachable
+                LOG.error("ResponseType switch is not exhaustive - this is a bug!");
+                throw new UnsupportedOperationException();
+        }
+    }
+    
+    private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        Optional<HandlerMethod> mapping = findMapping(method, req);
+        if (mapping.isPresent()) {
+            forwardAsSepcified(mapping.get().apply(req, resp), req, resp);
+        } else {
+            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
+        }
+    }
+    
     @Override
     protected final void doGet(HttpServletRequest req, HttpServletResponse resp)
             throws ServletException, IOException {
-        
-        findMapping(HttpMethod.GET, req).ifPresent((consumer) -> consumer.accept(req, resp));
-        
-        // TODO: let the invoked handler decide (signature must be changed from a BiConsumer to a BiFunction)
-        // TODO: we should call a default handler, if no specific mapping could be found
-        forwardToFullView(req, resp);
+        doProcess(HttpMethod.GET, req, resp);
     }
 
     @Override
     protected final void doPost(HttpServletRequest req, HttpServletResponse resp)
             throws ServletException, IOException {
-        
-        findMapping(HttpMethod.POST, req).ifPresent((consumer) -> consumer.accept(req, resp));
-        
-        forwardToFullView(req, resp);
+        doProcess(HttpMethod.POST, req, resp);
     }
 }

mercurial