Skip to content

Conversation

dmitry-timofeev
Copy link
Contributor

@dmitry-timofeev dmitry-timofeev commented May 8, 2019

Overview

Check that the loaded native library is compatible, i.e.,
has the same version as the Java library. This check is useful in
service integration tests that can update their dependency
on 'exonum-java-binding-core' but not the application (that
contains the native library), or vice versa.

Requires native code to enable this check: ECR-3172


See: https://jira.bf.local/browse/ECR-3148

Definition of Done

  • There are no TODOs left in the code
  • Change is covered by automated tests
  • The coding guidelines are followed
  • Public API has Javadoc
  • Method preconditions are checked and documented in the Javadoc of the method
  • Changelog is updated if needed (in case of notable or breaking changes)
  • The continuous integration build passes

Check that the loaded native library is compatible, i.e.,
has the same version as the Java library. This check is useful in
_service integration tests_ that can update their dependency
on 'exonum-java-binding-core' but not the application (that
contains the native library), or vice versa.

Requires native code to enable this check: ECR-3172
}
}

// todo: Shall we extract it as a Supplier<String>, and System.load() as Consumer<String>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can possibly parameterize this class with this couple of functions (hence the constructor):

private static final LibraryLoader INSTANCE =
    new LibraryLoader(System::loadLibrary, LibraryLoader::nativeGetLibraryVersion,
                      JAVA_BINDING_VERSION);

Is this complication worth it? Also, it will still have dependencies on the system properties and environment, unless we also abstract them and parameterize 🙃

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about such refactoring for tests because it contains only one equals statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙆‍♂️ , let's keep it simple(r) then.

private static final String JAVA_LIBRARY_PATH_PROPERTY = "java.library.path";
private static final String DYNAMIC_LIBRARIES_ENV_VAR_WINDOWS = "PATH";
private static final String DYNAMIC_LIBRARIES_ENV_VAR_UNIX = "LD_LIBRARY_PATH";
// TODO: Shall this class read the properties instead?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make Maven auto-generate the properties file with the version, but … not sure we need that at this moment (with some code here reading those properties).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we then update the version string here on each release?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — update this string each release or make Maven write and this code read the build properties.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible/better to generate source file with constants before building the "static" source code? (move it to compilation phase instead of reading properties at runtime)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's trivial to pick up maven properties in runtime. So I believe it brings more benefits than drawbacks 🦊
Also, looks like we can configure RUSTFLAGS in maven and remove tests_profile? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's trivial to pick up maven properties in runtime. So I believe it brings more benefits than drawbacks fox_face

Yes, it is not difficult at all, but it seems like an overkill at this point: this class will gain a responsibility to read (once) a certain file with properties + the build configuration needs to be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, looks like we can configure RUSTFLAGS in maven and remove tests_profile

How? It invokes rustup and find do determine some various paths to add to RUSTFLAGS.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example:

    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.7</version>
        <executions>
          <execution>
            <id>define-property</id>
            <phase>validate</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <exportAntProperties>true</exportAntProperties>
              <tasks>
                <exec executable="git" outputproperty="testproperty">
                  <arg value="--version"/>
                </exec>
              </tasks>
            </configuration>
          </execution>
          <execution>
            <id>show-property</id>
            <phase>compile</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <tasks>
                <echo>Displaying value of 'testproperty':</echo>
                <echo>[testproperty] ${testproperty}</echo>
              </tasks>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>

It'll print in the log:

     [echo] Displaying value of 'testproperty':
     [echo] [testproperty] git version 2.20.1 (Apple Git-117)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks interesting, maybe we can offload that to an Ant script instead of a bash script for ease of use (no source tests_profile) and support of any platform (Windows). Please file an issue if consider that promising.

Copy link
Contributor

@bullet-tooth bullet-tooth May 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dmitry-timofeev dmitry-timofeev added the work-in-progress 👷‍♂️ Do not expect reviewers to provide any feedback on WIP PRs — please ask for it explicitly! label May 8, 2019
@coveralls
Copy link

coveralls commented May 8, 2019

Coverage Status

Coverage increased (+0.04%) to 86.077% when pulling 94af3d7 on dmitry-timofeev:check-native-library-version-ECR-3148 into 201f5fc on exonum:master.

* 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: iff -> if

Copy link
Contributor Author

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"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* @param libraryVersion the version of this library to verify that the native library
* is compatible with it. The compatibility is d
*/
LibraryLoader(String libraryVersion) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does it package private, not private? I see no tests.

* Creates a new library loader.
*
* @param libraryVersion the version of this library to verify that the native library
* is compatible with it. The compatibility is d
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

d? 🙃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably "determined by comparing …" :-D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code review gives you great opportunity to discover lots of new words like "iff", "d" 🙃

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@skletsun OMG YOLO

private static final String JAVA_LIBRARY_PATH_PROPERTY = "java.library.path";
private static final String DYNAMIC_LIBRARIES_ENV_VAR_WINDOWS = "PATH";
private static final String DYNAMIC_LIBRARIES_ENV_VAR_UNIX = "LD_LIBRARY_PATH";
// TODO: Shall this class read the properties instead?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we then update the version string here on each release?


private static final Logger logger = LogManager.getLogger(LibraryLoader.class);

private static final LibraryLoader INSTANCE = new LibraryLoader(JAVA_BINDING_VERSION);
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 load.

https://docs.oracle.com/javase/specs/jls/se11/html/jls-12.html#jls-12.4.1

Copy link
Contributor

Choose a reason for hiding this comment

The 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 static void loadOnce() {
synchronized void loadOnce() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private?

}
}

// todo: Shall we extract it as a Supplier<String>, and System.load() as Consumer<String>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about such refactoring for tests because it contains only one equals statement.

@dmitry-timofeev dmitry-timofeev removed the work-in-progress 👷‍♂️ Do not expect reviewers to provide any feedback on WIP PRs — please ask for it explicitly! label May 14, 2019
@dmitry-timofeev dmitry-timofeev merged commit 77428d4 into exonum:master May 16, 2019
@dmitry-timofeev dmitry-timofeev deleted the check-native-library-version-ECR-3148 branch May 16, 2019 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants