-
Notifications
You must be signed in to change notification settings - Fork 30
Check that the loaded native library is compatible [ECR-3148]: #882
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
Changes from all commits
430f52e
1004585
ccb29f8
7b25886
77b4ef8
94af3d7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright 2018 The Exonum Team | ||
* Copyright 2019 The Exonum Team | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
|
@@ -20,11 +20,23 @@ | |
import org.apache.logging.log4j.Logger; | ||
|
||
/** | ||
* A loader of the native shared library with Exonum framework bindings. | ||
* A loader of the native shared library with Exonum framework bindings. It loads the native | ||
* library and also verifies that it is compatible with the Java classes. The native library | ||
* is compatible iff it has exactly the same version as this Java library. The revision | ||
* from which they were built is currently not checked, but may be in the future (see ECR-3173), | ||
* because the API between Java and native is considered internal and can be changed | ||
* in an incompatible way in any revision. | ||
* | ||
* <p>To have library java_bindings available by its name, | ||
* add a path to the folder containing it to <code>java.library.path</code> property, | ||
* e.g.: <code>java -Djava.library.path=${EXONUM_HOME}/lib/native …</code> | ||
* <p>To enable loading of java_bindings library, add a path to the folder containing it | ||
* to <code>java.library.path</code> property, e.g.: | ||
* <code>java -Djava.library.path=${EXONUM_HOME}/lib/native …</code> | ||
* | ||
* <p>This class is thread-safe. | ||
* | ||
* @see <a href="https://exonum.com/doc/version/0.11/get-started/java-binding/#installation"> | ||
* Exonum Java installation instructions</a> | ||
* @see <a href="https://exonum.com/doc/version/0.11/get-started/java-binding/#testing"> | ||
* Build configuration to enable integration testing of Java services</a> | ||
*/ | ||
public final class LibraryLoader { | ||
|
||
|
@@ -33,16 +45,62 @@ public final class LibraryLoader { | |
private static final String DYNAMIC_LIBRARIES_ENV_VAR_WINDOWS = "PATH"; | ||
private static final String DYNAMIC_LIBRARIES_ENV_VAR_UNIX = "LD_LIBRARY_PATH"; | ||
|
||
/** | ||
* The current version of the project. Must be updated on | ||
* <a href="https://wiki.bf.local/display/EJB/Java+Binding+Release+Checklist+Template"> | ||
* each release</a>. | ||
*/ | ||
private static final String JAVA_BINDING_VERSION = "0.7.0-SNAPSHOT"; | ||
|
||
// TODO: Remove in ECR-3172 | ||
private static final boolean LIBRARY_VERSION_VERIFICATION_ENABLED = false; | ||
|
||
private static final Logger logger = LogManager.getLogger(LibraryLoader.class); | ||
|
||
private static final LibraryLoader INSTANCE = new LibraryLoader(JAVA_BINDING_VERSION); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about to implement the singleton with lazy loading? It doesn't bring a big overhead but anyway. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But it will never be actually "lazy" loaded, if I understand correctly — a class is loaded when its first static symbol is accessed (with some exception for compile-time constants, I believe), and the only publicly accessible symbol is https://docs.oracle.com/javase/specs/jls/se11/html/jls-12.html#jls-12.4.1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, you are right. My doubt was that it'll be loaded in case no integration tests defined. |
||
|
||
private final String expectedLibVersion; | ||
private boolean loaded; | ||
|
||
/** | ||
* Creates a new library loader. | ||
* | ||
* @param libraryVersion the version of this library to verify that the native library | ||
* is compatible with it | ||
*/ | ||
private LibraryLoader(String libraryVersion) { | ||
this.expectedLibVersion = libraryVersion; | ||
this.loaded = false; | ||
} | ||
|
||
/** | ||
* Loads the native library with Exonum framework bindings. | ||
* | ||
* @throws LinkageError if the native library cannot be loaded; or if it is incompatible | ||
* with this library version | ||
*/ | ||
public static void load() { | ||
loadOnce(); | ||
INSTANCE.loadOnce(); | ||
} | ||
|
||
private static void loadOnce() { | ||
private synchronized void loadOnce() { | ||
if (loaded) { | ||
// It has already been attempted to load the library (successfully or not) | ||
return; | ||
} | ||
|
||
try { | ||
// Try to load the library | ||
loadLibrary(); | ||
|
||
// Check that it has the compatible version | ||
checkLibraryVersion(); | ||
} finally { | ||
loaded = true; | ||
} | ||
} | ||
|
||
private static void loadLibrary() { | ||
try { | ||
System.loadLibrary(BINDING_LIB_NAME); | ||
} catch (UnsatisfiedLinkError e) { | ||
|
@@ -96,5 +154,24 @@ private static String dynamicLibrariesEnvVar() { | |
} | ||
} | ||
|
||
private LibraryLoader() {} | ||
private void checkLibraryVersion() { | ||
if (!LIBRARY_VERSION_VERIFICATION_ENABLED) { | ||
return; | ||
} | ||
String nativeLibVersion = nativeGetLibraryVersion(); | ||
if (!expectedLibVersion.equals(nativeLibVersion)) { | ||
String message = String.format( | ||
"Mismatch between versions of Java library and native '%s' library:%n" | ||
+ " Java library version: %s%n" | ||
+ " Native library version: %s%n" | ||
+ "Check that the version of 'exonum-java-binding-core' matches the version of " | ||
+ "the installed 'Exonum Java' application.%n" | ||
+ "See https://exonum.com/doc/version/0.11/get-started/java-binding/#installation", | ||
BINDING_LIB_NAME, expectedLibVersion, nativeLibVersion); | ||
logger.fatal(message); | ||
throw new LinkageError(message); | ||
} | ||
} | ||
|
||
private static native String nativeGetLibraryVersion(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: iff -> if
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't: "if and only if"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://en.oxforddictionaries.com/definition/iff