Skip to content

Proxies are not created in GraalVM #67

Description

@juliuskrah

Defining new classes at runtime is not supported

When running in native mode, mongock migration fails with the following exception:

2024-03-18T15:11:33.437+01:00 ERROR 39217 --- [           main] i.m.r.core.executor.MongockRunnerImpl    : Error in mongock process. ABORTED OPERATION

io.mongock.api.exception.MongockException: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
	at io.mongock.runner.core.executor.MongockRunnerImpl.execute(MongockRunnerImpl.java:71) ~[na:na]
	at io.mongock.runner.springboot.base.MongockApplicationRunner.run(MongockApplicationRunner.java:18) ~[graphql-demo:na]
	at org.springframework.boot.SpringApplication.lambda$callRunner$4(SpringApplication.java:794) ~[graphql-demo:3.2.1]
	at org.springframework.util.function.ThrowingConsumer$1.acceptWithException(ThrowingConsumer.java:83) ~[na:na]
	at org.springframework.util.function.ThrowingConsumer.accept(ThrowingConsumer.java:60) ~[graphql-demo:6.1.2]
	at org.springframework.util.function.ThrowingConsumer$1.accept(ThrowingConsumer.java:88) ~[na:na]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:806) ~[graphql-demo:3.2.1]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:794) ~[graphql-demo:3.2.1]
	at org.springframework.boot.SpringApplication.lambda$callRunners$3(SpringApplication.java:782) ~[graphql-demo:3.2.1]
	at java.base@21/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184) ~[na:na]
	at java.base@21/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357) ~[na:na]
	at java.base@21/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510) ~[graphql-demo:na]
	at java.base@21/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[graphql-demo:na]
	at java.base@21/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) ~[graphql-demo:na]
	at java.base@21/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174) ~[na:na]
	at java.base@21/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[graphql-demo:na]
	at java.base@21/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[graphql-demo:na]
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:782) ~[graphql-demo:3.2.1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:341) ~[graphql-demo:3.2.1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1358) ~[graphql-demo:3.2.1]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1347) ~[graphql-demo:3.2.1]
	at com.example.graph.ApplicationKt.main(Application.kt:16) ~[graphql-demo:na]
	at java.base@21/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH) ~[na:na]
Caused by: java.lang.UnsupportedOperationException: Defining new classes at runtime is not supported
	at org.graalvm.nativeimage.builder/com.oracle.svm.core.util.VMError.unimplemented(VMError.java:195) ~[na:na]
	at java.base@21/java.lang.invoke.MethodHandles$Lookup.defineClass(MethodHandles.java:45) ~[graphql-demo:na]
	at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:295) ~[na:na]
	at javassist.util.proxy.DefineClassHelper$Java11.defineClass(DefineClassHelper.java:48) ~[na:na]
	at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:260) ~[na:na]
	at javassist.util.proxy.FactoryHelper.toClass(FactoryHelper.java:154) ~[na:na]
	at javassist.util.proxy.ProxyFactory.createClass3(ProxyFactory.java:640) ~[na:na]
	at javassist.util.proxy.ProxyFactory.createClass2(ProxyFactory.java:624) ~[na:na]
	at javassist.util.proxy.ProxyFactory.createClass1(ProxyFactory.java:560) ~[na:na]
	at javassist.util.proxy.ProxyFactory.createClass(ProxyFactory.java:481) ~[na:na]
	at io.mongock.driver.api.lock.guard.proxy.LockGuardProxyFactory.createProxy(LockGuardProxyFactory.java:92) ~[na:na]
	at io.mongock.driver.api.lock.guard.proxy.LockGuardProxyFactory.getRawProxy(LockGuardProxyFactory.java:62) ~[na:na]
	at io.mongock.runner.core.executor.dependency.DependencyManager.getDependency(DependencyManager.java:52) ~[graphql-demo:na]
	at io.mongock.runner.core.executor.dependency.DependencyManagerWithContext.getDependency(DependencyManagerWithContext.java:40) ~[na:na]
	at io.mongock.runner.core.executor.ChangeLogRuntimeImpl.getParameter(ChangeLogRuntimeImpl.java:120) ~[na:na]
	at io.mongock.runner.core.executor.ChangeLogRuntimeImpl.getInvokationParameters(ChangeLogRuntimeImpl.java:108) ~[na:na]
	at io.mongock.runner.core.executor.ChangeLogRuntimeImpl.getInstance(ChangeLogRuntimeImpl.java:92) ~[na:na]
	at io.mongock.runner.core.executor.ChangeExecutorBase.getChangeLogInstance(ChangeExecutorBase.java:140) ~[graphql-demo:na]
	at io.mongock.runner.core.executor.ChangeExecutorBase.processSingleChangeLog(ChangeExecutorBase.java:117) ~[graphql-demo:na]
	at io.mongock.runner.core.executor.ChangeExecutorBase.processChangeLogs(ChangeExecutorBase.java:107) ~[graphql-demo:na]
	at io.mongock.runner.core.executor.ChangeExecutorBase.lambda$processMigration$1(ChangeExecutorBase.java:101) ~[graphql-demo:na]
	at io.mongock.runner.core.executor.NonTransactioner.executeInTransaction(NonTransactioner.java:17) ~[na:na]
	at io.mongock.runner.core.executor.ChangeExecutorBase.processMigration(ChangeExecutorBase.java:101) ~[graphql-demo:na]
	at io.mongock.runner.core.executor.operation.migrate.MigrateExecutorBase.executeMigration(MigrateExecutorBase.java:66) ~[graphql-demo:na]
	at io.mongock.runner.core.executor.operation.migrate.MigrateExecutorBase.executeMigration(MigrateExecutorBase.java:18) ~[graphql-demo:na]
	at io.mongock.runner.core.executor.MongockRunnerImpl.execute(MongockRunnerImpl.java:57) ~[na:na]
	... 22 common frames omitted

