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

changeset 39
e722861558bb
parent 38
cf85ef18f231
child 40
276ef00a336d
equal deleted inserted replaced
38:cf85ef18f231 39:e722861558bb
58 /** 58 /**
59 * The EL proxy is necessary, because the EL resolver cannot handle annotation properties. 59 * The EL proxy is necessary, because the EL resolver cannot handle annotation properties.
60 */ 60 */
61 private LightPITModule.ELProxy moduleInfo = null; 61 private LightPITModule.ELProxy moduleInfo = null;
62 62
63
64 @FunctionalInterface
65 private interface HandlerMethod {
66 ResponseType apply(HttpServletRequest request, HttpServletResponse response, DataAccessObjects dao) throws IOException, SQLException;
67 }
68
69 /** 63 /**
70 * Invocation mapping gathered from the {@link RequestMapping} annotations. 64 * Invocation mapping gathered from the {@link RequestMapping} annotations.
71 * <p> 65 * <p>
72 * Paths in this map must always start with a leading slash, although 66 * Paths in this map must always start with a leading slash, although
73 * the specification in the annotation must not start with a leading slash. 67 * the specification in the annotation must not start with a leading slash.
74 * <p> 68 * <p>
75 * The reason for this is the different handling of empty paths in 69 * The reason for this is the different handling of empty paths in
76 * {@link HttpServletRequest#getPathInfo()}. 70 * {@link HttpServletRequest#getPathInfo()}.
77 */ 71 */
78 private final Map<HttpMethod, Map<String, HandlerMethod>> mappings = new HashMap<>(); 72 private final Map<HttpMethod, Map<String, Method>> mappings = new HashMap<>();
79 73
80 /** 74 /**
81 * Gives implementing modules access to the {@link ModuleManager}. 75 * Gives implementing modules access to the {@link ModuleManager}.
82 * 76 *
83 * @return the module manager 77 * @return the module manager
93 * @param connection the SQL connection 87 * @param connection the SQL connection
94 * @return a set of data access objects 88 * @return a set of data access objects
95 */ 89 */
96 private DataAccessObjects createDataAccessObjects(Connection connection) throws SQLException { 90 private DataAccessObjects createDataAccessObjects(Connection connection) throws SQLException {
97 final var df = (DatabaseFacade) getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); 91 final var df = (DatabaseFacade) getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME);
98 switch (df.getSQLDialect()) { 92 if (df.getSQLDialect() == DatabaseFacade.Dialect.Postgres) {
99 case Postgres: 93 return new PGDataAccessObjects(connection);
100 return new PGDataAccessObjects(connection); 94 }
101 default: 95 throw new AssertionError("Non-exhaustive if-else - this is a bug.");
102 throw new AssertionError("Non-exhaustive switch - this is a bug.");
103 }
104 } 96 }
105 97
106 private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { 98 private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
107 try { 99 try {
108 LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName()); 100 LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName());
158 && HttpServletResponse.class.isAssignableFrom(params[1]) 150 && HttpServletResponse.class.isAssignableFrom(params[1])
159 && DataAccessObjects.class.isAssignableFrom(params[2])) { 151 && DataAccessObjects.class.isAssignableFrom(params[2])) {
160 152
161 final String requestPath = "/" + mapping.get().requestPath(); 153 final String requestPath = "/" + mapping.get().requestPath();
162 154
163 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). 155 if (mappings
164 putIfAbsent(requestPath, 156 .computeIfAbsent(mapping.get().method(), k -> new HashMap<>())
165 (req, resp, dao) -> invokeMapping(method, req, resp, dao)) != null) { 157 .putIfAbsent(requestPath, method) != null) {
166 LOG.warn("{} {} has multiple mappings", 158 LOG.warn("{} {} has multiple mappings",
167 mapping.get().method(), 159 mapping.get().method(),
168 mapping.get().requestPath() 160 mapping.get().requestPath()
169 ); 161 );
170 } 162 }
230 222
231 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu()); 223 req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu());
232 req.getRequestDispatcher(HTML_FULL_DISPATCHER).forward(req, resp); 224 req.getRequestDispatcher(HTML_FULL_DISPATCHER).forward(req, resp);
233 } 225 }
234 226
235 private Optional<HandlerMethod> findMapping(HttpMethod method, HttpServletRequest req) { 227 private Optional<Method> findMapping(HttpMethod method, HttpServletRequest req) {
236 return Optional.ofNullable(mappings.get(method)).map( 228 return Optional.ofNullable(mappings.get(method))
237 (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("/")) 229 .map(rm -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("/"))
238 ); 230 );
239 } 231 }
240 232
241 private void forwardAsSpecified(ResponseType type, HttpServletRequest req, HttpServletResponse resp) 233 private void forwardAsSpecified(ResponseType type, HttpServletRequest req, HttpServletResponse resp)
242 throws ServletException, IOException { 234 throws ServletException, IOException {
243 switch (type) { 235 switch (type) {
275 267
276 // obtain a connection and create the data access objects 268 // obtain a connection and create the data access objects
277 final var db = (DatabaseFacade) req.getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); 269 final var db = (DatabaseFacade) req.getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME);
278 try (final var connection = db.getDataSource().getConnection()) { 270 try (final var connection = db.getDataSource().getConnection()) {
279 final var dao = createDataAccessObjects(connection); 271 final var dao = createDataAccessObjects(connection);
280 // call the handler, if available, or send an HTTP 404 error 272 try {
281 final var mapping = findMapping(method, req); 273 connection.setAutoCommit(false);
282 if (mapping.isPresent()) { 274 // call the handler, if available, or send an HTTP 404 error
283 forwardAsSpecified(mapping.get().apply(req, resp, dao), req, resp); 275 final var mapping = findMapping(method, req);
284 } else { 276 if (mapping.isPresent()) {
285 resp.sendError(HttpServletResponse.SC_NOT_FOUND); 277 forwardAsSpecified(invokeMapping(mapping.get(), req, resp, dao), req, resp);
278 } else {
279 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
280 }
281 connection.commit();
282 } catch (SQLException ex) {
283 LOG.warn("Database transaction failed (Code {}): {}", ex.getErrorCode(), ex.getMessage());
284 LOG.debug("Details: ", ex);
285 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unhandled Transaction Error - Code:" + ex.getErrorCode());
286 connection.rollback();
286 } 287 }
287 } catch (SQLException ex) { 288 } catch (SQLException ex) {
288 LOG.error("Database exception (Code {}): {}", ex.getErrorCode(), ex.getMessage()); 289 LOG.error("Severe Database Exception (Code {}): {}", ex.getErrorCode(), ex.getMessage());
289 LOG.debug("Details: ", ex); 290 LOG.debug("Details: ", ex);
290 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Database Error - Code:" + ex.getErrorCode()); 291 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Database Error - Code:" + ex.getErrorCode());
291 } 292 }
292 } 293 }
293 294

mercurial