Skip to content
This repository was archived by the owner on Feb 16, 2025. It is now read-only.

CM H3 Java JNI whitelist plugin #3889

Merged
merged 22 commits into from
Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions doc/news/_preparation_next_release.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,16 @@ you up to date with the multi-language support provided by Elektra.
- Introduced `KeySet::remove(Key)` and `KeySet::remove(String)`
- Removed `KeySet::lookup(Key, int)` and `KeySet::lookup(String, int)` as well as accompanying flag definitions `KeySet::KDB_O_NONE`, `KeySet::KDB_O_DEL` and `KeySet::KDB_O_POP`. Please use `KeySet::lookup(Key)` and `KeySet::lookup(String)` instead. Instead of `KeySet::KDB_O_DEL`, please consider using `Key::release`. The proper replacement for `KeySet::KDB_O_POP` is `KeySet::remove(Key)` or `KeySet::remove(String)`.
- Native library proxy interface `Elektra` is now package private (previously was public).
- Added example Java plugin `whitelist` (see [here](../../src/bindings/jna/plugins/whitelist/README.md))
- Changed `Key nextMeta()` to `Optional<Key> nextMeta ()` no longer throwing NoSuchElementException for non-exceptional behavior
- Added support of binary valued keys:
- Renamed `KeyBinaryTypeNotSupportedException` to `KeyStringValueException`
- Introduced `KeyBinaryValueException`
- Improved `Key` test coverage
- Introduced `Key::getBinary()` and `Key::setBinary(byte[])`
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be probably directly after "Added support of binary valued keys:"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@markus2330 I do not understand what you mean. What do you mean by this?

Copy link
Contributor

Choose a reason for hiding this comment

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

The sentence "- Introduced Key::getBinary() and Key::setBinary(byte[])" seems to be the most important feature for "Added support of binary valued keys:", so I would write it first.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will reorder the affected lines of the release note in #4002

- Fixed example project in `examples/external/java/read-keys-example`
Copy link
Contributor

Choose a reason for hiding this comment

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

What was fixed? A typo?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before it was not working at all... I will add a few words about that

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

- now works with a standard installation of Elektra
- updated code to work with current Java binding

_(Michael Tucek)_

Expand Down
41 changes: 29 additions & 12 deletions doc/tutorials/java-kdb.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,34 +85,51 @@ The _user_ namespace is accessible without special rights, but if you try to wri

### Traversing Keys in a `KeySet`

First we create a new `KDB` handle and fetch all keys for the desired namespace, in this example the whole `user` namespace. Since all all keys are put in the passed `keySet` variable we can then iterate through it.
The `at(int)` method returns a new `Key` object for the native key with the corresponding position within the `keySet`.
Copy link
Contributor

Choose a reason for hiding this comment

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

new Key object sounds like that something new is created but this isn't the case, is it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually there is a new Key object (Java representation for the native key) created.


```java
var keyNamespace = Key.create("user:/"); // select a namespace from which all keys should be fetched
var keyNamespace = Key.create("user:/"); // select a namespace from which all keys should be fetched
try (KDB kdb = KDB.open()) {
var keySet = kdb.get(keyNamespace); // fetch all keys int new key set
for (int i = 0; i < keySet.length(); i++) { // traverse the set
var keySet = kdb.get(keyNamespace); // fetch all keys into a new key set
for (int i = 0; i < keySet.length(); i++) { // traverse the set
var currentKey = keySet.at(i);
System.out.println(String.format("%s: %s",
currentKey.getName(), // fetch the key's name
currentKey.getString())); // fetch the key's value
currentKey.getName(), // fetch the key's name
currentKey.getString())); // fetch the key's value
}
} catch (KDB.KDBException e) {
e.printStackTrace();
}
```

