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

changeset 11
737ab27e37b3
parent 10
89e3e6e28b69
child 12
005d27918b57
     1.1 --- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sat Dec 16 20:19:28 2017 +0100
     1.2 +++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java	Sun Dec 17 01:45:28 2017 +0100
     1.3 @@ -29,6 +29,12 @@
     1.4  package de.uapcore.lightpit;
     1.5  
     1.6  import java.io.IOException;
     1.7 +import java.lang.reflect.Method;
     1.8 +import java.lang.reflect.Modifier;
     1.9 +import java.util.HashMap;
    1.10 +import java.util.Map;
    1.11 +import java.util.Optional;
    1.12 +import java.util.function.BiConsumer;
    1.13  import javax.servlet.ServletException;
    1.14  import javax.servlet.http.HttpServlet;
    1.15  import javax.servlet.http.HttpServletRequest;
    1.16 @@ -43,9 +49,23 @@
    1.17  public abstract class AbstractLightPITServlet extends HttpServlet {
    1.18      
    1.19      private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class);
    1.20 +    
    1.21 +    /**
    1.22 +     * Store a reference to the annotation for quicker access.
    1.23 +     */
    1.24 +    private Optional<LightPITModule> moduleInfo = Optional.empty();
    1.25  
    1.26 +    /**
    1.27 +     * The EL proxy is necessary, because the EL resolver cannot handle annotation properties.
    1.28 +     */
    1.29 +    private Optional<LightPITModule.ELProxy> moduleInfoELProxy = Optional.empty();
    1.30      
    1.31      /**
    1.32 +     * Invocation mapping gathered from the {@link RequestMapping} annotations.
    1.33 +     */
    1.34 +    private final Map<HttpMethod, Map<String, BiConsumer<HttpServletRequest, HttpServletResponse>>> mappings = new HashMap<>();
    1.35 +
    1.36 +    /**
    1.37       * Gives implementing modules access to the {@link ModuleManager}.
    1.38       * @return the module manager
    1.39       */
    1.40 @@ -53,25 +73,114 @@
    1.41          return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME);
    1.42      }
    1.43      
    1.44 -    private void addPathInformation(HttpServletRequest req) {
    1.45 -        final String path = req.getServletPath()+"/"+req.getPathInfo();
    1.46 -        req.setAttribute(Constants.REQ_ATTR_PATH, path);
    1.47 +    private void invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) {
    1.48 +        try {
    1.49 +            LOG.debug("invoke {}", method.getName());
    1.50 +            method.invoke(this, req, resp);
    1.51 +        } catch (ReflectiveOperationException ex) {
    1.52 +            LOG.error(String.format("invocation of method %s failed", method.getName()), ex);
    1.53 +        }
    1.54 +    }
    1.55 +
    1.56 +    @Override
    1.57 +    public void init() throws ServletException {
    1.58 +        moduleInfo = Optional.ofNullable(this.getClass().getAnnotation(LightPITModule.class));
    1.59 +        moduleInfoELProxy = moduleInfo.map(LightPITModule.ELProxy::convert);
    1.60 +        
    1.61 +        if (moduleInfo.isPresent()) {
    1.62 +            Method[] methods = getClass().getDeclaredMethods();
    1.63 +            for (Method method : methods) {
    1.64 +                Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class));
    1.65 +                if (mapping.isPresent()) {
    1.66 +                    if (!Modifier.isPublic(method.getModifiers())) {
    1.67 +                        LOG.warn("{} is annotated with {} but is not public",
    1.68 +                                method.getName(), RequestMapping.class.getSimpleName()
    1.69 +                        );
    1.70 +                        continue;
    1.71 +                    }
    1.72 +                    if (Modifier.isAbstract(method.getModifiers())) {
    1.73 +                        LOG.warn("{} is annotated with {} but is abstract",
    1.74 +                                method.getName(), RequestMapping.class.getSimpleName()
    1.75 +                        );
    1.76 +                        continue;
    1.77 +                    }
    1.78 +                    
    1.79 +                    Class<?>[] params = method.getParameterTypes();
    1.80 +                    if (params.length == 2
    1.81 +                            && HttpServletRequest.class.isAssignableFrom(params[0])
    1.82 +                            && HttpServletResponse.class.isAssignableFrom(params[1])) {
    1.83 +                        
    1.84 +                        if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
    1.85 +                                putIfAbsent(mapping.get().requestPath(),
    1.86 +                                        (req, resp) -> invokeMapping(method, req, resp)) != null) {
    1.87 +                            LOG.warn("{} {} has multiple mappings",
    1.88 +                                    mapping.get().method(),
    1.89 +                                    mapping.get().requestPath()
    1.90 +                            );
    1.91 +                        }
    1.92 +                        
    1.93 +                        LOG.info("{} {} maps to {}",
    1.94 +                                mapping.get().method(),
    1.95 +                                mapping.get().requestPath(),
    1.96 +                                method.getName()
    1.97 +                        );
    1.98 +                    } else {
    1.99 +                        LOG.warn("{} is annotated with {} but has the wrong signature - (HttpServletRequest,HttpServletResponse) required",
   1.100 +                                method.getName(), RequestMapping.class.getSimpleName()
   1.101 +                        );
   1.102 +                    }
   1.103 +                }
   1.104 +            }
   1.105 +        }
   1.106 +        
   1.107 +        LOG.trace("{} initialized", getServletName());
   1.108 +    }
   1.109 +
   1.110 +    @Override
   1.111 +    public void destroy() {
   1.112 +        mappings.clear();
   1.113 +        LOG.trace("{} destroyed", getServletName());
   1.114 +    }
   1.115 +    
   1.116 +    
   1.117 +    /**
   1.118 +     * Sets several requests attributes, that can be used by the JSP.
   1.119 +     * 
   1.120 +     * @param req the servlet request object
   1.121 +     * @see Constants#REQ_ATTR_PATH
   1.122 +     * @see Constants#REQ_ATTR_MODULE_CLASSNAME
   1.123 +     * @see Constants#REQ_ATTR_MODULE_INFO
   1.124 +     */
   1.125 +    private void setGenericRequestAttributes(HttpServletRequest req) {
   1.126 +        req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req));
   1.127 +
   1.128 +        req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName());
   1.129 +
   1.130 +        moduleInfoELProxy.ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy));
   1.131      }
   1.132      
   1.133      private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp)
   1.134              throws IOException, ServletException {
   1.135          
   1.136 -        addPathInformation(req);
   1.137 -        
   1.138 -        final ModuleManager mm = getModuleManager();
   1.139 -        req.setAttribute(Constants.REQ_ATTR_MENU, mm.getMainMenu());
   1.140 -        
   1.141 +        setGenericRequestAttributes(req);
   1.142 +        req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu());
   1.143          req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp);
   1.144      }
   1.145      
   1.146 +    private Optional<BiConsumer<HttpServletRequest, HttpServletResponse>> findMapping(HttpMethod method, HttpServletRequest req) {
   1.147 +        return Optional.ofNullable(mappings.get(method)).map(
   1.148 +                (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse(""))
   1.149 +        );
   1.150 +    }
   1.151 +    
   1.152      @Override
   1.153      protected final void doGet(HttpServletRequest req, HttpServletResponse resp)
   1.154              throws ServletException, IOException {
   1.155 +        
   1.156 +        findMapping(HttpMethod.GET, req).ifPresent((consumer) -> consumer.accept(req, resp));
   1.157 +        
   1.158 +        // TODO: let the invoked handler decide (signature must be changed from a BiConsumer to a BiFunction)
   1.159 +        // TODO: we should call a default handler, if no specific mapping could be found
   1.160          forwardToFullView(req, resp);
   1.161      }
   1.162  
   1.163 @@ -79,6 +188,8 @@
   1.164      protected final void doPost(HttpServletRequest req, HttpServletResponse resp)
   1.165              throws ServletException, IOException {
   1.166          
   1.167 +        findMapping(HttpMethod.POST, req).ifPresent((consumer) -> consumer.accept(req, resp));
   1.168 +        
   1.169          forwardToFullView(req, resp);
   1.170      }
   1.171  }

mercurial