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

changeset 34
824d4042c857
parent 33
fd8c40ff78c3
child 36
0f4f8f255c32
equal deleted inserted replaced
33:fd8c40ff78c3 34:824d4042c857
1 /* 1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * 3 *
4 * Copyright 2018 Mike Becker. All rights reserved. 4 * Copyright 2018 Mike Becker. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met: 7 * modification, are permitted provided that the following conditions are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer. 10 * notice, this list of conditions and the following disclaimer.
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 * 27 *
28 */ 28 */
29 package de.uapcore.lightpit; 29 package de.uapcore.lightpit;
30 30
31 import org.slf4j.Logger; 31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory; 32 import org.slf4j.LoggerFactory;
44 /** 44 /**
45 * A special implementation of a HTTPServlet which is focused on implementing 45 * A special implementation of a HTTPServlet which is focused on implementing
46 * the necessary functionality for {@link LightPITModule}s. 46 * the necessary functionality for {@link LightPITModule}s.
47 */ 47 */
48 public abstract class AbstractLightPITServlet extends HttpServlet { 48 public abstract class AbstractLightPITServlet extends HttpServlet {
49 49
50 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); 50 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class);
51 51
52 private static final String HTML_FULL_DISPATCHER = Functions.jspPath("html_full"); 52 private static final String HTML_FULL_DISPATCHER = Functions.jspPath("html_full");
53 53
54 /** 54 /**
55 * Store a reference to the annotation for quicker access. 55 * Store a reference to the annotation for quicker access.
56 */ 56 */
64 64
65 @FunctionalInterface 65 @FunctionalInterface
66 private interface HandlerMethod { 66 private interface HandlerMethod {
67 ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException; 67 ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException;
68 } 68 }
69 69
70 /** 70 /**
71 * Invocation mapping gathered from the {@link RequestMapping} annotations. 71 * Invocation mapping gathered from the {@link RequestMapping} annotations.
72 * 72 * <p>
73 * Paths in this map must always start with a leading slash, although 73 * Paths in this map must always start with a leading slash, although
74 * the specification in the annotation must not start with a leading slash. 74 * the specification in the annotation must not start with a leading slash.
75 * 75 * <p>
76 * The reason for this is the different handling of empty paths in 76 * The reason for this is the different handling of empty paths in
77 * {@link HttpServletRequest#getPathInfo()}. 77 * {@link HttpServletRequest#getPathInfo()}.
78 */ 78 */
79 private final Map<HttpMethod, Map<String, HandlerMethod>> mappings = new HashMap<>(); 79 private final Map<HttpMethod, Map<String, HandlerMethod>> mappings = new HashMap<>();
80 80
81 /** 81 /**
85 */ 85 */
86 protected final ModuleManager getModuleManager() { 86 protected final ModuleManager getModuleManager() {
87 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); 87 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME);
88 } 88 }
89 89
90 /**
91 * Returns the annotated module information.
92 *
93 * @return the module annotation
94 */
90 public final LightPITModule getModuleInfo() { 95 public final LightPITModule getModuleInfo() {
91 return moduleInfo; 96 return moduleInfo;
92 } 97 }
93 98
94 /** 99 /**
150 155
151 Class<?>[] params = method.getParameterTypes(); 156 Class<?>[] params = method.getParameterTypes();
152 if (params.length == 2 157 if (params.length == 2
153 && HttpServletRequest.class.isAssignableFrom(params[0]) 158 && HttpServletRequest.class.isAssignableFrom(params[0])
154 && HttpServletResponse.class.isAssignableFrom(params[1])) { 159 && HttpServletResponse.class.isAssignableFrom(params[1])) {
155 160
156 final String requestPath = "/"+mapping.get().requestPath(); 161 final String requestPath = "/" + mapping.get().requestPath();
157 162
158 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). 163 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
159 putIfAbsent(requestPath, 164 putIfAbsent(requestPath,
160 (req, resp) -> invokeMapping(method, req, resp)) != null) { 165 (req, resp) -> invokeMapping(method, req, resp)) != null) {
161 LOG.warn("{} {} has multiple mappings", 166 LOG.warn("{} {} has multiple mappings",
185 @Override 190 @Override
186 public void destroy() { 191 public void destroy() {
187 mappings.clear(); 192 mappings.clear();
188 LOG.trace("{} destroyed", getServletName()); 193 LOG.trace("{} destroyed", getServletName());
189 } 194 }
190 195
191 /** 196 /**
192 * Sets the name of the dynamic fragment. 197 * Sets the name of the dynamic fragment.
193 * 198 * <p>
194 * It is sufficient to specify the name without any extension. The extension 199 * It is sufficient to specify the name without any extension. The extension
195 * is added automatically if not specified. 200 * is added automatically if not specified.
196 * 201 * <p>
197 * The fragment must be located in the dynamic fragments folder. 202 * The fragment must be located in the dynamic fragments folder.
198 * 203 *
199 * @param req the servlet request object 204 * @param req the servlet request object
200 * @param fragmentName the name of the fragment 205 * @param fragmentName the name of the fragment
201 * @see Constants#DYN_FRAGMENT_PATH_PREFIX 206 * @see Constants#DYN_FRAGMENT_PATH_PREFIX
202 */ 207 */
203 public void setDynamicFragment(HttpServletRequest req, String fragmentName) { 208 public void setDynamicFragment(HttpServletRequest req, String fragmentName) {
204 req.setAttribute(Constants.REQ_ATTR_FRAGMENT, Functions.dynFragmentPath(fragmentName)); 209 req.setAttribute(Constants.REQ_ATTR_FRAGMENT, Functions.dynFragmentPath(fragmentName));
205 } 210 }
206 211
207 /** 212 /**
208 * Specifies the name of an additional stylesheet used by the module. 213 * Specifies the name of an additional stylesheet used by the module.
209 * 214 * <p>
210 * Setting an additional stylesheet is optional, but quite common for HTML 215 * Setting an additional stylesheet is optional, but quite common for HTML
211 * output. 216 * output.
212 * 217 * <p>
213 * It is sufficient to specify the name without any extension. The extension 218 * It is sufficient to specify the name without any extension. The extension
214 * is added automatically if not specified. 219 * is added automatically if not specified.
215 * 220 *
216 * @param req the servlet request object 221 * @param req the servlet request object
217 * @param stylesheet the name of the stylesheet 222 * @param stylesheet the name of the stylesheet
218 */ 223 */
219 public void setStylesheet(HttpServletRequest req, String stylesheet) { 224 public void setStylesheet(HttpServletRequest req, String stylesheet) {
220 req.setAttribute(Constants.REQ_ATTR_STYLESHEET, Functions.enforceExt(stylesheet, ".css")); 225 req.setAttribute(Constants.REQ_ATTR_STYLESHEET, Functions.enforceExt(stylesheet, ".css"));
221 } 226 }
222 227
223 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) 228 private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp)
224 throws IOException, ServletException { 229 throws IOException, ServletException {
225 230
226 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu(getDatabaseFacade())); 231 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu(getDatabaseFacade()));
227 req.getRequestDispatcher(HTML_FULL_DISPATCHER).forward(req, resp); 232 req.getRequestDispatcher(HTML_FULL_DISPATCHER).forward(req, resp);
228 } 233 }
229 234
230 private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) { 235 private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) {
231 return Optional.ofNullable(mappings.get(method)).map( 236 return Optional.ofNullable(mappings.get(method)).map(
232 (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("/")) 237 (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("/"))
233 ); 238 );
234 } 239 }
235 240
236 private void forwardAsSepcified(ResponseType type, HttpServletRequest req, HttpServletResponse resp) 241 private void forwardAsSpecified(ResponseType type, HttpServletRequest req, HttpServletResponse resp)
237 throws ServletException, IOException { 242 throws ServletException, IOException {
238 switch (type) { 243 switch (type) {
239 case NONE: return; 244 case NONE:
245 return;
240 case HTML_FULL: 246 case HTML_FULL:
241 forwardToFullView(req, resp); 247 forwardToFullView(req, resp);
242 return; 248 return;
243 // TODO: implement remaining response types 249 // TODO: implement remaining response types
244 default: 250 default:
245 // this code should be unreachable 251 throw new AssertionError("ResponseType switch is not exhaustive - this is a bug!");
246 LOG.error("ResponseType switch is not exhaustive - this is a bug!"); 252 }
247 throw new UnsupportedOperationException(); 253 }
248 } 254
249 }
250
251 private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) 255 private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp)
252 throws ServletException, IOException { 256 throws ServletException, IOException {
253 257
254 // Synchronize module information with database 258 // Synchronize module information with database
255 getModuleManager().syncWithDatabase(getDatabaseFacade()); 259 getModuleManager().syncWithDatabase(getDatabaseFacade());
256 260
257 // choose the requested language as session language (if available) or fall back to english, otherwise 261 // choose the requested language as session language (if available) or fall back to english, otherwise
258 HttpSession session = req.getSession(); 262 HttpSession session = req.getSession();
259 if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) { 263 if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) {
260 Optional<List<String>> availableLanguages = Functions.availableLanguages(getServletContext()).map(Arrays::asList); 264 Optional<List<String>> availableLanguages = Functions.availableLanguages(getServletContext()).map(Arrays::asList);
261 Optional<Locale> reqLocale = Optional.of(req.getLocale()); 265 Optional<Locale> reqLocale = Optional.of(req.getLocale());
262 Locale sessionLocale = reqLocale.filter((rl) -> availableLanguages.map((al) -> al.contains(rl.getLanguage())).orElse(false)).orElse(Locale.ENGLISH); 266 Locale sessionLocale = reqLocale.filter((rl) -> availableLanguages.map((al) -> al.contains(rl.getLanguage())).orElse(false)).orElse(Locale.ENGLISH);
263 session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, sessionLocale); 267 session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, sessionLocale);
264 LOG.debug("Settng language for new session {}: {}", session.getId(), sessionLocale.getDisplayLanguage()); 268 LOG.debug("Setting language for new session {}: {}", session.getId(), sessionLocale.getDisplayLanguage());
265 } else { 269 } else {
266 Locale sessionLocale = (Locale) session.getAttribute(Constants.SESSION_ATTR_LANGUAGE); 270 Locale sessionLocale = (Locale) session.getAttribute(Constants.SESSION_ATTR_LANGUAGE);
267 resp.setLocale(sessionLocale); 271 resp.setLocale(sessionLocale);
268 LOG.trace("Continuing session {} with language {}", session.getId(), sessionLocale); 272 LOG.trace("Continuing session {} with language {}", session.getId(), sessionLocale);
269 } 273 }
270 274
271 // set some internal request attributes 275 // set some internal request attributes
272 req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req)); 276 req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req));
273 req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); 277 req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName());
274 Optional.ofNullable(moduleInfoELProxy).ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); 278 Optional.ofNullable(moduleInfoELProxy).ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy));
275 279
276 280
277 // call the handler, if available, or send an HTTP 404 error 281 // call the handler, if available, or send an HTTP 404 error
278 Optional<HandlerMethod> mapping = findMapping(method, req); 282 Optional<HandlerMethod> mapping = findMapping(method, req);
279 if (mapping.isPresent()) { 283 if (mapping.isPresent()) {
280 forwardAsSepcified(mapping.get().apply(req, resp), req, resp); 284 forwardAsSpecified(mapping.get().apply(req, resp), req, resp);
281 } else { 285 } else {
282 resp.sendError(HttpServletResponse.SC_NOT_FOUND); 286 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
283 } 287 }
284 } 288 }
285 289
286 @Override 290 @Override
287 protected final void doGet(HttpServletRequest req, HttpServletResponse resp) 291 protected final void doGet(HttpServletRequest req, HttpServletResponse resp)
288 throws ServletException, IOException { 292 throws ServletException, IOException {
289 doProcess(HttpMethod.GET, req, resp); 293 doProcess(HttpMethod.GET, req, resp);
290 } 294 }

mercurial