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

changeset 38
cf85ef18f231
parent 36
0f4f8f255c32
child 39
e722861558bb
equal deleted inserted replaced
37:fecda0f466e6 38:cf85ef18f231
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 de.uapcore.lightpit.dao.DataAccessObjects;
32 import de.uapcore.lightpit.dao.postgres.PGDataAccessObjects;
31 import org.slf4j.Logger; 33 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory; 34 import org.slf4j.LoggerFactory;
33 35
34 import javax.servlet.ServletException; 36 import javax.servlet.ServletException;
35 import javax.servlet.http.HttpServlet; 37 import javax.servlet.http.HttpServlet;
37 import javax.servlet.http.HttpServletResponse; 39 import javax.servlet.http.HttpServletResponse;
38 import javax.servlet.http.HttpSession; 40 import javax.servlet.http.HttpSession;
39 import java.io.IOException; 41 import java.io.IOException;
40 import java.lang.reflect.Method; 42 import java.lang.reflect.Method;
41 import java.lang.reflect.Modifier; 43 import java.lang.reflect.Modifier;
44 import java.sql.Connection;
45 import java.sql.SQLException;
42 import java.util.*; 46 import java.util.*;
43 47
44 /** 48 /**
45 * A special implementation of a HTTPServlet which is focused on implementing 49 * A special implementation of a HTTPServlet which is focused on implementing
46 * the necessary functionality for {@link LightPITModule}s. 50 * the necessary functionality for {@link LightPITModule}s.
57 private LightPITModule.ELProxy moduleInfo = null; 61 private LightPITModule.ELProxy moduleInfo = null;
58 62
59 63
60 @FunctionalInterface 64 @FunctionalInterface
61 private interface HandlerMethod { 65 private interface HandlerMethod {
62 ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException; 66 ResponseType apply(HttpServletRequest request, HttpServletResponse response, DataAccessObjects dao) throws IOException, SQLException;
63 } 67 }
64 68
65 /** 69 /**
66 * Invocation mapping gathered from the {@link RequestMapping} annotations. 70 * Invocation mapping gathered from the {@link RequestMapping} annotations.
67 * <p> 71 * <p>
80 */ 84 */
81 protected final ModuleManager getModuleManager() { 85 protected final ModuleManager getModuleManager() {
82 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); 86 return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME);
83 } 87 }
84 88
85 /** 89
86 * Gives implementing modules access to the {@link DatabaseFacade}. 90 /**
91 * Creates a set of data access objects for the specified connection.
87 * 92 *
88 * @return the database facade 93 * @param connection the SQL connection
89 */ 94 * @return a set of data access objects
90 protected final DatabaseFacade getDatabaseFacade() { 95 */
91 return (DatabaseFacade) getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME); 96 private DataAccessObjects createDataAccessObjects(Connection connection) throws SQLException {
92 } 97 final var df = (DatabaseFacade) getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME);
93 98 switch (df.getSQLDialect()) {
94 private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp) throws IOException { 99 case Postgres:
100 return new PGDataAccessObjects(connection);
101 default:
102 throw new AssertionError("Non-exhaustive switch - this is a bug.");
103 }
104 }
105
106 private ResponseType invokeMapping(Method method, HttpServletRequest req, HttpServletResponse resp, DataAccessObjects dao) throws IOException {
95 try { 107 try {
96 LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName()); 108 LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName());
97 return (ResponseType) method.invoke(this, req, resp); 109 return (ResponseType) method.invoke(this, req, resp, dao);
98 } catch (ReflectiveOperationException | ClassCastException ex) { 110 } catch (ReflectiveOperationException | ClassCastException ex) {
99 LOG.error(String.format("invocation of method %s failed", method.getName()), ex); 111 LOG.error("invocation of method {} failed: {}", method.getName(), ex.getMessage());
112 LOG.debug("Details: ", ex);
100 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 113 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
101 return ResponseType.NONE; 114 return ResponseType.NONE;
102 } 115 }
103 } 116 }
104 117
138 ); 151 );
139 continue; 152 continue;
140 } 153 }
141 154
142 Class<?>[] params = method.getParameterTypes(); 155 Class<?>[] params = method.getParameterTypes();
143 if (params.length == 2 156 if (params.length == 3
144 && HttpServletRequest.class.isAssignableFrom(params[0]) 157 && HttpServletRequest.class.isAssignableFrom(params[0])
145 && HttpServletResponse.class.isAssignableFrom(params[1])) { 158 && HttpServletResponse.class.isAssignableFrom(params[1])
159 && DataAccessObjects.class.isAssignableFrom(params[2])) {
146 160
147 final String requestPath = "/" + mapping.get().requestPath(); 161 final String requestPath = "/" + mapping.get().requestPath();
148 162
149 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). 163 if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()).
150 putIfAbsent(requestPath, 164 putIfAbsent(requestPath,
151 (req, resp) -> invokeMapping(method, req, resp)) != null) { 165 (req, resp, dao) -> invokeMapping(method, req, resp, dao)) != null) {
152 LOG.warn("{} {} has multiple mappings", 166 LOG.warn("{} {} has multiple mappings",
153 mapping.get().method(), 167 mapping.get().method(),
154 mapping.get().requestPath() 168 mapping.get().requestPath()
155 ); 169 );
156 } 170 }
236 default: 250 default:
237 throw new AssertionError("ResponseType switch is not exhaustive - this is a bug!"); 251 throw new AssertionError("ResponseType switch is not exhaustive - this is a bug!");
238 } 252 }
239 } 253 }
240 254
241 private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) 255 private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
242 throws ServletException, IOException {
243 256
244 // choose the requested language as session language (if available) or fall back to english, otherwise 257 // choose the requested language as session language (if available) or fall back to english, otherwise
245 HttpSession session = req.getSession(); 258 HttpSession session = req.getSession();
246 if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) { 259 if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) {
247 Optional<List<String>> availableLanguages = Functions.availableLanguages(getServletContext()).map(Arrays::asList); 260 Optional<List<String>> availableLanguages = Functions.availableLanguages(getServletContext()).map(Arrays::asList);
258 // set some internal request attributes 271 // set some internal request attributes
259 req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req)); 272 req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req));
260 req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); 273 req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName());
261 Optional.ofNullable(moduleInfo).ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); 274 Optional.ofNullable(moduleInfo).ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy));
262 275
263 276 // obtain a connection and create the data access objects
264 // call the handler, if available, or send an HTTP 404 error 277 final var db = (DatabaseFacade) req.getServletContext().getAttribute(DatabaseFacade.SC_ATTR_NAME);
265 Optional<HandlerMethod> mapping = findMapping(method, req); 278 try (final var connection = db.getDataSource().getConnection()) {
266 if (mapping.isPresent()) { 279 final var dao = createDataAccessObjects(connection);
267 forwardAsSpecified(mapping.get().apply(req, resp), req, resp); 280 // call the handler, if available, or send an HTTP 404 error
268 } else { 281 final var mapping = findMapping(method, req);
269 resp.sendError(HttpServletResponse.SC_NOT_FOUND); 282 if (mapping.isPresent()) {
283 forwardAsSpecified(mapping.get().apply(req, resp, dao), req, resp);
284 } else {
285 resp.sendError(HttpServletResponse.SC_NOT_FOUND);
286 }
287 } catch (SQLException ex) {
288 LOG.error("Database exception (Code {}): {}", ex.getErrorCode(), ex.getMessage());
289 LOG.debug("Details: ", ex);
290 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Database Error - Code:" + ex.getErrorCode());
270 } 291 }
271 } 292 }
272 293
273 @Override 294 @Override
274 protected final void doGet(HttpServletRequest req, HttpServletResponse resp) 295 protected final void doGet(HttpServletRequest req, HttpServletResponse resp)

mercurial