Skip to content

Commit b709e3e

Browse files
authored
Java fixes, cursor methods and tests (#373)
* [java-shell] support explain * [java-shell] support db.createCollection() * [java-shell] UUID function should create real UUID instead of raw binary * [java-shell] test db.serverStatus() * [java-shell] support db.coll.find().explain() * [java-shell] support explain aggregate * [java-shell] update graaljs library * [java-shell] support all bulkWrite options * [java-shell] delete redundant test files * [java-shell] support Cursor.readPref * [java-shell] drop collections before createCollection test * [java-shell] clear resources on cursor and shell close. Do not close mongo client
1 parent 66ba800 commit b709e3e

39 files changed

+557
-163
lines changed

packages/java-shell/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ compileJava.dependsOn browserifyShellApi
2828
dependencies {
2929
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
3030
testCompile group: 'junit', name: 'junit', version: '4.12'
31-
compile group: 'org.graalvm.js', name: 'js', version: '20.0.0'
31+
compile group: 'org.graalvm.js', name: 'js', version: '20.2.0'
3232
compile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.12.7'
3333
compile group: 'org.apache.commons', name: 'commons-text', version: '1.8'
3434
}

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/MongoShell.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ package com.mongodb.mongosh
22

33
import com.mongodb.client.MongoClient
44
import com.mongodb.mongosh.result.MongoShellResult
5-
import java.io.Closeable
65

7-
class MongoShell(client: MongoClient) : Closeable {
6+
class MongoShell(client: MongoClient) {
87
private val context = MongoShellContext(client)
98

109
fun eval(script: String): MongoShellResult<*> {
@@ -14,5 +13,5 @@ class MongoShell(client: MongoClient) : Closeable {
1413
return context.extract(value, if (type.isString) type.asString() else null)
1514
}
1615

17-
override fun close() = context.close()
16+
fun close() = context.close()
1817
}

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/MongoShellContext.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import org.graalvm.polyglot.Source
1616
import org.graalvm.polyglot.Value
1717
import org.graalvm.polyglot.proxy.ProxyExecutable
1818
import org.intellij.lang.annotations.Language
19-
import java.io.Closeable
19+
import java.lang.IllegalStateException
2020
import java.time.LocalDateTime
2121
import java.time.ZoneOffset
2222
import java.time.format.DateTimeFormatter
@@ -31,8 +31,8 @@ import java.util.concurrent.ExecutionException
3131
import java.util.concurrent.TimeUnit
3232
import java.util.regex.Pattern
3333

34-
internal class MongoShellContext(client: MongoClient) : Closeable {
35-
val ctx: Context = Context.create()
34+
internal class MongoShellContext(client: MongoClient) {
35+
private var ctx: Context? = Context.create()
3636
private val serviceProvider = JavaServiceProvider(client, this)
3737
private val shellEvaluator: Value
3838
private val bsonTypes: BsonTypes
@@ -44,6 +44,7 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
4444
init {
4545
val setupScript = MongoShell::class.java.getResource("/js/all-standalone.js").readText()
4646
evalInner(setupScript, "all-standalone.js")
47+
val ctx = checkClosed()
4748
val context = ctx.getBindings("js")
4849
val global = context["_global"]!!
4950
context.removeMember("_global")
@@ -76,6 +77,7 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
7677
context.putMember("Date", date)
7778
val isoDate = functionProducer.execute(ProxyExecutable { args -> dateHelper(true, args.toList()) })
7879
context.putMember("ISODate", isoDate)
80+
context.putMember("UUID", functionProducer.execute(ProxyExecutable { args -> if (args.isEmpty()) UUID.randomUUID() else UUID.fromString(args[0].asString()) }))
7981
}
8082

8183
private fun dateHelper(createObject: Boolean, args: List<Value>): Any {
@@ -124,9 +126,14 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
124126
}
125127

126128
operator fun get(value: String): Value? {
129+
val ctx = checkClosed()
127130
return ctx.getBindings("js")[value]
128131
}
129132

133+
private fun checkClosed(): Context {
134+
return this.ctx ?: throw IllegalStateException("Context has already been closed")
135+
}
136+
130137
internal operator fun Value.get(identifier: String): Value? {
131138
return getMember(identifier)
132139
}
@@ -158,7 +165,8 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
158165
v.instanceOf("Promise") -> extract(unwrapPromise(v))
159166
type == "Help" -> extract(v["attr"]!!)
160167
type == "Cursor" -> FindCursorResult(FindCursor<Any?>(v, this))
161-
type == "AggregationCursor" -> AggregationCursorResult(AggregationCursor<Any?>(v, this))
168+
// document with aggregation explain result also has type AggregationCursor, so we need to make sure that value contains cursor
169+
type == "AggregationCursor" && v.hasMember("_cursor") -> AggregationCursorResult(AggregationCursor<Any?>(v, this))
162170
type == "InsertOneResult" -> InsertOneResult(v["acknowledged"]!!.asBoolean(), v["insertedId"]!!.asString())
163171
type == "DeleteResult" -> DeleteResult(v["acknowledged"]!!.asBoolean(), v["deletedCount"]!!.asLong())
164172
type == "UpdateResult" -> {
@@ -240,6 +248,7 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
240248
}
241249

242250
private fun evalInner(@Language("js") script: String, name: String = "Unnamed"): Value {
251+
val ctx = checkClosed()
243252
return ctx.eval(Source.newBuilder("js", script, name).buildLiteral())
244253
}
245254

@@ -257,7 +266,11 @@ internal class MongoShellContext(client: MongoClient) : Closeable {
257266
}
258267
}
259268

260-
override fun close() = serviceProvider.close()
269+
fun close() {
270+
val ctx = checkClosed()
271+
ctx.close(true)
272+
this.ctx = null
273+
}
261274

262275
private fun Value.instanceOf(clazz: Value?): Boolean {
263276
return clazz != null && evalInner("(o, clazz) => o instanceof clazz", "instance_of_class_script").execute(this, clazz).asBoolean()
Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,36 @@
11
package com.mongodb.mongosh.result
22

33
import com.mongodb.mongosh.MongoShellContext
4-
import org.bson.Document
54
import org.graalvm.polyglot.Value
65

7-
class AggregationCursor<out T> internal constructor(private val cursor: Value, private val context: MongoShellContext) : Cursor<T> {
6+
class AggregationCursor<out T> internal constructor(private var cursor: Value?, private var context: MongoShellContext?) : Cursor<T> {
87
override fun hasNext(): Boolean {
8+
val (cursor, _) = checkClosed()
99
return cursor.invokeMember("hasNext").asBoolean()
1010
}
1111

1212
override fun next(): T {
13+
val (cursor, context) = checkClosed()
1314
if (!hasNext()) throw NoSuchElementException()
1415
return context.extract(cursor.invokeMember("next")).value as T
1516
}
1617

1718
override fun _asPrintable(): String {
19+
val (cursor, context) = checkClosed()
1820
return context.extract(cursor.invokeMember("_asPrintable"))._asPrintable()
1921
}
22+
23+
override fun close() {
24+
val (c, _) = checkClosed()
25+
c.invokeMember("close")
26+
cursor = null
27+
context = null
28+
}
29+
30+
private fun checkClosed(): Pair<Value, MongoShellContext> {
31+
val cursor = this.cursor
32+
val context = this.context
33+
if (cursor == null || context == null) throw IllegalStateException("Cursor has already been closed")
34+
return cursor to context
35+
}
2036
}

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/result/Cursor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ package com.mongodb.mongosh.result
22

33
interface Cursor<out T> : Iterator<T> {
44
fun _asPrintable(): String
5+
fun close()
56
}
Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,41 @@
11
package com.mongodb.mongosh.result
22

33
import com.mongodb.mongosh.MongoShellContext
4-
import org.bson.Document
54
import org.graalvm.polyglot.Value
65

7-
class FindCursor<out T> internal constructor(private val cursor: Value, private val context: MongoShellContext) : Cursor<T> {
6+
class FindCursor<out T> internal constructor(private var cursor: Value?, private var context: MongoShellContext?) : Cursor<T> {
87
override fun hasNext(): Boolean {
8+
val (cursor, _) = checkClosed()
99
return cursor.invokeMember("hasNext").asBoolean()
1010
}
1111

1212
override fun next(): T {
13+
val (cursor, context) = checkClosed()
1314
if (!hasNext()) throw NoSuchElementException()
1415
return context.extract(cursor.invokeMember("next")).value as T
1516
}
1617

1718
fun batchSize(size: Int) {
19+
val (cursor, _) = checkClosed()
1820
cursor.invokeMember("batchSize", size)
1921
}
2022

2123
override fun _asPrintable(): String {
24+
val (cursor, context) = checkClosed()
2225
return context.extract(cursor.invokeMember("_asPrintable"))._asPrintable()
2326
}
27+
28+
override fun close() {
29+
val (c, _) = checkClosed()
30+
c.invokeMember("close")
31+
cursor = null
32+
context = null
33+
}
34+
35+
private fun checkClosed(): Pair<Value, MongoShellContext> {
36+
val cursor = this.cursor
37+
val context = this.context
38+
if (cursor == null || context == null) throw IllegalStateException("Cursor has already been closed")
39+
return cursor to context
40+
}
2441
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.mongodb.mongosh.service
2+
3+
import org.graalvm.polyglot.Value
4+
5+
/**
6+
* see service-provider-core/src/admin.ts
7+
*/
8+
internal interface AdminServiceProvider {
9+
fun createCollection(database: String, collection: String, options: Value?): Value
10+
}

packages/java-shell/src/main/kotlin/com/mongodb/mongosh/service/Cursor.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.mongodb.mongosh.service
22

33
import com.mongodb.client.MongoCursor
4-
import com.mongodb.client.model.Collation
54
import com.mongodb.mongosh.MongoShellContext
65
import com.mongodb.mongosh.result.DocumentResult
76
import org.bson.Document
@@ -56,7 +55,7 @@ internal class Cursor(private var helper: MongoIterableHelper<*>, private val co
5655
@HostAccess.Export
5756
override fun close() {
5857
closed = true
59-
getOrCreateIterator().close()
58+
iterator?.close()
6059
}
6160

6261
@HostAccess.Export
@@ -76,9 +75,7 @@ internal class Cursor(private var helper: MongoIterableHelper<*>, private val co
7675
if (!v.hasMembers()) {
7776
throw IllegalArgumentException("Expected one argument of type object. Got: $v")
7877
}
79-
val collation = convert(Collation.builder(), collationConverters, collationDefaultConverter, toDocument(context, v))
80-
.getOrThrow()
81-
.build()
78+
val collation = toDocument(context, v)
8279
helper.collation(collation)
8380
return this
8481
}
@@ -97,8 +94,9 @@ internal class Cursor(private var helper: MongoIterableHelper<*>, private val co
9794
}
9895

9996
@HostAccess.Export
100-
override fun explain(verbosity: String) {
101-
throw NotImplementedError("explain is not supported")
97+
override fun explain(verbosity: String?): Any? {
98+
checkQueryNotExecuted()
99+
return context.toJs(helper.explain(verbosity))
102100
}
103101

104102
@HostAccess.Export
@@ -205,9 +203,10 @@ internal class Cursor(private var helper: MongoIterableHelper<*>, private val co
205203
}
206204

207205
@HostAccess.Export
208-
override fun setReadPreference(v: Value): Cursor {
206+
override fun setReadPreference(v: String): Cursor {
209207
checkQueryNotExecuted()
210-
throw NotImplementedError("setReadPreference is not supported")
208+
helper = helper.readPrev(v, null)
209+
return this
211210
}
212211

213212
override fun showRecordId(v: Boolean): ServiceProviderCursor {

0 commit comments

Comments
 (0)