41 * the necessary functionality for {@link LightPITModule}s. |
47 * the necessary functionality for {@link LightPITModule}s. |
42 */ |
48 */ |
43 public abstract class AbstractLightPITServlet extends HttpServlet { |
49 public abstract class AbstractLightPITServlet extends HttpServlet { |
44 |
50 |
45 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); |
51 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); |
|
52 |
|
53 /** |
|
54 * Store a reference to the annotation for quicker access. |
|
55 */ |
|
56 private Optional<LightPITModule> moduleInfo = Optional.empty(); |
46 |
57 |
|
58 /** |
|
59 * The EL proxy is necessary, because the EL resolver cannot handle annotation properties. |
|
60 */ |
|
61 private Optional<LightPITModule.ELProxy> moduleInfoELProxy = Optional.empty(); |
47 |
62 |
|
63 /** |
|
64 * Invocation mapping gathered from the {@link RequestMapping} annotations. |
|
65 */ |
|
66 private final Map<HttpMethod, Map<String, BiConsumer<HttpServletRequest, HttpServletResponse>>> mappings = new HashMap<>(); |
|
67 |
48 /** |
68 /** |
49 * Gives implementing modules access to the {@link ModuleManager}. |
69 * Gives implementing modules access to the {@link ModuleManager}. |
50 * @return the module manager |
70 * @return the module manager |
51 */ |
71 */ |
52 protected final ModuleManager getModuleManager() { |
72 protected final ModuleManager getModuleManager() { |
53 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); |
73 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); |
54 } |
74 } |
55 |
75 |
56 private void addPathInformation(HttpServletRequest req) { |
76 private void invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) { |
57 final String path = req.getServletPath()+"/"+req.getPathInfo(); |
77 try { |
58 req.setAttribute(Constants.REQ_ATTR_PATH, path); |
78 LOG.debug("invoke {}", method.getName()); |
|
79 method.invoke(this, req, resp); |
|
80 } catch (ReflectiveOperationException ex) { |
|
81 LOG.error(String.format("invocation of method %s failed", method.getName()), ex); |
|
82 } |
|
83 } |
|
84 |
|
85 @Override |
|
86 public void init() throws ServletException { |
|
87 moduleInfo = Optional.ofNullable(this.getClass().getAnnotation(LightPITModule.class)); |
|
88 moduleInfoELProxy = moduleInfo.map(LightPITModule.ELProxy::convert); |
|
89 |
|
90 if (moduleInfo.isPresent()) { |
|
91 Method[] methods = getClass().getDeclaredMethods(); |
|
92 for (Method method : methods) { |
|
93 Optional<RequestMapping> mapping = Optional.ofNullable(method.getAnnotation(RequestMapping.class)); |
|
94 if (mapping.isPresent()) { |
|
95 if (!Modifier.isPublic(method.getModifiers())) { |
|
96 LOG.warn("{} is annotated with {} but is not public", |
|
97 method.getName(), RequestMapping.class.getSimpleName() |
|
98 ); |
|
99 continue; |
|
100 } |
|
101 if (Modifier.isAbstract(method.getModifiers())) { |
|
102 LOG.warn("{} is annotated with {} but is abstract", |
|
103 method.getName(), RequestMapping.class.getSimpleName() |
|
104 ); |
|
105 continue; |
|
106 } |
|
107 |
|
108 Class<?>[] params = method.getParameterTypes(); |
|
109 if (params.length == 2 |
|
110 && HttpServletRequest.class.isAssignableFrom(params[0]) |
|
111 && HttpServletResponse.class.isAssignableFrom(params[1])) { |
|
112 |
|
113 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). |
|
114 putIfAbsent(mapping.get().requestPath(), |
|
115 (req, resp) -> invokeMapping(method, req, resp)) != null) { |
|
116 LOG.warn("{} {} has multiple mappings", |
|
117 mapping.get().method(), |
|
118 mapping.get().requestPath() |
|
119 ); |
|
120 } |
|
121 |
|
122 LOG.info("{} {} maps to {}", |
|
123 mapping.get().method(), |
|
124 mapping.get().requestPath(), |
|
125 method.getName() |
|
126 ); |
|
127 } else { |
|
128 LOG.warn("{} is annotated with {} but has the wrong signature - (HttpServletRequest,HttpServletResponse) required", |
|
129 method.getName(), RequestMapping.class.getSimpleName() |
|
130 ); |
|
131 } |
|
132 } |
|
133 } |
|
134 } |
|
135 |
|
136 LOG.trace("{} initialized", getServletName()); |
|
137 } |
|
138 |
|
139 @Override |
|
140 public void destroy() { |
|
141 mappings.clear(); |
|
142 LOG.trace("{} destroyed", getServletName()); |
|
143 } |
|
144 |
|
145 |
|
146 /** |
|
147 * Sets several requests attributes, that can be used by the JSP. |
|
148 * |
|
149 * @param req the servlet request object |
|
150 * @see Constants#REQ_ATTR_PATH |
|
151 * @see Constants#REQ_ATTR_MODULE_CLASSNAME |
|
152 * @see Constants#REQ_ATTR_MODULE_INFO |
|
153 */ |
|
154 private void setGenericRequestAttributes(HttpServletRequest req) { |
|
155 req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req)); |
|
156 |
|
157 req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); |
|
158 |
|
159 moduleInfoELProxy.ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); |
59 } |
160 } |
60 |
161 |
61 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) |
162 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) |
62 throws IOException, ServletException { |
163 throws IOException, ServletException { |
63 |
164 |
64 addPathInformation(req); |
165 setGenericRequestAttributes(req); |
65 |
166 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu()); |
66 final ModuleManager mm = getModuleManager(); |
|
67 req.setAttribute(Constants.REQ_ATTR_MENU, mm.getMainMenu()); |
|
68 |
|
69 req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp); |
167 req.getRequestDispatcher(Functions.jspPath("full.jsp")).forward(req, resp); |
|
168 } |
|
169 |
|
170 private Optional<BiConsumer<HttpServletRequest, HttpServletResponse>> findMapping(HttpMethod method, HttpServletRequest req) { |
|
171 return Optional.ofNullable(mappings.get(method)).map( |
|
172 (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("")) |
|
173 ); |
70 } |
174 } |
71 |
175 |
72 @Override |
176 @Override |
73 protected final void doGet(HttpServletRequest req, HttpServletResponse resp) |
177 protected final void doGet(HttpServletRequest req, HttpServletResponse resp) |
74 throws ServletException, IOException { |
178 throws ServletException, IOException { |
|
179 |
|
180 findMapping(HttpMethod.GET, req).ifPresent((consumer) -> consumer.accept(req, resp)); |
|
181 |
|
182 // TODO: let the invoked handler decide (signature must be changed from a BiConsumer to a BiFunction) |
|
183 // TODO: we should call a default handler, if no specific mapping could be found |
75 forwardToFullView(req, resp); |
184 forwardToFullView(req, resp); |
76 } |
185 } |
77 |
186 |
78 @Override |
187 @Override |
79 protected final void doPost(HttpServletRequest req, HttpServletResponse resp) |
188 protected final void doPost(HttpServletRequest req, HttpServletResponse resp) |
80 throws ServletException, IOException { |
189 throws ServletException, IOException { |
81 |
190 |
|
191 findMapping(HttpMethod.POST, req).ifPresent((consumer) -> consumer.accept(req, resp)); |
|
192 |
82 forwardToFullView(req, resp); |
193 forwardToFullView(req, resp); |
83 } |
194 } |
84 } |
195 } |