1.1 --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sun May 10 10:58:31 2020 +0200 1.2 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Mon May 11 19:09:06 2020 +0200 1.3 @@ -28,6 +28,8 @@ 1.4 */ 1.5 package de.uapcore.lightpit; 1.6 1.7 +import de.uapcore.lightpit.dao.DataAccessObjects; 1.8 +import de.uapcore.lightpit.dao.postgres.PGDataAccessObjects; 1.9 import org.slf4j.Logger; 1.10 import org.slf4j.LoggerFactory; 1.11 1.12 @@ -39,6 +41,8 @@ 1.13 import java.io.IOException; 1.14 import java.lang.reflect.Method; 1.15 import java.lang.reflect.Modifier; 1.16 +import java.sql.Connection; 1.17 +import java.sql.SQLException; 1.18 import java.util.*; 1.19 1.20 /** 1.21 @@ -59,7 +63,7 @@ 1.22 1.23 @FunctionalInterface 1.24 private interface HandlerMethod { 1.25 - ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException; 1.26 + ResponseType apply(HttpServletRequest request, HttpServletResponse response, DataAccessObjects dao) throws IOException, SQLException; 1.27 } 1.28 1.29 /** 1.30 @@ -82,21 +86,30 @@ 1.31 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); 1.32 } 1.33 1.34 + 1.35 /** 1.36 - * Gives implementing modules access to the {@link DatabaseFacade}. 1.37 + * Creates a set of data access objects for the specified connection. 1.38 * 1.39 - * @return the database facade 1.40 + * @param connection the SQL connection 1.41 + * @return a set of data access objects 1.42 */ 1.43 - protected final DatabaseFacade getDatabaseFacade() { 1.44 - return (DatabaseFacade) getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); 1.45 + private DataAccessObjects createDataAccessObjects(Connection connection) throws SQLException { 1.46 + final var df = (DatabaseFacade) getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); 1.47 + switch (df.getSQLDialect()) { 1.48 + case Postgres: 1.49 + return new PGDataAccessObjects(connection); 1.50 + default: 1.51 + throw new AssertionError("Non-exhaustive switch - this is a bug."); 1.52 + } 1.53 } 1.54 1.55 - private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) throws IOException { 1.56 + private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException { 1.57 try { 1.58 LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName()); 1.59 - return (ResponseType) method.invoke(this, req, resp); 1.60 + return (ResponseType) method.invoke(this, req, resp, dao); 1.61 } catch (ReflectiveOperationException | ClassCastException ex) { 1.62 - LOG.error(String.format("invocation of method %s failed", method.getName()), ex); 1.63 + LOG.error("invocation of method {} failed: {}", method.getName(), ex.getMessage()); 1.64 + LOG.debug("Details: ", ex); 1.65 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 1.66 return ResponseType.NONE; 1.67 } 1.68 @@ -140,15 +153,16 @@ 1.69 } 1.70 1.71 Class<?>[] params = method.getParameterTypes(); 1.72 - if (params.length == 2 1.73 + if (params.length == 3 1.74 && HttpServletRequest.class.isAssignableFrom(params[0]) 1.75 - && HttpServletResponse.class.isAssignableFrom(params[1])) { 1.76 + && HttpServletResponse.class.isAssignableFrom(params[1]) 1.77 + && DataAccessObjects.class.isAssignableFrom(params[2])) { 1.78 1.79 final String requestPath = "/" + mapping.get().requestPath(); 1.80 1.81 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). 1.82 putIfAbsent(requestPath, 1.83 - (req, resp) -> invokeMapping(method, req, resp)) != null) { 1.84 + (req, resp, dao) -> invokeMapping(method, req, resp, dao)) != null) { 1.85 LOG.warn("{} {} has multiple mappings", 1.86 mapping.get().method(), 1.87 mapping.get().requestPath() 1.88 @@ -238,8 +252,7 @@ 1.89 } 1.90 } 1.91 1.92 - private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) 1.93 - throws ServletException, IOException { 1.94 + private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 1.95 1.96 // choose the requested language as session language (if available) or fall back to english, otherwise 1.97 HttpSession session = req.getSession(); 1.98 @@ -260,13 +273,21 @@ 1.99 req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); 1.100 Optional.ofNullable(moduleInfo).ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); 1.101 1.102 - 1.103 - // call the handler, if available, or send an HTTP 404 error 1.104 - Optional<HandlerMethod> mapping = findMapping(method, req); 1.105 - if (mapping.isPresent()) { 1.106 - forwardAsSpecified(mapping.get().apply(req, resp), req, resp); 1.107 - } else { 1.108 - resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.109 + // obtain a connection and create the data access objects 1.110 + final var db = (DatabaseFacade) req.getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); 1.111 + try (final var connection = db.getDataSource().getConnection()) { 1.112 + final var dao = createDataAccessObjects(connection); 1.113 + // call the handler, if available, or send an HTTP 404 error 1.114 + final var mapping = findMapping(method, req); 1.115 + if (mapping.isPresent()) { 1.116 + forwardAsSpecified(mapping.get().apply(req, resp, dao), req, resp); 1.117 + } else { 1.118 + resp.sendError(HttpServletResponse.SC_NOT_FOUND); 1.119 + } 1.120 + } catch (SQLException ex) { 1.121 + LOG.error("Database exception (Code {}): {}", ex.getErrorCode(), ex.getMessage()); 1.122 + LOG.debug("Details: ", ex); 1.123 + resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Database Error - Code:" + ex.getErrorCode()); 1.124 } 1.125 } 1.126