Skip to content

docs: New Readme #10

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

Merged
merged 7 commits into from
Oct 16, 2023
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
30 changes: 30 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*

.project
.classpath
.settings

target/
212 changes: 194 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
Extism Java-SDK
---
# Extism Java SDK

Java SDK for the [extism](https://extism.org/) WebAssembly Plugin-System.
Java SDK for the [Extism](https://extism.org/) WebAssembly Plugin-System.

> **Note**: This is an early 1.0 release and is unstable until we hit 1.0. If you are looking to integrate now consider looking at the 0.x version in the [extism/extism](https://github.com/extism/extism/tree/main/java) repo.
> **Note**: This is an early 1.0 release and is unstable until we hit 1.0 in December. If you are looking for a stable release consider the 0.x version in the [Extism/extism](https://github.com/extism/extism/tree/main/java) repo.

# Build
[![maven](https://img.shields.io/maven-central/v/org.extism.sdk/extism)](https://search.maven.org/artifact/org.extism.sdk/extism)
[![javadoc](https://javadoc.io/badge2/org.extism.sdk/extism/javadoc.svg)](https://javadoc.io/doc/org.extism.sdk/extism)

To build the extism java-sdk run the following command:
## Installation

```
mvn clean verify
### Install the Extism Runtime Dependency

For this library, you first need to install the Extism Runtime. You can [download the shared object directly from a release](https://github.com/extism/extism/releases) or use the [Extism CLI](https://github.com/extism/cli) to install it:

> **Note**: This library has breaking changes and targets 1.0 of the runtime. For the time being, install the runtime from our nightly development builds on git: `sudo extism lib install --version git`.

```bash
sudo extism lib install latest

#=> Fetching https://github.com/extism/extism/releases/download/v0.5.2/libextism-aarch64-apple-darwin-v0.5.2.tar.gz
#=> Copying libextism.dylib to /usr/local/lib/libextism.dylib
#=> Copying extism.h to /usr/local/include/extism.h
```

# Usage
### Install Jar

To use the extism java-sdk you need to add the `org.extism.sdk` dependency to your dependency management and ensure that
the native extism library is installed on your system. For installing the native library refer to the [extism documentation](https://extism.org/docs/install).
To use the Extism java-sdk you need to add the `org.extism.sdk` dependency to your dependency management system.

Instead of installing the native library on your system, you can also download the appropriate library for your platform
yourself.
To do that simply download the [extism release](https://github.com/extism/extism/releases) to a `folder` and run your java application with the system property `-Djna.library.path=/path/to/folder`.
#### Maven

## Maven
To use the extism java-sdk with maven you need to add the following dependency to your `pom.xml` file:
To use the Extism java-sdk with maven you need to add the following dependency to your `pom.xml` file:
```xml
<dependency>
<groupId>org.extism.sdk</groupId>
Expand All @@ -32,10 +38,180 @@ To use the extism java-sdk with maven you need to add the following dependency t
</dependency>
```

## Gradle
To use the extism java-sdk with maven you need to add the following dependency to your `build.gradle` file:
#### Gradle

To use the Extism java-sdk with maven you need to add the following dependency to your `build.gradle` file:

```
implementation 'org.extism.sdk:extism:1.0.0-rc1'
```

## Getting Started

This guide should walk you through some of the concepts in Extism and this java library.

### Creating A Plug-in

The primary concept in Extism is the [plug-in](https://extism.org/docs/concepts/plug-in). You can think of a plug-in as a code module stored in a `.wasm` file.
Since you may not have an Extism plug-in on hand to test, let's load a demo plug-in from the web:

```java
import org.extism.sdk.manifest.Manifest;
import org.extism.sdk.wasm.UrlWasmSource;
import org.extism.sdk.Plugin;

var url = "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm";
var manifest = new Manifest(List.of(UrlWasmSource.fromUrl(url)));
var plugin = new Plugin(manifest, false, null);
```

> **Note**: See [the Manifest docs](https://www.javadoc.io/doc/org.extism.sdk/extism/latest/org/extism/sdk/manifest/Manifest.html) as it has a rich schema and a lot of options.

### Calling A Plug-in's Exports

This plug-in was written in Rust and it does one thing, it counts vowels in a string. As such, it exposes one "export" function: `count_vowels`. We can call exports using [Plugin#call](https://www.javadoc.io/doc/org.extism.sdk/extism/latest/org/extism/sdk/Plugin.html#call(java.lang.String,byte[]))

```java
var output = plugin.call("count_vowels", "Hello, World!");
System.out.println(output);
// => "{"count": 3, "total": 3, "vowels": "aeiouAEIOU"}"
```

All exports have a simple interface of bytes-in and bytes-out.
This plug-in happens to take a string and return a JSON encoded string with a report of results.

### Plug-in State

Plug-ins may be stateful or stateless. Plug-ins can maintain state b/w calls by the use of variables.
Our count vowels plug-in remembers the total number of vowels it's ever counted in the "total" key in the result.
You can see this by making subsequent calls to the export:

```java
var output = plugin.call("count_vowels", "Hello, World!");
System.out.println(output);
// => "{"count": 3, "total": 6, "vowels": "aeiouAEIOU"}"

var output = plugin.call("count_vowels", "Hello, World!");
System.out.println(output);
// => "{"count": 3, "total": 9, "vowels": "aeiouAEIOU"}"
```

These variables will persist until this plug-in is freed or you initialize a new one.

### Configuration

Plug-ins may optionally take a configuration object. This is a static way to configure the plug-in.
Our count-vowels plugin takes an optional configuration to change out which characters are considered vowels. Example:

```java
var plugin = new Plugin(manifest, false, null);
var output = plugin.call("count_vowels", "Yellow, World!");
System.out.println(output);
// => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}

// Let's change the vowels config it uses to determine what is a vowel:
var config = Map.of("vowels", "aeiouyAEIOUY");
var manifest2 = new Manifest(List.of(UrlWasmSource.fromUrl(url)), null, config);
var plugin = new Plugin(manifest2, false, null);
var output = plugin.call("count_vowels", "Yellow, World!");
System.out.println(output);
// => {"count": 4, "total": 4, "vowels": "aeiouyAEIOUY"}
// ^ note count changed to 4 as we configured Y as a vowel this time
```

### Host Functions

Let's extend our count-vowels example a little bit: Instead of storing the `total` in an ephemeral plug-in var,
let's store it in a persistent key-value store!

Wasm can't use our app's KV store on its own. This is where [Host Functions](https://extism.org/docs/concepts/host-functions) come in.

[Host functions](https://extism.org/docs/concepts/host-functions) allow us to grant new capabilities to our plug-ins from our application.
They are simply some java methods you write which can be passed down and invoked from any language inside the plug-in.

Let's load the manifest like usual but load up this `count_vowels_kvstore` plug-in:

```java
var url = "https://github.com/extism/plugins/releases/latest/download/count_vowels_kvstore.wasm";
var manifest = new Manifest(List.of(UrlWasmSource.fromUrl(url)));
var plugin = new Plugin(manifest, false, null);
```

> *Note*: The source code for this plug-in is [here](https://github.com/extism/plugins/blob/main/count_vowels_kvstore/src/lib.rs)
> and is written in rust, but it could be written in any of our PDK languages.

Unlike our previous plug-in, this plug-in expects you to provide host functions that satisfy its import interface for a KV store.
We want to expose two functions to our plugin, `kv_write(String key, Bytes value)` which writes a bytes value to a key and `Bytes kv_read(String key)` which reads the bytes at the given `key`.

```java
// Our application KV store
// Pretend this is redis or a database :)
var kvStore = new HashMap<String, byte[]>();

ExtismFunction kvWrite = (plugin, params, returns, data) -> {
System.out.println("Hello from kv_write Java Function!");
var key = plugin.inputString(params[0]);
var value = plugin.inputBytes(params[1]);
System.out.println("Writing to key " + key);
kvStore.put(key, value);
};

ExtismFunction kvRead = (plugin, params, returns, data) -> {
System.out.println("Hello from kv_read Java Function!");
var key = plugin.inputString(params[0]);
System.out.println("Reading from key " + key);
var value = kvStore.get(key);
if (value == null) {
// default to zeroed bytes
var zero = new byte[]{0,0,0,0};
plugin.returnBytes(returns[0], zero);
} else {
plugin.returnBytes(returns[0], value);
}
};

HostFunction kvWriteHostFn = new HostFunction<>(
"kv_write",
new LibExtism.ExtismValType[]{LibExtism.ExtismValType.I64, LibExtism.ExtismValType.I64},
new LibExtism.ExtismValType[0],
kvWrite,
Optional.empty()
);

HostFunction kvReadHostFn = new HostFunction<>(
"kv_read",
new LibExtism.ExtismValType[]{LibExtism.ExtismValType.I64},
new LibExtism.ExtismValType[]{LibExtism.ExtismValType.I64},
kvRead,
Optional.empty()
);

```

> *Note*: In order to write host functions you should get familiar with the methods on the [ExtismCurrentPlugin](https://www.javadoc.io/doc/org.extism.sdk/extism/latest/org/extism/sdk/ExtismCurrentPlugin.html) class.
> The `plugin` parameter is an instance of this class.

Now we just need to pass in these function references when creating the plugin:.

```java
HostFunction[] functions = {kvWriteHostFn, kvReadHostFn};
var plugin = new Plugin(manifest, false, functions);
var output = plugin.call("count_vowels", "Hello, World!");
// => Hello from kv_read Java Function!
// => Reading from key count-vowels
// => Hello from kv_write Java Function!
// => Writing to key count-vowels
System.out.println(output);
// => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
```

## Development

# Build

To build the Extism java-sdk run the following command:

```
mvn clean verify
```

1 change: 1 addition & 0 deletions src/main/java/org/extism/sdk/Extism.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static void setLogFile(Path path, LogLevel level) {

/**
* Invokes the named {@code function} from the {@link Manifest} with the given {@code input}.
* This is a convenience method. Prefer initializing and using a {@link Plugin} where possible.
*
* @param manifest the manifest containing the function
* @param function the name of the function to call
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/org/extism/sdk/wasm/UrlWasmSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.extism.sdk.wasm;

/**
* WASM Source represented by a url.
*/
public class UrlWasmSource implements WasmSource {

private final String name;

private final String url;

private final String hash;

/**
* Provides a quick way to instantiate with just a url
*
* @param url String url to the wasm file
* @return
*/
public static UrlWasmSource fromUrl(String url) {
return new UrlWasmSource(null, url, null);
}

/**
* Constructor
* @param name
* @param url
* @param hash
*/
public UrlWasmSource(String name, String url, String hash) {
this.name = name;
this.url = url;
this.hash = hash;
}

@Override
public String name() {
return name;
}

@Override
public String hash() {
return hash;
}

public String url() {
return url;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.extism.sdk.support.Hashing;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
Expand All @@ -18,7 +19,6 @@ public PathWasmSource resolve(Path path) {
}

public PathWasmSource resolve(String name, Path path) {

Objects.requireNonNull(path, "path");

var wasmFile = path.toFile();
Expand Down
Loading