Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility with GraalVM #403

Closed
Gillani0 opened this issue Dec 9, 2020 · 5 comments · Fixed by #995
Closed

Compatibility with GraalVM #403

Gillani0 opened this issue Dec 9, 2020 · 5 comments · Fixed by #995

Comments

@Gillani0
Copy link

Gillani0 commented Dec 9, 2020

Hi,
I'd love to use this awsome library with the native graalVM support.

Currently I'm fixing the similar issue as described here. Following are my error logs:

Detailed message:
Trace: Object was reached by 
        reading field java.security.SecureRandom.secureRandomSpi of
                constant java.security.SecureRandom@45c5e218 reached by 
        scanning method org.dizitart.no2.Security.getNextSalt(Security.java:140)
Call path from entry point to org.dizitart.no2.Security.getNextSalt(): 
        at org.dizitart.no2.Security.getNextSalt(Security.java:139)
        at org.dizitart.no2.Security.createSecurely(Security.java:59)
        at org.dizitart.no2.NitriteBuilder.openOrCreateInternal(NitriteBuilder.java:459)
        at org.dizitart.no2.NitriteBuilder.openOrCreate(NitriteBuilder.java:415)
        at com.wattsense.config.DbTemplate.init(DbTemplate.kt:37)
        at com.wattsense.config.DbTemplate.onStart(DbTemplate.kt:22)
        at com.wattsense.config.DbTemplate_Observer_onStart_fd71b5e0b207b7d1ef838b94eaeff75e52b8f463.notify(DbTemplate_Observer_onStart_fd71b5e0b207b7d1ef838b94eaeff75e52b8f463.zig:147)
        at io.quarkus.arc.impl.EventImpl$DeferredEventNotification.run(EventImpl.java:426)
        at com.oracle.svm.core.jdk.RuntimeSupport.executeHooks(RuntimeSupport.java:125)
        at com.oracle.svm.core.jdk.RuntimeSupport.executeStartupHooks(RuntimeSupport.java:75)
        at com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:141)
        at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:184)
        at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)

com.oracle.svm.core.util.UserError$UserException: No instances of sun.security.provider.NativePRNG are allowed in the image heap as this class should be initialized at image runtime. To see how this object got instantiated use -H:+TraceClassInitialization.
Detailed message:
Trace: Object was reached by 
        reading field java.security.SecureRandom.secureRandomSpi of
                constant java.security.SecureRandom@45c5e218 reached by 
        scanning method org.dizitart.no2.Security.getNextSalt(Security.java:140)
Call path from entry point to org.dizitart.no2.Security.getNextSalt(): 
        at org.dizitart.no2.Security.getNextSalt(Security.java:139)
        at org.dizitart.no2.Security.createSecurely(Security.java:59)
        at org.dizitart.no2.NitriteBuilder.openOrCreateInternal(NitriteBuilder.java:459)
        at org.dizitart.no2.NitriteBuilder.openOrCreate(NitriteBuilder.java:415)
        at com.wattsense.config.DbTemplate.init(DbTemplate.kt:37)
        at com.wattsense.config.DbTemplate.onStart(DbTemplate.kt:22)
        at com.wattsense.config.DbTemplate_Observer_onStart_fd71b5e0b207b7d1ef838b94eaeff75e52b8f463.notify(DbTemplate_Observer_onStart_fd71b5e0b207b7d1ef838b94eaeff75e52b8f463.zig:147)
        at io.quarkus.arc.impl.EventImpl$DeferredEventNotification.run(EventImpl.java:426)
        at com.oracle.svm.core.jdk.RuntimeSupport.executeHooks(RuntimeSupport.java:125)
        at com.oracle.svm.core.jdk.RuntimeSupport.executeStartupHooks(RuntimeSupport.java:75)
        at com.oracle.svm.core.JavaMainWrapper.runCore(JavaMainWrapper.java:141)
        at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:184)
        at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)

@anidotnet
Copy link
Contributor

I have not tested it with GraalVM. But any community contribution is more than welcome.

@anidotnet
Copy link
Contributor

Have you got any progress on this issue?

@anidotnet anidotnet closed this as not planned Won't fix, can't repro, duplicate, stale Aug 5, 2023
@DarkAtra
Copy link
Contributor

Hey @anidotnet,
are you still open for contributions here? I was actually using version 3.x of nitrite (with mvstore) with graalvm in my project. There were only a few runtime hints required for it to work - most of them were related to java serialization. Version 4.x likely requires the same or at least somehwat similar runtime hints.

@anidotnet
Copy link
Contributor

@DarkAtra contribution is always welcome in latest Nitrite.

@DarkAtra
Copy link
Contributor

DarkAtra commented Jul 6, 2024

@DarkAtra contribution is always welcome in latest Nitrite.

After looking at the tests a bit, it's rather unlikely that i'm able to also introduce native tests for nitrite mvstore as it still uses junit 4. Apparently, native tests require at least junit version 5.8.1 (JUnit Platform 1.8.1, JUnit Jupiter 5.8.1, JUnit Vintage 5.8.1): https://graalvm.github.io/native-build-tools/latest/maven-plugin.html#testing-support. Nitrite currently uses 4.13.2.

Update: i've created a separate module that uses junit 5 with a few test cases for basic nitrite operations - see the PR.


Regarding the native hints: it seems like the following configuration is required for nitrite to properly read and write in a native image (when using mvstore with custom EntityConverters):