First we create a new `KDB` session and fetch all keys for the desired namespace, in this example the whole `user` namespace. Since all all keys are put in the passed `keySet` variable we can then iterate through it.
The `at(int)` method return a new `Key` object for the native key with the corresponding position within the `keySet`.
`KeySet` also provides an iterator implementation:

```java
var keyNamespace = Key.create("user:/"); // select a namespace from which all keys should be fetched
try (KDB kdb = KDB.open()) {
var iter = kdb.get(keyNamespace).iterator(); // fetch all keys into a new key set
while(iter.hasNext()) { // traverse the set
var currentKey = iter.next();
System.out.println(String.format("%s: %s",
currentKey.getName(), // fetch the key's name
currentKey.getString())); // fetch the key's value
}
} catch (KDB.KDBException e) {
e.printStackTrace();
}
```

Of course, alternatively a for each loop can be used:

```java
var keyNamespace = Key.create("user:/"); // select a namespace from which all keys should be fetched
var keyNamespace = Key.create("user:/"); // select a namespace from which all keys should be fetched
try (KDB kdb = KDB.open()) {
var keySet = kdb.get(keyNamespace); // fetch all keys int new key set
for (var currentKey : keySet) { // traverse the set
var keySet = kdb.get(keyNamespace); // fetch all keys into a new key set
for (var currentKey : keySet) { // traverse the set
System.out.println(String.format("%s: %s",
currentKey.getName(), // fetch the key's name
currentKey.getString())); // fetch the key's value
currentKey.getName(), // fetch the key's name
currentKey.getString())); // fetch the key's value
}
} catch (KDB.KDBException e) {
e.printStackTrace();
Expand Down
15 changes: 15 additions & 0 deletions examples/external/java/read-keys-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# ignore java class files
*.class

# ignore secret settings
secret-settings.gradle

# ignore gradle cache
/.gradle

# ignore eclipse project files
.project
.classpath
bin
target
.settings
30 changes: 26 additions & 4 deletions examples/external/java/read-keys-example/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
This is a fully working example for how to use Elektra with Maven or Gradle.
This is a fully working example for how to use Elektra with Maven or Gradle for reading keys.

Make sure Elektra and the JNA binding are installed correctly.
First Make sure to install the Elektra Java binding to your local maven repository:

See [JNA binding](../../../../src/bindings/jna/README.md) for more information.
```sh
cd ./src/bindings/jna
./gradlew publishLibelektraPublicationToMavenLocal
```

TODO add short intro how to build suing gradle or maven
Alternatively change the `libelektra` dependency in `build.gradle` to a published version, fitting your Elektra installation.
(See [JNA binding](../../../../src/bindings/jna/README.md) for more information.)

Then change to the example app folder:

```sh
cd ../../../examples/external/java/read-keys-example
```

Use a local Gradle installation to run the example app:

```sh
gradle run
```

Alternatively use a local Maven installation to run the example app:

```sh
mvn compile exec:java
```
4 changes: 2 additions & 2 deletions examples/external/java/read-keys-example/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
package org.libelektra.app;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
import org.libelektra.KDB;
import org.libelektra.KDBException;
import org.libelektra.Key;
import org.libelektra.KeySet;
import org.libelektra.exception.KDBException;

public class App
{

private final static String MOUNT_SPACE = "user:/";
private final static String KEY_PREFIX = MOUNT_SPACE + "/sw/clock/central/";
private static final String MOUNT_SPACE = "system:/";
private static final String KEY_PREFIX = MOUNT_SPACE + "elektra/version/infos/";

public static void main (String[] args)
{
System.out.println ("Example started");
Map<String, String> stringStringMap = loadConfigurationSettings ();
var stringStringMap = loadConfigurationSettings ();
System.out.println ("Result:");
System.out.println (stringStringMap);
System.out.println ("Example terminated");
Expand All @@ -27,57 +28,38 @@ public static void main (String[] args)
private static Map<String, String> loadConfigurationSettings ()
{
// all keys we want to retrieve
String[] keys = new String[] {
"server/port",
"spring/profiles/active",
"spring/datasource/type",
"spring/datasource/url",
"spring/datasource/username",
"spring/datasource/password",
"spring/jpa/database-platform",
"spring/jpa/database",
"spring/mail/host",
"spring/mail/port",
"jhipster/mail/from",
"jhipster/mail/base-url",
};
String[] keys = new String[] { "author", "description", "licence", "versions" };

// read the keys
return readKeys (keys);
}

private static Map<String, String> readKeys (String[] keys)
{
System.out.println ("Reading following keys:");
for (String key : keys)
{
System.out.println (key);
}
// create a key without specifying a name, which is allowed
Key key = Key.create ("");
Arrays.stream (keys).map (k -> KEY_PREFIX + k).forEach (System.out::println);

// open KDB with autoclose functionality
// keep in mind this is an expensive operation, avoid calling it too frequently
try (KDB kdb = KDB.open (key))
try (KDB kdb = KDB.open ())
{
// create keyset
KeySet keySet = KeySet.create ();
// set mount space
kdb.get (keySet, Key.create (MOUNT_SPACE));
// fetch key set
KeySet keySet = kdb.get (Key.create (MOUNT_SPACE));

// fetch values
return Arrays.stream (keys)
.map (k -> {
Key lookedUpValue = keySet.lookup (KEY_PREFIX + k);
// if null (not found), set it to null value
// if specification with default value is applied, this actually should never happen
return new String[] { k, (lookedUpValue != null) ? lookedUpValue.getString () : null };
})
// collect to map
.collect (Collectors.toMap (keyValue -> keyValue[0], keyValue -> keyValue[1]));
.map (k -> KEY_PREFIX + k)
.collect (Collectors.toMap (k
-> k,
// if not found, set it to "(no-spec-default)"
// if specification with default value is applied, this actually should never
// happen
k -> keySet.lookup (k).map (Key::getString).orElse ("(no-spec-default)")));
}
catch (KDBException e)
{
// a problem occured with kdb.get(keySet, Key.create(MOUNT_SPACE));
e.printStackTrace ();
}
return null;
return Collections.emptyMap ();
}
}
35 changes: 14 additions & 21 deletions src/bindings/jna/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,6 @@ if (Java_JAVAC_EXECUTABLE)
set (GRADLE_TEST_EXCLUDES "")
java_test_needs_plugin (GOptsTest gopts)

if ((CMAKE_SYSTEM_NAME STREQUAL "Darwin")
Copy link
Contributor

Choose a reason for hiding this comment

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

How is this change related to the PR?

Copy link
Contributor Author

@tucek tucek Aug 24, 2021

Choose a reason for hiding this comment

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

Previously, all tests have been explicitly added to not be executed on this mac os version. I took the opportunity to optimize the cmake file instead of adding more (new) tests to the exclusion list.

Copy link
Member

@mpranj mpranj Aug 26, 2021

Choose a reason for hiding this comment

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

Doesn't this basically exclude all related tests on all our CI macOS instances?

Actually running some tests would be a requirement before merging this PR.

AND NOT (CMAKE_SYSTEM_VERSION VERSION_LESS 15)
OR (NOT BUILD_TESTING))
# we cannot set DYLD_LIBRARY_PATH on new macOS versions, making the kdb tests fail if its not
# installed in the system
java_exclude_test (ExceptionMapperIT)
java_exclude_test (ExceptionMapperTest)
java_exclude_test (GOptsTest)
java_exclude_test (KDBExceptionTest)
java_exclude_test (KDBTest)
java_exclude_test (KeyNameIteratorTest)
java_exclude_test (KeySetTest)
java_exclude_test (KeyTest)
java_exclude_test (PluginLoaderIT)
java_exclude_test (ReturnTest)
java_exclude_test (WarningEntryTest)
endif ()

# first fill the elektra version in the pom file @ONLY to avoid replacing pom.xml placeholders which also
# use the format ${}
configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/version-settings.gradle.in"
Expand Down Expand Up @@ -114,13 +96,14 @@ if (Java_JAVAC_EXECUTABLE)
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/InterfaceException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/InternalException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KDBClosedException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeyBinaryTypeNotSupportedException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeyBinaryValueException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeyException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeyMetaException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeyNameException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeyReleasedException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeySetAppendException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeySetReleasedException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/KeyStringValueException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/LogicalException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/OutOfMemoryException.java
${JNA_BINDING_SOURCE_DIRECTORY_EXCEPTION}/package-info.java
Expand All @@ -141,6 +124,10 @@ if (Java_JAVAC_EXECUTABLE)
${JNA_BINDING_SOURCE_DIRECTORY_TEST_PLUGIN}/ReturnTest.java
${JNA_BINDING_PREFIX}/build.gradle
${JNA_BINDING_PREFIX}/dependencies.gradle
plugins/build.gradle
plugins/dependencies.gradle
plugins/whitelist/src/main/java/org/libelektra/plugin/WhitelistPlugin.java
plugins/whitelist/src/test/java/org/libelektra/plugin/WhitelistPluginTests.java
build.gradle
gradle.properties
java-library.gradle
Expand Down Expand Up @@ -180,8 +167,14 @@ if (Java_JAVAC_EXECUTABLE)
DESTINATION "share/java"
COMPONENT java-elektra)

# add gradle test to execute with ctest in the testing phase
if (BUILD_TESTING AND NOT ENABLE_ASAN)
# we cannot set DYLD_LIBRARY_PATH on new macOS versions, making the kdb tests fail if its not installed in
# the system
if (BUILD_TESTING
AND NOT ENABLE_ASAN
AND NOT
((CMAKE_SYSTEM_NAME STREQUAL "Darwin")
AND NOT (CMAKE_SYSTEM_VERSION VERSION_LESS 15)
OR (NOT BUILD_TESTING)))
add_test (
NAME testjna_gradle
COMMAND ${GRADLE_EXECUTABLE} -q --console=plain test
Expand Down
1 change: 0 additions & 1 deletion src/bindings/jna/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ You can run unit tests by invoking:

## Limitations

- no binary keys
- no Java iterator for metadata

## Release publishing to Maven Central
Expand Down
9 changes: 0 additions & 9 deletions src/bindings/jna/java-shared.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,6 @@ tasks.named('test').configure {
}
}

// apply optional test override
File testExclusions = new File('test-exclusion.gradle')
if (testExclusions.exists()) {
apply from: testExclusions
logger.lifecycle("Test exclusions have been applied.")
} else {
logger.lifecycle("Test exclusions file not found.")
}

// eclipse IDE integration
eclipse {
classpath {
Expand Down
9 changes: 9 additions & 0 deletions src/bindings/jna/libelektra/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
// apply project configuration for java libraries
apply from: "$rootProject.projectDir/java-library.gradle"

// apply optional test override
File testExclusions = new File('test-exclusion.gradle')
if (testExclusions.exists()) {
apply from: testExclusions
logger.lifecycle("Test exclusions have been applied.")
} else {
logger.lifecycle("Test exclusions file not found.")
}

// apply project dependencies
apply from: 'dependencies.gradle'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,9 @@ int elektraGOptsContractFromStrings (Pointer contractKeySet, long argsSize, Stri

int keySetString (Pointer key, String newString);

// int keyGetBinary(Pointer key, byte[] returnedBinary, int maxSize);
// int keySetBinary(Pointer key, byte[] newBinary, int dataSize);
int keyGetBinary (Pointer key, byte[] returnedBinary, int maxSize);

int keySetBinary (Pointer key, @Nullable byte[] newBinary, int dataSize);

// KeySet methods -----------------------------------------------------------

Expand Down
Loading