Skip to content

Commit d2cb3e0

Browse files
committed
refactor: splits modern and compat Graal implementations.
1 parent df6a332 commit d2cb3e0

File tree

9 files changed

+337
-120
lines changed

9 files changed

+337
-120
lines changed

core/api/src/main/java/io/gatehill/imposter/script/ExecutionContext.kt

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,6 @@ class ExecutionContext(
5858
put("request", request)
5959
}
6060

61-
private val request: Request
62-
get() = get("request") as Request
63-
6461
override fun get(key: String): Any? {
6562
// legacy properties
6663
if (key == "params") {
@@ -77,63 +74,100 @@ class ExecutionContext(
7774
return super.get(key)
7875
}
7976

77+
interface Request {
78+
val path: String
79+
val method: String
80+
val uri: String
81+
val headers: Map<String, String>
82+
83+
/**
84+
* @return the request path parameters
85+
*/
86+
val pathParams: Map<String, String>
87+
88+
/**
89+
* @return the request query parameters
90+
*/
91+
val queryParams: Map<String, String>
92+
93+
/**
94+
* @return the request form parameters
95+
*/
96+
val formParams: Map<String, String>
97+
98+
/**
99+
* @return the request body
100+
*/
101+
val body: String?
102+
103+
/**
104+
* @return the [headers] map, but with all keys in lowercase
105+
*/
106+
val normalisedHeaders: Map<String, String>
107+
108+
/**
109+
* Legacy property removed.
110+
*/
111+
@get:Deprecated("Use queryParams instead.", ReplaceWith("queryParams"))
112+
val params: Map<String, String>
113+
}
114+
80115
/**
81116
* Representation of the request, supporting lazily-initialised collections for params and headers.
82117
*/
83-
class Request(
118+
class RequestImpl(
119+
override val path: String,
120+
override val method: String,
121+
override val uri: String,
84122
private val headersSupplier: Supplier<Map<String, String>>,
85123
private val pathParamsSupplier: Supplier<Map<String, String>>,
86124
private val queryParamsSupplier: Supplier<Map<String, String>>,
87125
private val formParamsSupplier: Supplier<Map<String, String>>,
88126
private val bodySupplier: Supplier<String?>,
89-
) {
90-
lateinit var path: String
91-
lateinit var method: String
92-
lateinit var uri: String
93-
94-
val headers: Map<String, String> by lazy {
127+
) : Request {
128+
override val headers: Map<String, String> by lazy {
95129
headersSupplier.get()
96130
}
97131

98132
/**
99133
* @return the request path parameters
100134
*/
101-
val pathParams: Map<String, String> by lazy {
102-
pathParamsSupplier.get()
135+
override val pathParams: Map<String, String> get() {
136+
return pathParamsSupplier.get()
103137
}
104138

105139
/**
106140
* @return the request query parameters
107141
*/
108-
val queryParams: Map<String, String> by lazy {
142+
override val queryParams: Map<String, String> by lazy {
109143
queryParamsSupplier.get()
110144
}
111145

112146
/**
113147
* @return the request form parameters
114148
*/
115-
val formParams: Map<String, String> by lazy {
149+
override val formParams: Map<String, String> by lazy {
116150
formParamsSupplier.get()
117151
}
118152

119153
/**
120154
* @return the request body
121155
*/
122-
val body: String? by lazy {
156+
override val body: String? by lazy {
123157
bodySupplier.get()
124158
}
125159

126160
/**
127161
* @return the [headers] map, but with all keys in lowercase
128162
*/
129-
val normalisedHeaders: Map<String, String>
163+
override val normalisedHeaders: Map<String, String>
130164
get() = CollectionUtil.convertKeysToLowerCase(headers)
131165

132166
/**
133167
* Legacy property removed.
134168
*/
135169
@get:Deprecated("Use queryParams instead.", ReplaceWith("queryParams"))
136-
val params: Map<String, String>
170+
override val params: Map<String, String>
137171
get() = throw UnsupportedOperationException(
138172
"Error: the deprecated 'context.request.params' property was removed. Use 'context.request.queryParams' or 'context.request.pathParams' instead."
139173
)

core/api/src/main/java/io/gatehill/imposter/script/RuntimeContext.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,4 @@ class RuntimeContext(
7070
additionalBindings?.let(bindings::putAll)
7171
return bindings
7272
}
73-
}
73+
}

core/engine/src/main/java/io/gatehill/imposter/script/ScriptUtil.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,16 @@ object ScriptUtil {
9797
}
9898

9999
// request information
100-
val request = ExecutionContext.Request(
100+
val request = ExecutionContext.RequestImpl(
101+
path = internalRequest.path,
102+
method = internalRequest.method.name,
103+
uri = internalRequest.absoluteUri,
101104
headersSupplier,
102105
pathParamsSupplier,
103106
queryParamsSupplier,
104107
formParamsSupplier,
105108
bodySupplier
106109
)
107-
request.path = internalRequest.path
108-
request.method = internalRequest.method.name
109-
request.uri = internalRequest.absoluteUri
110110

111111
// root context
112112
val executionContext = ExecutionContext(request)

scripting/common/src/main/java/io/gatehill/imposter/scripting/common/util/JavaScriptUtil.kt

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ package io.gatehill.imposter.scripting.common.util
4545

4646
import io.gatehill.imposter.config.util.EnvVars
4747
import io.gatehill.imposter.script.RuntimeContext
48-
import io.gatehill.imposter.scripting.common.dsl.RunnableDsl
48+
import io.gatehill.imposter.script.dsl.DslImpl
4949
import io.gatehill.imposter.scripting.common.shim.ConsoleShim
5050
import io.gatehill.imposter.service.ScriptSource
5151
import org.apache.logging.log4j.LogManager
@@ -70,21 +70,12 @@ object JavaScriptUtil {
7070
"stores",
7171
)
7272

73-
private val DSL_FUNCTIONS: String
7473
private val GLOBAL_DSL_OBJECTS: String
7574

7675
init {
77-
// expose superclass methods as DSL functions
78-
val dslMethods = listOf(
79-
"respond",
80-
)
81-
DSL_FUNCTIONS = dslMethods.distinct().joinToString("\r\n") { methodName ->
82-
"${DSL_OBJECT_PREFIX}${methodName} = Java.super(__dsl).${methodName};"
83-
}
84-
8576
// optionally expose as global objects
8677
GLOBAL_DSL_OBJECTS = globals.distinct().joinToString("\r\n") { methodName ->
87-
"$methodName = ${DSL_OBJECT_PREFIX}${methodName};"
78+
"var $methodName = ${DSL_OBJECT_PREFIX}${methodName};"
8879
}
8980
}
9081

@@ -128,15 +119,13 @@ object JavaScriptUtil {
128119

129120
private fun buildWrappedScript(script: String, setGlobalDslObjects: Boolean): WrappedScript {
130121
val preScript = """
131-
var RunnableDsl = Java.type('${RunnableDsl::class.java.canonicalName}');
132-
133-
var __dsl = new RunnableDsl() {
134-
run: function() {
122+
var DslImpl = Java.type('${DslImpl::class.java.canonicalName}');
123+
var __dsl = new DslImpl();
135124
136125
/* ------------------------------------------------------------------------- */
137126
/* DSL functions */
138127
/* ------------------------------------------------------------------------- */
139-
$DSL_FUNCTIONS
128+
var ${DSL_OBJECT_PREFIX}respond = function() { return __dsl.respond(); }
140129
${if (setGlobalDslObjects) GLOBAL_DSL_OBJECTS else ""}
141130
/* ------------------------------------------------------------------------- */
142131
/* Shim for '__imposter_types' module exports */
@@ -160,14 +149,13 @@ function require(moduleName) {
160149
/* ------------------------------------------------------------------------- */
161150
/* Mock script */
162151
/* ------------------------------------------------------------------------- */
152+
function __run() {
163153
"""
164154
val postScript = """
165-
/* ------------------------------------------------------------------------- */
166-
167-
}
168155
}
156+
/* ------------------------------------------------------------------------- */
169157
170-
__dsl.run();
158+
__run();
171159
__dsl;
172160
"""
173161
return WrappedScript(preScript.lines().size, preScript + script + postScript)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright (c) 2024.
3+
*
4+
* This file is part of Imposter.
5+
*
6+
* "Commons Clause" License Condition v1.0
7+
*
8+
* The Software is provided to you by the Licensor under the License, as
9+
* defined below, subject to the following condition.
10+
*
11+
* Without limiting other conditions in the License, the grant of rights
12+
* under the License will not include, and the License does not grant to
13+
* you, the right to Sell the Software.
14+
*
15+
* For purposes of the foregoing, "Sell" means practicing any or all of
16+
* the rights granted to you under the License to provide to third parties,
17+
* for a fee or other consideration (including without limitation fees for
18+
* hosting or consulting/support services related to the Software), a
19+
* product or service whose value derives, entirely or substantially, from
20+
* the functionality of the Software. Any license notice or attribution
21+
* required by the License must also include this Commons Clause License
22+
* Condition notice.
23+
*
24+
* Software: Imposter
25+
*
26+
* License: GNU Lesser General Public License version 3
27+
*
28+
* Licensor: Peter Cornish
29+
*
30+
* Imposter is free software: you can redistribute it and/or modify
31+
* it under the terms of the GNU Lesser General Public License as published by
32+
* the Free Software Foundation, either version 3 of the License, or
33+
* (at your option) any later version.
34+
*
35+
* Imposter is distributed in the hope that it will be useful,
36+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
37+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38+
* GNU Lesser General Public License for more details.
39+
*
40+
* You should have received a copy of the GNU Lesser General Public License
41+
* along with Imposter. If not, see <https://www.gnu.org/licenses/>.
42+
*/
43+
44+
package io.gatehill.imposter.scripting.graalvm
45+
46+
import io.gatehill.imposter.script.ExecutionContext
47+
import org.graalvm.polyglot.Value
48+
import org.graalvm.polyglot.proxy.ProxyObject
49+
50+
/**
51+
* Graal polyglot object proxy for [ExecutionContext.Request].
52+
*/
53+
class RequestProxy(
54+
private val request: ExecutionContext.Request
55+
) : ProxyObject {
56+
private val properties = arrayOf(
57+
"path",
58+
"method",
59+
"uri",
60+
"headers",
61+
"pathParams",
62+
"queryParams",
63+
"formParams",
64+
"body",
65+
"normalisedHeaders",
66+
)
67+
68+
override fun getMember(key: String?): Any? = when (key) {
69+
"path" -> request.path
70+
"method" -> request.method
71+
"uri" -> request.uri
72+
"headers" -> MapObjectProxy(request.headers)
73+
"pathParams" -> MapObjectProxy(request.pathParams)
74+
"queryParams" -> MapObjectProxy(request.queryParams)
75+
"formParams" -> MapObjectProxy(request.formParams)
76+
"body" -> request.body
77+
"normalisedHeaders" -> MapObjectProxy(request.normalisedHeaders)
78+
else -> null
79+
}
80+
81+
override fun getMemberKeys(): Array<*> = properties
82+
83+
override fun hasMember(key: String?) =
84+
key?.let { properties.contains(key) } ?: false
85+
86+
override fun putMember(key: String?, value: Value?) {
87+
throw UnsupportedOperationException("Request cannot be modified")
88+
}
89+
}
90+
91+
class MapObjectProxy(private val orig: Map<String, *>): ProxyObject {
92+
override fun getMember(key: String?): Any? {
93+
return key?.let { orig[key] }
94+
}
95+
96+
override fun getMemberKeys(): Any {
97+
return orig.keys.toTypedArray()
98+
}
99+
100+
override fun hasMember(key: String?): Boolean {
101+
return key?.let { orig.containsKey(key) } ?: false
102+
}
103+
104+
override fun putMember(key: String?, value: Value?) {
105+
throw UnsupportedOperationException("Object is unmodifiable")
106+
}
107+
}

0 commit comments

Comments
 (0)