serialization-config.json

[
    {
        "name": "java.lang.Integer"
    },
    {
        "name": "java.lang.Long"
    },
    {
        "name": "java.lang.Number"
    },
    {
        "name": "java.lang.String"
    },
    {
        "name": "java.util.ArrayList"
    },
    {
        "name": "java.util.concurrent.atomic.AtomicBoolean"
    },
    {
        "name": "java.util.concurrent.ConcurrentHashMap"
    },
    {
        "name": "java.util.concurrent.ConcurrentHashMap$Segment"
    },
    {
        "name": "java.util.concurrent.CopyOnWriteArrayList"
    },
    {
        "name": "java.util.concurrent.locks.AbstractOwnableSynchronizer"
    },
    {
        "name": "java.util.concurrent.locks.AbstractQueuedSynchronizer"
    },
    {
        "name": "java.util.concurrent.locks.ReentrantLock"
    },
    {
        "name": "java.util.concurrent.locks.ReentrantLock$NonfairSync"
    },
    {
        "name": "java.util.concurrent.locks.ReentrantLock$Sync"
    },
    {
        "name": "java.util.HashMap"
    },
    {
        "name": "java.util.HashSet"
    },
    {
        "name": "java.util.LinkedHashMap"
    },
    {
        "name": "org.dizitart.no2.collection.NitriteDocument"
    },
    {
        "name": "org.dizitart.no2.collection.NitriteId"
    },
    {
        "name": "org.dizitart.no2.common.DBValue"
    },
    {
        "name": "org.dizitart.no2.common.Fields"
    },
    {
        "name": "org.dizitart.no2.common.meta.Attributes"
    },
    {
        "name": "org.dizitart.no2.common.tuples.Pair"
    },
    {
        "name": "org.dizitart.no2.index.IndexDescriptor"
    },
    {
        "name": "org.dizitart.no2.index.IndexMeta"
    },
    {
        "name": "org.dizitart.no2.store.UserCredential"
    }
]

Also, users will have to provide additional reflection hints for classes that create indices.

For example, if i was to store the following Error class:

package de.darkatra

import org.dizitart.no2.index.IndexType
import org.dizitart.no2.repository.annotations.Id
import org.dizitart.no2.repository.annotations.Index
import org.dizitart.no2.repository.annotations.Indices
import java.time.Instant

@Indices(
    value = [
        Index(fields = ["message"], type = IndexType.NON_UNIQUE),
    ]
)
data class Error(
    val message: String,
    val timestamp: Instant
)

using the following entity converter:

class ErrorEntityConverter : EntityConverter<Error> {

    override fun getEntityType(): Class<Error> = Error::class.java

    override fun fromDocument(document: Document, nitriteMapper: NitriteMapper): Error {
        return Error(
            message = document.get(Error::message.name, String::class.java),
            timestamp = Instant.parse(document.get(Error::timestamp.name, String::class.java))
        )
    }

    override fun toDocument(error: Error, nitriteMapper: NitriteMapper): Document {
        return Document.createDocument().apply {
            put(Error::message.name, error.message)
            put(Error::timestamp.name, error.timestamp.toString())
        }
    }
}

via:

val repository = database.getRepository(Error::class.java)
repository.insert(
  Error(
    message = "message",
    timestamp = Instant.now()
  )
)

I'd have to add the following reflect-config.json:

{
  "name": "de.darkatra.Error",
  "allDeclaredFields": true
}

or i'd get the following error message:

org.dizitart.no2.exceptions.ValidationException: No such field 'message' for type de.darkatra.Error
        at org.dizitart.no2.repository.Reflector.getField(Reflector.java:127) ~[na:na]
        at org.dizitart.no2.repository.AnnotationScanner.populateIndex(AnnotationScanner.java:156) ~[na:na]
        at org.dizitart.no2.repository.AnnotationScanner.scanIndicesAnnotation(AnnotationScanner.java:95) ~[na:na]
        at org.dizitart.no2.repository.AnnotationScanner.performScan(AnnotationScanner.java:71) ~[na:na]
        at org.dizitart.no2.repository.RepositoryOperations.scanIndexes(RepositoryOperations.java:65) ~[na:na]
        at org.dizitart.no2.repository.DefaultObjectRepository.initialize(DefaultObjectRepository.java:239) ~[na:na]
        at org.dizitart.no2.repository.DefaultObjectRepository.<init>(DefaultObjectRepository.java:55) ~[na:na]
        at org.dizitart.no2.repository.RepositoryFactory.createRepository(RepositoryFactory.java:145) ~[na:na]
        at org.dizitart.no2.repository.RepositoryFactory.getRepository(RepositoryFactory.java:78) ~[na:na]
        at org.dizitart.no2.repository.RepositoryFactory.getRepository(RepositoryFactory.java:52) ~[na:na]
        at org.dizitart.no2.NitriteDatabase.getRepository(NitriteDatabase.java:73) ~[na:na]

Users will also have to add additional serialization hints for all serializable types they write to the database. For example, the following code:

val collection = database.getCollection("test")
collection.insert(
  Document.createDocument("boolean", true)
)

would require the following hint to be added to the users serialization-config.json:

{
  "name": "java.lang.Boolean"
}

Demo project: https://github.com/DarkAtra/nitrite-graalvm-demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants