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

Tidy up and share (between processes) temporary copies of JNI shared library loaded from JAR #13318

Draft
wants to merge 24 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2ce180a
Share large temporary library files used by Java
alanpaxton Jan 13, 2025
e17362e
WIP Get multiprocess test to work, fix silly errors
alanpaxton Jan 13, 2025
c9e0aa3
Clean up test/debug
alanpaxton Jan 14, 2025
6bc3994
Tests happier, add utilty `searchOrCreate()`
alanpaxton Jan 14, 2025
3ec5487
Wire SharedTempFile into native library loader
alanpaxton Jan 14, 2025
3c0b783
Add SharedTempFileLoaderTest to jtest
alanpaxton Jan 15, 2025
9a76994
Quietly ignore “lock directory already cleaned up”
alanpaxton Jan 15, 2025
b728896
Better clean up files after test
alanpaxton Jan 15, 2025
9ee2f1d
Replace random temporary path with digest
alanpaxton Jan 15, 2025
8de3ac0
Test loading using Djava.library.path (no jar)
alanpaxton Jan 20, 2025
43fd56b
Fix formatting
alanpaxton Jan 20, 2025
e1f05ab
fix format
alanpaxton Jan 20, 2025
0a91dee
Add omitted new source to CMake build
alanpaxton Jan 20, 2025
de2e3ce
Fix format (again (why ?))
alanpaxton Jan 20, 2025
bca93ac
Fix or suppress new PMD violations.
alanpaxton Jan 20, 2025
3c9b827
Fix a warning - missing @throws
alanpaxton Jan 20, 2025
bc2e813
Add large class comment to explain locking better.
alanpaxton Jan 21, 2025
270df78
CI failure - are we getting ClassLoader wrong way?
alanpaxton Jan 22, 2025
b2773f3
Format comments fix
alanpaxton Jan 22, 2025
00effca
Typo in cmake build
alanpaxton Jan 22, 2025
9dfd68a
Copy java test resources in jtest
alanpaxton Jan 22, 2025
9f44449
Try to make SharedTempfileTest work in Windows
alanpaxton Jan 22, 2025
d7a2346
Add new test support files to cmake
alanpaxton Jan 27, 2025
07b841a
Add omitted test util file to cmake
alanpaxton Jan 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions java/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ set(JAVA_MAIN_CLASSES
src/main/java/org/rocksdb/util/Environment.java
src/main/java/org/rocksdb/util/IntComparator.java
src/main/java/org/rocksdb/util/ReverseBytewiseComparator.java
src/main/java/org/rocksdb/util/SharedTempFile.java
src/main/java/org/rocksdb/util/SizeUnit.java
src/main/java/org/rocksdb/util/StdErrLogger.java
src/test/java/org/rocksdb/NativeComparatorWrapperTest.java
Expand Down Expand Up @@ -384,6 +385,9 @@ set(JAVA_TEST_CLASSES
src/test/java/org/rocksdb/RocksIteratorTest.java
src/test/java/org/rocksdb/RocksMemEnvTest.java
src/test/java/org/rocksdb/SecondaryDBTest.java
src/test/java/org/rocksdb/SharedTempFileLoaderTest.java
src/test/java/org/rocksdb/SharedTempFileMockMain.java
src/test/java/org/rocksdb/SharedTempFileRocksDBMain.java
src/test/java/org/rocksdb/SliceTest.java
src/test/java/org/rocksdb/SnapshotTest.java
src/test/java/org/rocksdb/SstFileManagerTest.java
Expand Down Expand Up @@ -415,6 +419,7 @@ set(JAVA_TEST_CLASSES
src/test/java/org/rocksdb/test/RocksJunitRunner.java
src/test/java/org/rocksdb/test/TestableEventListener.java
src/test/java/org/rocksdb/test/TestableEventListener.java
src/main/java/org/rocksdb/util/ArgUtil.java
src/test/java/org/rocksdb/util/ByteBufferAllocator.java
src/test/java/org/rocksdb/util/BytewiseComparatorIntTest.java
src/test/java/org/rocksdb/util/BytewiseComparatorTest.java
Expand All @@ -429,6 +434,10 @@ set(JAVA_TEST_CLASSES
src/test/java/org/rocksdb/util/TestUtil.java
)

set(JAVA_TEST_RESOURCES
src/test/resources/shared-temp-file.txt
)

set(JAVA_TEST_RUNNING_CLASSES
org.rocksdb.BackupEngineOptionsTest
org.rocksdb.BackupEngineTest
Expand Down Expand Up @@ -503,6 +512,7 @@ set(JAVA_TEST_RUNNING_CLASSES
org.rocksdb.RocksIteratorTest
org.rocksdb.RocksMemEnvTest
org.rocksdb.SecondaryDBTest
org.rocksdb.SharedTempFileLoaderTest
org.rocksdb.SliceTest
org.rocksdb.SnapshotTest
org.rocksdb.SstFileManagerTest
Expand Down Expand Up @@ -598,6 +608,7 @@ add_jar(
SOURCES
${JAVA_MAIN_CLASSES}
${JAVA_TEST_CLASSES}
${JAVA_TEST_RESOURCES}
INCLUDE_JARS ${JAVA_TESTCLASSPATH}
GENERATE_NATIVE_HEADERS rocksdbjni_test_headers DESTINATION ${JNI_OUTPUT_DIR}
)
Expand Down
3 changes: 3 additions & 0 deletions java/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ JAVA_TESTS = \
org.rocksdb.RocksMemEnvTest\
org.rocksdb.util.SizeUnitTest\
org.rocksdb.SecondaryDBTest\
org.rocksdb.SharedTempFileLoaderTest\
org.rocksdb.SliceTest\
org.rocksdb.SnapshotTest\
org.rocksdb.SstFileManagerTest\
Expand Down Expand Up @@ -212,6 +213,7 @@ JAVA_TESTS = \

MAIN_SRC = src/main/java
TEST_SRC = src/test/java
TEST_RESOURCES = src/test/resources
OUTPUT = target
MAIN_CLASSES = $(OUTPUT)/classes
TEST_CLASSES = $(OUTPUT)/test-classes
Expand Down Expand Up @@ -452,6 +454,7 @@ java_test: java resolve_test_deps
$(AM_V_GEN)mkdir -p $(TEST_CLASSES)
$(AM_V_at) $(JAVAC_CMD) $(JAVAC_ARGS) -cp $(MAIN_CLASSES):$(JAVA_TESTCLASSPATH) -h $(NATIVE_INCLUDE) -d $(TEST_CLASSES)\
$(TEST_SOURCES)
cp -v $(TEST_RESOURCES)/* $(TEST_CLASSES)

test: java java_test
$(MAKE) run_test
Expand Down
86 changes: 38 additions & 48 deletions java/src/main/java/org/rocksdb/NativeLibraryLoader.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
package org.rocksdb;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Path;
import org.rocksdb.util.Environment;
import org.rocksdb.util.SharedTempFile;

/**
* This class is used to load the RocksDB shared library from within the jar.
Expand Down Expand Up @@ -104,63 +105,52 @@ public synchronized void loadLibrary(final String tmpDir) throws IOException {
void loadLibraryFromJar(final String tmpDir)
throws IOException {
if (!initialized) {
System.load(loadLibraryFromJarToTemp(tmpDir).getAbsolutePath());
System.load(loadLibraryFromJarToTemp(tmpDir).toString());
initialized = true;
}
}

private File createTemp(final String tmpDir, final String libraryFileName) throws IOException {
// create a temporary file to copy the library to
final File temp;
if (tmpDir == null || tmpDir.isEmpty()) {
temp = File.createTempFile(tempFilePrefix, tempFileSuffix);
} else {
final File parentDir = new File(tmpDir);
if (!parentDir.exists()) {
throw new RuntimeException(
"Directory: " + parentDir.getAbsolutePath() + " does not exist!");
}
temp = new File(parentDir, libraryFileName);
if (temp.exists() && !temp.delete()) {
throw new RuntimeException(
"File: " + temp.getAbsolutePath() + " already exists and cannot be removed.");
}
if (!temp.createNewFile()) {
throw new RuntimeException("File: " + temp.getAbsolutePath() + " could not be created.");
}
@SuppressWarnings({"PMD.UseProperClassLoader", "PMD.CloseResource"})
private InputStream libraryFromJar() {
InputStream is = getClass().getClassLoader().getResourceAsStream(jniLibraryFileName);
if (is == null) {
is = getClass().getClassLoader().getResourceAsStream(fallbackJniLibraryFileName);
}
if (temp.exists()) {
temp.deleteOnExit();
return temp;
} else {
throw new RuntimeException("File " + temp.getAbsolutePath() + " does not exist.");
if (is == null) {
throw new RuntimeException(jniLibraryFileName + " was not found inside JAR.");
}
return is;
}

@SuppressWarnings({"PMD.UseProperClassLoader", "PMD.UseTryWithResources"})
File loadLibraryFromJarToTemp(final String tmpDir) throws IOException {
try (InputStream is = getClass().getClassLoader().getResourceAsStream(jniLibraryFileName)) {
if (is != null) {
final File temp = createTemp(tmpDir, jniLibraryFileName);
Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
return temp;
}
@SuppressWarnings("PMD.UseProperClassLoader")
private String libraryResourcePath() {
URL resource = getClass().getClassLoader().getResource(jniLibraryFileName);
if (resource == null) {
resource = getClass().getClassLoader().getResource(fallbackJniLibraryFileName);
}

if (fallbackJniLibraryFileName == null) {
throw new RuntimeException(fallbackJniLibraryFileName + " was not found inside JAR.");
if (resource == null) {
throw new RuntimeException(jniLibraryFileName + " was not found inside JAR.");
}
return resource.getFile();
}

try (InputStream is =
getClass().getClassLoader().getResourceAsStream(fallbackJniLibraryFileName)) {
if (is != null) {
final File temp = createTemp(tmpDir, fallbackJniLibraryFileName);
Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING);
return temp;
@SuppressWarnings({"PMD.UseTryWithResources", "PMD.CloseResource"})
Path loadLibraryFromJarToTemp(final String tmpDir) throws IOException {
String prefix = tempFilePrefix;
String suffix = tempFileSuffix;
if (jniLibraryFileName != null) {
String[] split = jniLibraryFileName.split("\\.");
if (split.length == 2) {
prefix = split[0];
suffix = split[1];
}
}

throw new RuntimeException(jniLibraryFileName + " was not found inside JAR.");
SharedTempFile.Instance instance =
new SharedTempFile.Instance(tmpDir, prefix, libraryResourcePath(), suffix);
SharedTempFile sharedTemp = instance.create();
SharedTempFile.Lock lock = sharedTemp.lock(this::libraryFromJar);
Runtime.getRuntime().addShutdownHook(new Thread(lock::close));
return sharedTemp.getContent();
}

/**
Expand Down
Loading
Loading