cloudant / sync-android

A JSON-based document datastore for Android applications
Apache License 2.0
267 stars 91 forks source link

cloudant-sync-datastore-javase can't find sqlite4java binary from java library path #598

Closed tiagosmx closed 4 years ago

tiagosmx commented 4 years ago

Hello, can someone provide a working build.gradle script for cloudant-sync-datastore-javase running on Windows 7 x64? It seems sqlite4java-win32-x64-1.0.392 dll is not being added to the classpath.

Bug Description

The library fails to open or create a DocumentStore.

1. Steps to reproduce and the simplest code sample possible to demonstrate the issue

The code I am trying to run:

import com.cloudant.sync.documentstore.DocumentStore;
import com.cloudant.sync.documentstore.DocumentStoreNotOpenedException;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;

public class Main {
    public static void main(String[] args) throws DocumentStoreNotOpenedException, URISyntaxException {
        URI remoteCouchDB = new URI("http://admin:admin@localhost:5984/hello");
        File file = new File("D:/hello.couchdb-sync");
        DocumentStore ds = DocumentStore.getInstance(file);
    }
}

My build.gradle file:

plugins {
    id 'java'
    id 'org.jetbrains.kotlin.jvm' version '1.3.61'
}

group 'me.aleatitus'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
    implementation group: 'com.almworks.sqlite4java', name: 'sqlite4java-win32-x64', version: '1.0.392'
    compile group: 'com.cloudant', name: 'cloudant-sync-datastore-javase', version:'latest.release'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    // https://mvnrepository.com/artifact/com.almworks.sqlite4java/sqlite4java-win32-x64

}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
}
compileKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}
compileTestKotlin {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

2. What you expected to happen

Being able to create or open a local DocumentStore.

3. What actually happened

An exception is thrown:

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.IllegalStateException: Failed to open database.
Caused by: com.almworks.sqlite4java.SQLiteException: [-91] cannot load library: java.lang.UnsatisfiedLinkError: no sqlite4java-win32-x64-1.0.392 in java.library.path
Caused by: com.almworks.sqlite4java.SQLiteException: [-91] cannot load library: java.lang.UnsatisfiedLinkError: no sqlite4java-win32-x64-1.0.392 in java.library.path
Caused by: java.lang.UnsatisfiedLinkError: no sqlite4java-win32-x64-1.0.392 in java.library.path

Environment details

The full exception log:

nov 30, 2019 3:01:31 PM com.almworks.sqlite4java.Internal log
INFORMAÇÕES: [sqlite] DB[1]: instantiated [null]
Exception in thread "main" java.lang.ExceptionInInitializerError
    at com.cloudant.sync.internal.sqlite.SQLDatabaseQueue.<init>(SQLDatabaseQueue.java:67)
    at com.cloudant.sync.internal.documentstore.DatabaseImpl.<init>(DatabaseImpl.java:153)
    at com.cloudant.sync.documentstore.DocumentStore.<init>(DocumentStore.java:90)
    at com.cloudant.sync.documentstore.DocumentStore.getInstance(DocumentStore.java:150)
    at com.cloudant.sync.documentstore.DocumentStore.getInstance(DocumentStore.java:120)
    at Main.main(Main.java:12)
Caused by: java.lang.IllegalStateException: Failed to open database.
    at com.cloudant.sync.internal.sqlite.sqlite4java.SQLiteWrapper.createNewConnection(SQLiteWrapper.java:92)
    at com.cloudant.sync.internal.sqlite.sqlite4java.SQLiteWrapper.getConnection(SQLiteWrapper.java:73)
    at com.cloudant.sync.internal.sqlite.sqlite4java.SQLiteWrapper.isOpen(SQLiteWrapper.java:121)
    at com.cloudant.sync.internal.sqlite.sqlite4java.SQLiteWrapper.beginTransaction(SQLiteWrapper.java:126)
    at com.cloudant.sync.internal.sqlite.SQLDatabaseFactory.isFtsAvailable(SQLDatabaseFactory.java:52)
    at com.cloudant.sync.internal.sqlite.SQLDatabaseFactory.<clinit>(SQLDatabaseFactory.java:45)
    ... 6 more
Caused by: com.almworks.sqlite4java.SQLiteException: [-91] cannot load library: java.lang.UnsatisfiedLinkError: no sqlite4java-win32-x64-1.0.392 in java.library.path
    at com.almworks.sqlite4java.SQLite.loadLibrary(SQLite.java:97)
    at com.almworks.sqlite4java.SQLiteConnection.open0(SQLiteConnection.java:1441)
    at com.almworks.sqlite4java.SQLiteConnection.open(SQLiteConnection.java:282)
    at com.cloudant.sync.internal.sqlite.sqlite4java.SQLiteWrapper.createNewConnection(SQLiteWrapper.java:88)
    ... 11 more
Caused by: java.lang.UnsatisfiedLinkError: no sqlite4java-win32-x64-1.0.392 in java.library.path
    at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2541)
    at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:873)
    at java.base/java.lang.System.loadLibrary(System.java:1857)
    at com.almworks.sqlite4java.Internal.tryLoadFromSystemPath(Internal.java:352)
    at com.almworks.sqlite4java.Internal.loadLibraryX(Internal.java:124)
    at com.almworks.sqlite4java.SQLite.loadLibrary(SQLite.java:95)
    ... 14 more

FAILURE: Build failed with an exception.
ricellis commented 4 years ago

As per sqlite4java documentation you need to direct sqlite4java to the location of the native (dll in this case) files. We do this by passing the -Dsqlite4java.library.path=some/path system property on startup, but it sounds like there are potentially some other options too.

I guess as long as gradle is downloading the binary correctly you can either set the property to point to that location or make tasks to move it around to where you need it to be.

tiagosmx commented 4 years ago

Thank you for the tip! I managed to make it work through "other options" using System.setProperty:

System.setProperty("sqlite4java.library.path", "C:/sqlite4java/dlls");

I found an example for DynamoDB here which happens to use sqlite4java as well.

Apart from that, Gradle downloaded the binary correctly but it seems I am missing some Gradle/IntelliJ/something else manual configuration to make them find the binary. The same happens in Linux (Debian 10) with IntelliJ, so it is more like a Maven/Gradle configuration issue than SO specific.