23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 */ |
24 */ |
25 |
25 |
26 package de.uapcore.lightpit |
26 package de.uapcore.lightpit |
27 |
27 |
|
28 import de.uapcore.lightpit.dao.DataAccessObject |
|
29 import de.uapcore.lightpit.viewmodel.NavMenu |
|
30 import de.uapcore.lightpit.viewmodel.View |
|
31 import javax.servlet.http.HttpServletRequest |
|
32 import javax.servlet.http.HttpServletResponse |
|
33 import javax.servlet.http.HttpSession |
28 import kotlin.math.min |
34 import kotlin.math.min |
29 |
35 |
30 /** |
36 typealias MappingMethod = (HttpRequest, DataAccessObject) -> Unit |
31 * Maps requests to methods. |
37 typealias PathParameters = Map<String, String> |
32 * |
38 |
33 * This annotation is used to annotate methods within classes which |
39 class HttpRequest( |
34 * override [AbstractServlet]. |
40 val request: HttpServletRequest, |
35 */ |
41 val response: HttpServletResponse, |
36 @MustBeDocumented |
42 val pathParams: PathParameters = emptyMap() |
37 @Retention(AnnotationRetention.RUNTIME) |
43 ) { |
38 @Target(AnnotationTarget.FUNCTION) |
44 val session: HttpSession = request.session |
39 annotation class RequestMapping( |
45 |
40 |
46 val remoteUser: String? = request.remoteUser |
41 /** |
47 |
42 * Specifies the HTTP method. |
48 /** |
43 * |
49 * The name of the content page. |
44 * @return the HTTP method handled by the annotated Java method |
50 * |
45 */ |
51 * @see Constants#REQ_ATTR_CONTENT_PAGE |
46 val method: HttpMethod, |
52 */ |
47 |
53 var contentPage = "" |
48 /** |
54 set(value) { |
49 * Specifies the request path relative to the module path. |
55 field = value |
50 * The trailing slash is important. |
56 request.setAttribute(Constants.REQ_ATTR_CONTENT_PAGE, jspPath(value)) |
51 * A node may start with a dollar ($) sign. |
57 } |
52 * This part of the path is then treated as an path parameter. |
58 |
53 * Path parameters can be obtained by including the [PathParameters] type in the signature. |
59 /** |
54 * |
60 * A list of additional style sheets. |
55 * @return the request path the annotated method should handle |
61 * |
56 */ |
62 * @see Constants#REQ_ATTR_STYLESHEET |
57 val requestPath: String = "/" |
63 */ |
58 ) |
64 var styleSheets = emptyList<String>() |
59 |
65 set(value) { |
60 class PathParameters : HashMap<String, String>() |
66 field = value |
|
67 request.setAttribute(Constants.REQ_ATTR_STYLESHEET, |
|
68 value.map { it.withExt(".css") } |
|
69 ) |
|
70 } |
|
71 |
|
72 /** |
|
73 * The name of the navigation menu JSP. |
|
74 * |
|
75 * @see Constants#REQ_ATTR_NAVIGATION |
|
76 */ |
|
77 var navigationMenu: NavMenu? = null |
|
78 set(value) { |
|
79 field = value |
|
80 request.setAttribute(Constants.REQ_ATTR_NAVIGATION, navigationMenu) |
|
81 } |
|
82 |
|
83 var redirectLocation = "" |
|
84 set(value) { |
|
85 field = value |
|
86 request.setAttribute(Constants.REQ_ATTR_REDIRECT_LOCATION, baseHref + value) |
|
87 } |
|
88 |
|
89 /** |
|
90 * The view object. |
|
91 * |
|
92 * @see Constants#REQ_ATTR_VIEWMODEL |
|
93 */ |
|
94 var view: View? = null |
|
95 set(value) { |
|
96 field = value |
|
97 request.setAttribute(Constants.REQ_ATTR_VIEWMODEL, value) |
|
98 } |
|
99 |
|
100 /** |
|
101 * The base path of this application. |
|
102 */ |
|
103 val baseHref get() = "${request.scheme}://${request.serverName}:${request.serverPort}${request.contextPath}/" |
|
104 |
|
105 private fun String.withExt(ext: String) = if (endsWith(ext)) this else plus(ext) |
|
106 private fun jspPath(name: String) = Constants.JSP_PATH_PREFIX.plus(name).withExt(".jsp") |
|
107 |
|
108 fun param(name: String): String? = request.getParameter(name) |
|
109 fun paramArray(name: String): Array<String> = request.getParameterValues(name) ?: emptyArray() |
|
110 |
|
111 fun render(page: String? = null) { |
|
112 page?.let { contentPage = it } |
|
113 request.getRequestDispatcher(jspPath("site")).forward(request, response) |
|
114 } |
|
115 |
|
116 fun renderCommit(location: String? = null) { |
|
117 location?.let { redirectLocation = it } |
|
118 contentPage = Constants.JSP_COMMIT_SUCCESSFUL |
|
119 render() |
|
120 } |
|
121 } |
61 |
122 |
62 /** |
123 /** |
63 * A path pattern optionally containing placeholders. |
124 * A path pattern optionally containing placeholders. |
64 * |
125 * |
65 * The special directories . and .. are disallowed in the pattern. |
126 * The special directories . and .. are disallowed in the pattern. |
66 * Placeholders start with a $ sign. |
127 * Placeholders start with a % sign. |
67 * |
128 * |
68 * @param pattern the pattern |
129 * @param pattern the pattern |
69 */ |
130 */ |
70 class PathPattern(pattern: String) { |
131 class PathPattern(pattern: String) { |
71 private val nodePatterns: List<String> |
132 private val nodePatterns: List<String> |
104 * @param path the path |
165 * @param path the path |
105 * @return the path parameters, if any, or an empty map |
166 * @return the path parameters, if any, or an empty map |
106 * @see .matches |
167 * @see .matches |
107 */ |
168 */ |
108 fun obtainPathParameters(path: String): PathParameters { |
169 fun obtainPathParameters(path: String): PathParameters { |
109 val params = PathParameters() |
170 val params = mutableMapOf<String, String>() |
110 val nodes = parse(path) |
171 val nodes = parse(path) |
111 for (i in 0 until min(nodes.size, nodePatterns.size)) { |
172 for (i in 0 until min(nodes.size, nodePatterns.size)) { |
112 val pattern = nodePatterns[i] |
173 val pattern = nodePatterns[i] |
113 val node = nodes[i] |
174 val node = nodes[i] |
114 if (pattern.startsWith("$")) { |
175 if (pattern.startsWith("%")) { |
115 params[pattern.substring(1)] = node |
176 params[pattern.substring(1)] = node |
116 } |
177 } |
117 } |
178 } |
118 return params |
179 return params |
119 } |
180 } |
120 |
181 |
121 override fun hashCode(): Int { |
182 override fun hashCode(): Int { |
122 val str = StringBuilder() |
183 val str = StringBuilder() |
123 for (node in nodePatterns) { |
184 for (node in nodePatterns) { |
124 if (node.startsWith("$")) { |
185 if (node.startsWith("%")) { |
125 str.append("/$") |
186 str.append("/%") |
126 } else { |
187 } else { |
127 str.append('/') |
188 str.append('/') |
128 str.append(node) |
189 str.append(node) |
129 } |
190 } |
130 } |
191 } |