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.DaoProvider; |
31 import de.uapcore.lightpit.dao.DataAccessObject; |
32 import de.uapcore.lightpit.dao.postgres.PGDaoProvider; |
32 import de.uapcore.lightpit.dao.PostgresDataAccessObject; |
33 import org.slf4j.Logger; |
33 import org.slf4j.Logger; |
34 import org.slf4j.LoggerFactory; |
34 import org.slf4j.LoggerFactory; |
35 |
35 |
36 import javax.servlet.ServletException; |
36 import javax.servlet.ServletException; |
37 import javax.servlet.http.HttpServlet; |
37 import javax.servlet.http.HttpServlet; |
54 |
54 |
55 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); |
55 private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); |
56 |
56 |
57 private static final String SITE_JSP = jspPath("site"); |
57 private static final String SITE_JSP = jspPath("site"); |
58 |
58 |
59 |
|
60 @FunctionalInterface |
|
61 protected interface SQLFindFunction<K, T> { |
|
62 T apply(K key) throws SQLException; |
|
63 |
|
64 default <V> SQLFindFunction<V, T> compose(Function<? super V, ? extends K> before) throws SQLException { |
|
65 Objects.requireNonNull(before); |
|
66 return (v) -> this.apply(before.apply(v)); |
|
67 } |
|
68 |
|
69 default <V> SQLFindFunction<K, V> andThen(Function<? super T, ? extends V> after) throws SQLException { |
|
70 Objects.requireNonNull(after); |
|
71 return (t) -> after.apply(this.apply(t)); |
|
72 } |
|
73 |
|
74 static <K> Function<K, K> identity() { |
|
75 return (t) -> t; |
|
76 } |
|
77 } |
|
78 |
|
79 /** |
59 /** |
80 * Invocation mapping gathered from the {@link RequestMapping} annotations. |
60 * Invocation mapping gathered from the {@link RequestMapping} annotations. |
81 * <p> |
61 * <p> |
82 * Paths in this map must always start with a leading slash, although |
62 * Paths in this map must always start with a leading slash, although |
83 * the specification in the annotation must not start with a leading slash. |
63 * the specification in the annotation must not start with a leading slash. |
99 * Creates a set of data access objects for the specified connection. |
79 * Creates a set of data access objects for the specified connection. |
100 * |
80 * |
101 * @param connection the SQL connection |
81 * @param connection the SQL connection |
102 * @return a set of data access objects |
82 * @return a set of data access objects |
103 */ |
83 */ |
104 private DaoProvider createDataAccessObjects(Connection connection) throws SQLException { |
84 private DataAccessObject createDataAccessObjects(Connection connection) { |
105 final var df = (DataSourceProvider) getServletContext().getAttribute(DataSourceProvider.Companion.getSC_ATTR_NAME()); |
85 final var df = (DataSourceProvider) getServletContext().getAttribute(DataSourceProvider.Companion.getSC_ATTR_NAME()); |
106 if (df.getDialect() == DataSourceProvider.Dialect.Postgres) { |
86 if (df.getDialect() == DataSourceProvider.Dialect.Postgres) { |
107 return new PGDaoProvider(connection); |
87 return new PostgresDataAccessObject(connection); |
108 } |
88 } |
109 throw new UnsupportedOperationException("Non-exhaustive if-else - this is a bug."); |
89 throw new UnsupportedOperationException("Non-exhaustive if-else - this is a bug."); |
110 } |
90 } |
111 |
91 |
112 private void invokeMapping(Map.Entry<PathPattern, Method> mapping, HttpServletRequest req, HttpServletResponse resp, DaoProvider dao) throws IOException { |
92 private void invokeMapping(Map.Entry<PathPattern, Method> mapping, HttpServletRequest req, HttpServletResponse resp, DataAccessObject dao) throws IOException { |
113 final var pathPattern = mapping.getKey(); |
93 final var pathPattern = mapping.getKey(); |
114 final var method = mapping.getValue(); |
94 final var method = mapping.getValue(); |
115 try { |
95 try { |
116 LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName()); |
96 LOG.trace("invoke {}#{}", method.getDeclaringClass().getName(), method.getName()); |
117 final var paramTypes = method.getParameterTypes(); |
97 final var paramTypes = method.getParameterTypes(); |
120 if (paramTypes[i].isAssignableFrom(HttpServletRequest.class)) { |
100 if (paramTypes[i].isAssignableFrom(HttpServletRequest.class)) { |
121 paramValues[i] = req; |
101 paramValues[i] = req; |
122 } else if (paramTypes[i].isAssignableFrom(HttpServletResponse.class)) { |
102 } else if (paramTypes[i].isAssignableFrom(HttpServletResponse.class)) { |
123 paramValues[i] = resp; |
103 paramValues[i] = resp; |
124 } |
104 } |
125 if (paramTypes[i].isAssignableFrom(DaoProvider.class)) { |
105 if (paramTypes[i].isAssignableFrom(DataAccessObject.class)) { |
126 paramValues[i] = dao; |
106 paramValues[i] = dao; |
127 } |
107 } |
128 if (paramTypes[i].isAssignableFrom(PathParameters.class)) { |
108 if (paramTypes[i].isAssignableFrom(PathParameters.class)) { |
129 paramValues[i] = pathPattern.obtainPathParameters(sanitizeRequestPath(req)); |
109 paramValues[i] = pathPattern.obtainPathParameters(sanitizeRequestPath(req)); |
130 } |
110 } |
177 } |
157 } |
178 |
158 |
179 boolean paramsInjectible = true; |
159 boolean paramsInjectible = true; |
180 for (var param : method.getParameterTypes()) { |
160 for (var param : method.getParameterTypes()) { |
181 paramsInjectible &= HttpServletRequest.class.isAssignableFrom(param) |
161 paramsInjectible &= HttpServletRequest.class.isAssignableFrom(param) |
182 || HttpServletResponse.class.isAssignableFrom(param) |
162 || HttpServletResponse.class.isAssignableFrom(param) |
183 || PathParameters.class.isAssignableFrom(param) |
163 || PathParameters.class.isAssignableFrom(param) |
184 || DaoProvider.class.isAssignableFrom(param); |
164 || DataAccessObject.class.isAssignableFrom(param); |
185 } |
165 } |
186 if (paramsInjectible) { |
166 if (paramsInjectible) { |
187 try { |
167 try { |
188 PathPattern pathPattern = new PathPattern(mapping.get().requestPath()); |
168 PathPattern pathPattern = new PathPattern(mapping.get().requestPath()); |
189 |
169 |
358 * @param <T> the type of the request parameter |
338 * @param <T> the type of the request parameter |
359 * @param <R> the type of the looked up entity |
339 * @param <R> the type of the looked up entity |
360 * @return the retrieved entity or an empty optional if there is no such entity or the request parameter was missing |
340 * @return the retrieved entity or an empty optional if there is no such entity or the request parameter was missing |
361 * @throws SQLException if the find function throws an exception |
341 * @throws SQLException if the find function throws an exception |
362 */ |
342 */ |
363 protected <T, R> Optional<R> findByParameter(HttpServletRequest req, Class<T> clazz, String name, SQLFindFunction<? super T, ? extends R> find) throws SQLException { |
343 protected <T, R> Optional<R> findByParameter(HttpServletRequest req, Class<T> clazz, String name, Function<? super T, ? extends R> find) { |
364 final var param = getParameter(req, clazz, name); |
344 final var param = getParameter(req, clazz, name); |
365 if (param.isPresent()) { |
345 if (param.isPresent()) { |
366 return Optional.ofNullable(find.apply(param.get())); |
346 return Optional.ofNullable(find.apply(param.get())); |
367 } else { |
347 } else { |
368 return Optional.empty(); |
348 return Optional.empty(); |