diff -r fecda0f466e6 -r cf85ef18f231 src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sun May 10 10:58:31 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Mon May 11 19:09:06 2020 +0200 @@ -28,6 +28,8 @@ */ package de.uapcore.lightpit; +import de.uapcore.lightpit.dao.DataAccessObjects; +import de.uapcore.lightpit.dao.postgres.PGDataAccessObjects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +41,8 @@ import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.sql.Connection; +import java.sql.SQLException; import java.util.*; /** @@ -59,7 +63,7 @@ @FunctionalInterface private interface HandlerMethod { - ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException; + ResponseType apply(HttpServletRequest request, HttpServletResponse response, DataAccessObjects dao) throws IOException, SQLException; } /** @@ -82,21 +86,30 @@ return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); } + /** - * Gives implementing modules access to the {@link DatabaseFacade}. + * Creates a set of data access objects for the specified connection. * - * @return the database facade + * @param connection the SQL connection + * @return a set of data access objects */ - protected final DatabaseFacade getDatabaseFacade() { - return (DatabaseFacade) getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); + private DataAccessObjects createDataAccessObjects(Connection connection) throws SQLException { + final var df = (DatabaseFacade) getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); + switch (df.getSQLDialect()) { + case Postgres: + return new PGDataAccessObjects(connection); + default: + throw new AssertionError("Non-exhaustive switch - this is a bug."); + } } - private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) throws IOException { + private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { try { LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName()); - return (ResponseType) method.invoke(this, req, resp); + return (ResponseType) method.invoke(this, req, resp, dao); } catch (ReflectiveOperationException | ClassCastException ex) { - LOG.error(String.format("invocation of method %s failed", method.getName()), ex); + LOG.error("invocation of method {} failed: {}", method.getName(), ex.getMessage()); + LOG.debug("Details: ", ex); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return ResponseType.NONE; } @@ -140,15 +153,16 @@ } Class[] params = method.getParameterTypes(); - if (params.length == 2 + if (params.length == 3 && HttpServletRequest.class.isAssignableFrom(params[0]) - && HttpServletResponse.class.isAssignableFrom(params[1])) { + && HttpServletResponse.class.isAssignableFrom(params[1]) + && DataAccessObjects.class.isAssignableFrom(params[2])) { final String requestPath = "/" + mapping.get().requestPath(); if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). putIfAbsent(requestPath, - (req, resp) -> invokeMapping(method, req, resp)) != null) { + (req, resp, dao) -> invokeMapping(method, req, resp, dao)) != null) { LOG.warn("{} {} has multiple mappings", mapping.get().method(), mapping.get().requestPath() @@ -238,8 +252,7 @@ } } - private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // choose the requested language as session language (if available) or fall back to english, otherwise HttpSession session = req.getSession(); @@ -260,13 +273,21 @@ req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); Optional.ofNullable(moduleInfo).ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); - - // call the handler, if available, or send an HTTP 404 error - Optional mapping = findMapping(method, req); - if (mapping.isPresent()) { - forwardAsSpecified(mapping.get().apply(req, resp), req, resp); - } else { - resp.sendError(HttpServletResponse.SC_NOT_FOUND); + // obtain a connection and create the data access objects + final var db = (DatabaseFacade) req.getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); + try (final var connection = db.getDataSource().getConnection()) { + final var dao = createDataAccessObjects(connection); + // call the handler, if available, or send an HTTP 404 error + final var mapping = findMapping(method, req); + if (mapping.isPresent()) { + forwardAsSpecified(mapping.get().apply(req, resp, dao), req, resp); + } else { + resp.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } catch (SQLException ex) { + LOG.error("Database exception (Code {}): {}", ex.getErrorCode(), ex.getMessage()); + LOG.debug("Details: ", ex); + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Database Error - Code:" + ex.getErrorCode()); } }