PRIORITY

NORMAL

Version and environment

  • kotlin:1.9.21
  • Oracle GraalVM 21+35.1 (build 21+35-jvmci-23.1-b15)

Mongock

  • Mongock version
    • io.mongock:mongodb-reactive-driver:5.4.0
    • io.mongock:mongock-springboot:5.4.0
  • Modules involved(springboot, MongoDB)
  • How Mongock is used(builder and annotation approach)

Environment

  • Framework and libraries versions. (Spring, Spring data, MongoDB driver)
  • Infrastructure: Docker Compose.

Steps to Reproduce

Given the change unit class:

@ChangeUnit(id = "create-product-collection-202401151530", order = "202401151530", author = "Julius Krah")
class CreateProductCollectionChangeUnit202401151530(
    private val database: MongoDatabase,
    private val session: ClientSession,
    private val jackson: ObjectMapper
) {
    private val log: System.Logger = System.getLogger(CreateProductCollectionChangeUnit202401151530::class.simpleName, ResourceBundle.getBundle("messages.messages"))

    companion object {
        @JvmStatic
        val PRODUCT_COLLECTION_NAME = "products"
    }

    @BeforeExecution
    fun createProductCollection() {
        val bsonType = "bsonType"
        val description = "description"
        val schema: Bson = Document(mapOf(
            bsonType to "object",
            "title" to "Product Object validation",
            "required" to setOf("createdAt", "updatedAt", "createdBy", "updatedBy", "title"),
            "properties" to Document("title", Document(bsonType, "string").append(description, "'title' must be a string and is required"))
                .append("createdAt", Document(bsonType, "date").append(description, "'createdAt' must be a date and is required"))
                .append("updatedAt", Document(bsonType, "date").append(description, "'updatedAt' must be a date and is required"))
                .append("createdBy", Document(bsonType, "string").append(description, "'createdBy' must be a string and is required"))
                .append("updatedBy", Document(bsonType, "string").append(description, "'updatedBy' must be a string and is required"))
                .append("mediaUrls", Document(bsonType, "array").append("items", Document(bsonType, "string")))
        ))
        val validator: Bson = Filters.jsonSchema(schema)
        val validationOptions = ValidationOptions().validator(validator)
        val options = CreateCollectionOptions().validationOptions(validationOptions)
        val subscriber = MongoSubscriberSync<Void>()
        this.database.createCollection(this.session, PRODUCT_COLLECTION_NAME, options).subscribe(subscriber)
        subscriber.await()
    }

    @RollbackBeforeExecution
    fun deleteProductCollection() {
        val subscriber = MongoSubscriberSync<Void>()
        this.database.getCollection(PRODUCT_COLLECTION_NAME).drop(this.session).subscribe(subscriber)
        subscriber.await()
    }

    @Execution
    fun addDataProductCollection() {
        val jsonProducts = readFile("data/products.json")
        log.log(INFO, "log.migration.json.data", jsonProducts)
        val products = jsonProducts.map{
            it["createdAt"] = Instant.ofEpochSecond((it["createdAt"] as Int).toLong())
            it["updatedAt"] = Instant.ofEpochSecond((it["updatedAt"] as Int).toLong())
            it
        }.map(::Document)
        val subscriber = MongoSubscriberSync<InsertManyResult>()
        this.database.getCollection(PRODUCT_COLLECTION_NAME).insertMany(this.session, products).subscribe(subscriber)
        subscriber.await()
    }

    @RollbackExecution
    fun removeDataProductCollection() {
        val subscriber = MongoSubscriberSync<DeleteResult>()
        this.database.getCollection(PRODUCT_COLLECTION_NAME).deleteMany(this.session, Filters.empty()).subscribe(subscriber)
        subscriber.await()
    }

    @Throws(IOException::class)
    private fun readFile(filePath: String): List<MutableMap<String, Any>> {
        val inputStream = ClassPathResource(filePath).inputStream
        return this.jackson.readValue(inputStream, object: TypeReference<List<MutableMap<String, Any>>>(){})
    }
}

when configured with change unit class:

val builder: RunnerSpringbootBuilder = MongockSpringboot.builder()
builder.addMigrationClass(CreateProductCollectionChangeUnit202401151530::class.java)
builder.buildRunner()

// ...
@Bean
@ConditionalOnExpression("'\${mongock.runner-type:ApplicationRunner}'.toLowerCase().equals('applicationrunner')")
fun applicationRunner(mongockRunner: MongockRunner): MongockApplicationRunner {
    return MongockApplicationRunner(mongockRunner)
}

and built in native mode ./gradlew nativeCompile; fails to start migration

Behaviour

Expected behavior: Migration should run

Actual behavior: Migration does not run

How often the bug happens: When the changeunit is run the first time

Link to repository using Mongock: https://github.com/juliuskrah/graphql-demo

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions