diff --git a/README.md b/README.md index d584dcf..96d3f3e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ The Java host implementation for Proxy-Wasm, enabling developers to run Proxy-Wa TODO: still need to document how to use in non-cdi containers. -If your using Quarkus, see the [Quarkus Proxy-Wasm Extension](https://github.com/roastedroot/quarkus-proxy-wasm) docs. +If your using Quarkus, see the [Quarkus Proxy-Wasm Extension](https://docs.quarkiverse.io/quarkus-proxy-wasm/dev/index.html) docs. ## Building diff --git a/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/Plugin.java b/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/Plugin.java index 73a5dc6..a102096 100644 --- a/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/Plugin.java +++ b/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/Plugin.java @@ -1,23 +1,10 @@ package io.roastedroot.proxywasm; -import static io.roastedroot.proxywasm.internal.Helpers.bytes; - -import com.dylibso.chicory.runtime.ImportMemory; -import com.dylibso.chicory.runtime.Instance; -import com.dylibso.chicory.runtime.Machine; -import com.dylibso.chicory.wasi.WasiOptions; -import com.dylibso.chicory.wasm.WasmModule; -import io.roastedroot.proxywasm.internal.ProxyWasm; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - /** * Represents a Proxy-WASM plugin, providing the bridge between the host * environment and the WASM module. * - *
Concrete Plugin instances are created using the {@link Builder}. + *
Concrete Plugin instances are created using a {@link PluginFactory}.
* The actual WASM instance and interaction logic are managed internally.
*/
public interface Plugin {
@@ -28,333 +15,4 @@ public interface Plugin {
* @return the plugin name, which might be null if not explicitly set via the builder.
*/
String name();
-
- /**
- * Creates a new {@link Builder} to configure and construct a {@link Plugin} instance
- * from the given WASM module.
- *
- * @param module the compiled {@link WasmModule} representing the plugin's code.
- * @return a new {@link Plugin.Builder} instance.
- */
- static Plugin.Builder builder(WasmModule module) {
- return new Plugin.Builder(module);
- }
-
- /**
- * Builder for creating a Plugin instance.
- */
- final class Builder {
-
- private final WasmModule module;
- private final ProxyWasm.Builder proxyWasmBuilder = ProxyWasm.builder().withStart(false);
- private boolean shared;
- private String name;
- private HashMap If {@code strictUpstreams} is {@code true}, attempting to use an undefined upstream name
- * will result in an error being reported back to the plugin.
- *
- * If {@code strictUpstreams} is {@code false} (the default behavior if this method is not called),
- * the host will try to parse the upstream name as URI.
- *
- * @param strictUpstreams {@code true} to enforce that all used upstream names must be explicitly mapped,
- * {@code false} to allow fallback resolution.
- * @return this {@code Builder} instance for method chaining.
- */
- public Builder withStrictUpstreams(boolean strictUpstreams) {
- this.strictUpstreams = strictUpstreams;
- return this;
- }
-
- /**
- * Sets a minimum interval for the plugin's periodic timer ticks ({@code proxy_on_tick}).
- * The Proxy-WASM ABI allows plugins to request a timer tick period. This setting enforces
- * a lower bound on that period to prevent plugins from requesting excessively frequent ticks,
- * which could overload the host.
- *
- * If the plugin requests a tick period shorter than this minimum, the host will use
- * this minimum value instead.
- *
- * @param minTickPeriodMilliseconds the minimum allowed tick period in milliseconds. A value of 0 or less
- * implies no minimum enforcement (host default behavior).
- * @return this {@code Builder} instance for method chaining.
- */
- public Builder withMinTickPeriodMilliseconds(int minTickPeriodMilliseconds) {
- this.minTickPeriodMilliseconds = minTickPeriodMilliseconds;
- return this;
- }
-
- /**
- * Provides a {@link LogHandler} implementation for the plugin to use.
- * This handler receives log messages generated by the WASM module via the {@code proxy_log} ABI call.
- * If no logger is provided, {@link LogHandler#DEFAULT} (a no-op logger) is used.
- *
- * @param logger the {@link LogHandler} implementation to handle plugin logs.
- * @return this {@code Builder} instance for method chaining.
- * @see LogHandler
- */
- public Builder withLogger(LogHandler logger) {
- this.logger = logger;
- return this;
- }
-
- /**
- * Provides a {@link MetricsHandler} implementation for the plugin to use.
- * This handler manages metric definition, recording, and retrieval requested by the WASM module
- * via the relevant {@code proxy_*} ABI calls (e.g., {@code proxy_define_metric}).
- * If no handler is provided, {@link MetricsHandler#DEFAULT} (which returns UNIMPLEMENTED)
- * might be used implicitly.
- *
- * @param metricsHandler the {@link MetricsHandler} implementation to manage plugin metrics.
- * @return this {@code Builder} instance for method chaining.
- * @see MetricsHandler
- */
- public Builder withMetricsHandler(MetricsHandler metricsHandler) {
- this.metricsHandler = metricsHandler;
- return this;
- }
-
- /**
- * Provides a {@link SharedQueueHandler} implementation for the plugin to use.
- * This handler manages operations on shared message queues requested by the WASM module
- * via the relevant {@code proxy_*} ABI calls (e.g., {@code proxy_register_shared_queue}).
- * If no handler is provided, {@link SharedQueueHandler#DEFAULT} (which returns UNIMPLEMENTED)
- * might be used implicitly.
- *
- * @param sharedQueueHandler the {@link SharedQueueHandler} implementation to manage shared queues.
- * @return this {@code Builder} instance for method chaining.
- * @see SharedQueueHandler
- */
- public Builder withSharedQueueHandler(SharedQueueHandler sharedQueueHandler) {
- this.sharedQueueHandler = sharedQueueHandler;
- return this;
- }
-
- /**
- * Provides a {@link SharedDataHandler} implementation for the plugin to use.
- * This handler manages operations on shared key-value data requested by the WASM module
- * via the relevant {@code proxy_*} ABI calls (e.g., {@code proxy_get_shared_data}).
- * If no handler is provided, {@link SharedDataHandler#DEFAULT} (which returns UNIMPLEMENTED)
- * might be used implicitly.
- *
- * @param sharedDataHandler the {@link SharedDataHandler} implementation to manage shared data.
- * @return this {@code Builder} instance for method chaining.
- * @see SharedDataHandler
- */
- public Builder withSharedDataHandler(SharedDataHandler sharedDataHandler) {
- this.sharedDataHandler = sharedDataHandler;
- return this;
- }
-
- /**
- * Configures whether the plugin instance should be shared across multiple host requests or contexts.
- *
- * If {@code shared} is {@code true}, a single WASM instance will be created and reused.
- * across multiple concurrent requests. Since Proxy-Wasm plugins are not thread-safe, the requests will
- * contend on an access lock for the plugin. Using a shared plugin allows the plugin to maintain state
- * between the requests. It will use less memory but will have a performance impact due to the contention.
- *
- * If {@code shared} is {@code false} (the default), the host will create a new, separate WASM instance for each
- * request or context (depending on the host implementation and threading model). This provides better
- * isolation, eliminates contention, but consumes more memory.
- *
- * @param shared {@code true} to indicate the plugin instance can be shared, {@code false} otherwise.
- * @return this {@code Builder} instance for method chaining.
- */
- public Builder withShared(boolean shared) {
- this.shared = shared;
- return this;
- }
-
- /**
- * Sets the Virtual Machine (VM) configuration data for the plugin.
- * This configuration is typically provided once when the VM (and the plugin) is initialized.
- * It's accessible to the plugin via the {@code proxy_get_vm_configuration} ABI call.
- *
- * @param vmConfig A byte array containing the VM configuration data.
- * @return this {@code Builder} instance for method chaining.
- */
- public Builder withVmConfig(byte[] vmConfig) {
- this.vmConfig = vmConfig;
- return this;
- }
-
- /**
- * Sets the Virtual Machine (VM) configuration data for the plugin using a String.
- * The string will be converted to bytes using the platform's default charset.
- * This configuration is accessible via the {@code proxy_get_vm_configuration} ABI call.
- *
- * @param vmConfig A String containing the VM configuration data.
- * @return this {@code Builder} instance for method chaining.
- * @see #withVmConfig(byte[])
- */
- public Builder withVmConfig(String vmConfig) {
- this.vmConfig = bytes(vmConfig);
- return this;
- }
-
- /**
- * Sets the specific configuration data for this plugin instance.
- * This configuration is provided during the plugin's initialization phase
- * (via {@code proxy_on_configure}) and allows tailoring the plugin's behavior.
- * It's accessible to the plugin via the {@code proxy_get_plugin_configuration} ABI call.
- *
- * @param pluginConfig A byte array containing the plugin-specific configuration data.
- * @return this {@code Builder} instance for method chaining.
- */
- public Builder withPluginConfig(byte[] pluginConfig) {
- this.pluginConfig = pluginConfig;
- return this;
- }
-
- /**
- * Sets the specific configuration data for this plugin instance using a String.
- * The string will be converted to bytes using the platform's default charset.
- * This configuration is accessible via the {@code proxy_get_plugin_configuration} ABI call.
- *
- * @param pluginConfig A String containing the plugin-specific configuration data.
- * @return this {@code Builder} instance for method chaining.
- * @see #withPluginConfig(byte[])
- */
- public Builder withPluginConfig(String pluginConfig) {
- this.pluginConfig = bytes(pluginConfig);
- return this;
- }
-
- /**
- * Provides an explicit memory instance to be used by the WASM module.
- *
- * @param memory The {@link ImportMemory} instance to be used by the WASM module.
- * @return this {@code Builder} instance for method chaining.
- */
- public Builder withImportMemory(ImportMemory memory) {
- proxyWasmBuilder.withImportMemory(memory);
- return this;
- }
-
- /**
- * Configures a custom factory for creating the {@link Machine} used to execute the WASM code.
- * The {@link Machine} controls the low-level execution of WASM instructions.
- * By default, an interpreter-based machine is used.
- * Providing a custom factory allows using alternative execution strategies, such as
- * wasm to bytecode compilation to improve execution performance.
- *
- * See the Chicory documentation (https://chicory.dev/docs/usage/runtime-compiler) for more details
- * on WASM to bytecode compilation and execution.
- *
- * @param machineFactory A function that takes a WASM {@link Instance} and returns a {@link Machine}.
- * @return this {@code Builder} instance for method chaining.
- */
- public Builder withMachineFactory(Function This involves setting up the WASM environment, linking host functions, applying configurations,
- * and calling the necessary Proxy-WASM lifecycle functions (like {@code _start} and
- * {@code proxy_on_vm_start}).
- *
- * @return The fully configured and initialized {@link Plugin} instance.
- * @throws StartException If any error occurs during the plugin initialization process
- * (e.g., WASM instantiation failure, error during {@code proxy_on_vm_start}).
- */
- public Plugin build() throws StartException {
- return new io.roastedroot.proxywasm.internal.Plugin(
- proxyWasmBuilder.build(module),
- shared,
- name,
- foreignFunctions,
- upstreams,
- strictUpstreams,
- minTickPeriodMilliseconds,
- logger,
- vmConfig,
- pluginConfig,
- metricsHandler,
- sharedQueueHandler,
- sharedDataHandler);
- }
- }
}
diff --git a/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/PluginFactory.java b/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/PluginFactory.java
index 879b131..06ecfd3 100644
--- a/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/PluginFactory.java
+++ b/proxy-wasm-java-host/src/main/java/io/roastedroot/proxywasm/PluginFactory.java
@@ -1,5 +1,18 @@
package io.roastedroot.proxywasm;
+import static io.roastedroot.proxywasm.internal.Helpers.bytes;
+
+import com.dylibso.chicory.runtime.ImportMemory;
+import com.dylibso.chicory.runtime.Instance;
+import com.dylibso.chicory.runtime.Machine;
+import com.dylibso.chicory.wasi.WasiOptions;
+import com.dylibso.chicory.wasm.WasmModule;
+import io.roastedroot.proxywasm.internal.ProxyWasm;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
/**
* A functional interface representing a factory for creating {@link Plugin} instances.
*
@@ -8,8 +21,8 @@
* Implementations might handle loading WASM modules, configuring builders, and returning
* the ready-to-use plugin.
*/
-@FunctionalInterface
public interface PluginFactory {
+
/**
* Creates and returns a new {@link Plugin} instance.
* Implementations are responsible for all necessary setup, including potentially
@@ -20,4 +33,380 @@ public interface PluginFactory {
* initialization errors within the plugin's start lifecycle).
*/
Plugin create() throws Exception;
+
+ /**
+ * Returns the configured name of the plugin.
+ *
+ * @return the plugin name.
+ */
+ String name();
+
+ /**
+ * Indicates whether this plugin instance is shared across multiple contexts or requests.
+ *
+ * If {@code true}, the plugin will be instantiated once and reused, allowing it to maintain state
+ * between requests but potentially introducing contention. If {@code false}, a new instance will be created
+ * for each request or context, providing better isolation but consuming more memory.
+ *
+ * @return {@code true} if the plugin instance is shared, {@code false} otherwise.
+ */
+ boolean shared();
+
+ /**
+ * Creates a new {@link Builder} to configure and construct a {@link PluginFactory} instance
+ * from the given WASM module.
+ *
+ * @param module the compiled {@link WasmModule} representing the plugin's code.
+ * @return a new {@link PluginFactory.Builder} instance.
+ */
+ static PluginFactory.Builder builder(WasmModule module) {
+ return new PluginFactory.Builder(module);
+ }
+
+ /**
+ * Builder for creating a PluginFactory instance that can create Plugin instances
+ * with pre-configured settings.
+ */
+ final class Builder {
+
+ private final WasmModule module;
+ private final ProxyWasm.Builder proxyWasmBuilder = ProxyWasm.builder().withStart(false);
+ private String name;
+ private HashMap If {@code strictUpstreams} is {@code true}, attempting to use an undefined upstream name
+ * will result in an error being reported back to the plugin.
+ *
+ * If {@code strictUpstreams} is {@code false} (the default behavior if this method is not called),
+ * the host will try to parse the upstream name as URI.
+ *
+ * @param strictUpstreams {@code true} to enforce that all used upstream names must be explicitly mapped,
+ * {@code false} to allow fallback resolution.
+ * @return this {@code Builder} instance for method chaining.
+ */
+ public PluginFactory.Builder withStrictUpstreams(boolean strictUpstreams) {
+ this.strictUpstreams = strictUpstreams;
+ return this;
+ }
+
+ /**
+ * Sets a minimum interval for the plugin's periodic timer ticks ({@code proxy_on_tick}).
+ * The Proxy-WASM ABI allows plugins to request a timer tick period. This setting enforces
+ * a lower bound on that period to prevent plugins from requesting excessively frequent ticks,
+ * which could overload the host.
+ *
+ * If the plugin requests a tick period shorter than this minimum, the host will use
+ * this minimum value instead.
+ *
+ * @param minTickPeriodMilliseconds the minimum allowed tick period in milliseconds. A value of 0 or less
+ * implies no minimum enforcement (host default behavior).
+ * @return this {@code Builder} instance for method chaining.
+ */
+ public PluginFactory.Builder withMinTickPeriodMilliseconds(int minTickPeriodMilliseconds) {
+ this.minTickPeriodMilliseconds = minTickPeriodMilliseconds;
+ return this;
+ }
+
+ /**
+ * Provides a {@link LogHandler} implementation for the plugin to use.
+ * This handler receives log messages generated by the WASM module via the {@code proxy_log} ABI call.
+ * If no logger is provided, {@link LogHandler#DEFAULT} (a no-op logger) is used.
+ *
+ * @param logger the {@link LogHandler} implementation to handle plugin logs.
+ * @return this {@code Builder} instance for method chaining.
+ * @see LogHandler
+ */
+ public PluginFactory.Builder withLogger(LogHandler logger) {
+ this.logger = logger;
+ return this;
+ }
+
+ /**
+ * Provides a {@link MetricsHandler} implementation for the plugin to use.
+ * This handler manages metric definition, recording, and retrieval requested by the WASM module
+ * via the relevant {@code proxy_*} ABI calls (e.g., {@code proxy_define_metric}).
+ * If no handler is provided, {@link MetricsHandler#DEFAULT} (which returns UNIMPLEMENTED)
+ * might be used implicitly.
+ *
+ * @param metricsHandler the {@link MetricsHandler} implementation to manage plugin metrics.
+ * @return this {@code Builder} instance for method chaining.
+ * @see MetricsHandler
+ */
+ public PluginFactory.Builder withMetricsHandler(MetricsHandler metricsHandler) {
+ this.metricsHandler = metricsHandler;
+ return this;
+ }
+
+ /**
+ * Provides a {@link SharedQueueHandler} implementation for the plugin to use.
+ * This handler manages operations on shared message queues requested by the WASM module
+ * via the relevant {@code proxy_*} ABI calls (e.g., {@code proxy_register_shared_queue}).
+ * If no handler is provided, {@link SharedQueueHandler#DEFAULT} (which returns UNIMPLEMENTED)
+ * might be used implicitly.
+ *
+ * @param sharedQueueHandler the {@link SharedQueueHandler} implementation to manage shared queues.
+ * @return this {@code Builder} instance for method chaining.
+ * @see SharedQueueHandler
+ */
+ public PluginFactory.Builder withSharedQueueHandler(SharedQueueHandler sharedQueueHandler) {
+ this.sharedQueueHandler = sharedQueueHandler;
+ return this;
+ }
+
+ /**
+ * Provides a {@link SharedDataHandler} implementation for the plugin to use.
+ * This handler manages operations on shared key-value data requested by the WASM module
+ * via the relevant {@code proxy_*} ABI calls (e.g., {@code proxy_get_shared_data}).
+ * If no handler is provided, {@link SharedDataHandler#DEFAULT} (which returns UNIMPLEMENTED)
+ * might be used implicitly.
+ *
+ * @param sharedDataHandler the {@link SharedDataHandler} implementation to manage shared data.
+ * @return this {@code Builder} instance for method chaining.
+ * @see SharedDataHandler
+ */
+ public PluginFactory.Builder withSharedDataHandler(SharedDataHandler sharedDataHandler) {
+ this.sharedDataHandler = sharedDataHandler;
+ return this;
+ }
+
+ /**
+ * Sets the Virtual Machine (VM) configuration data for the plugin.
+ * This configuration is typically provided once when the VM (and the plugin) is initialized.
+ * It's accessible to the plugin via the {@code proxy_get_vm_configuration} ABI call.
+ *
+ * @param vmConfig A byte array containing the VM configuration data.
+ * @return this {@code Builder} instance for method chaining.
+ */
+ public PluginFactory.Builder withVmConfig(byte[] vmConfig) {
+ this.vmConfig = vmConfig;
+ return this;
+ }
+
+ /**
+ * Sets the Virtual Machine (VM) configuration data for the plugin using a String.
+ * The string will be converted to bytes using the platform's default charset.
+ * This configuration is accessible via the {@code proxy_get_vm_configuration} ABI call.
+ *
+ * @param vmConfig A String containing the VM configuration data.
+ * @return this {@code Builder} instance for method chaining.
+ * @see #withVmConfig(byte[])
+ */
+ public PluginFactory.Builder withVmConfig(String vmConfig) {
+ this.vmConfig = bytes(vmConfig);
+ return this;
+ }
+
+ /**
+ * Sets the specific configuration data for this plugin instance.
+ * This configuration is provided during the plugin's initialization phase
+ * (via {@code proxy_on_configure}) and allows tailoring the plugin's behavior.
+ * It's accessible to the plugin via the {@code proxy_get_plugin_configuration} ABI call.
+ *
+ * @param pluginConfig A byte array containing the plugin-specific configuration data.
+ * @return this {@code Builder} instance for method chaining.
+ */
+ public PluginFactory.Builder withPluginConfig(byte[] pluginConfig) {
+ this.pluginConfig = pluginConfig;
+ return this;
+ }
+
+ /**
+ * Sets the specific configuration data for this plugin instance using a String.
+ * The string will be converted to bytes using the platform's default charset.
+ * This configuration is accessible via the {@code proxy_get_plugin_configuration} ABI call.
+ *
+ * @param pluginConfig A String containing the plugin-specific configuration data.
+ * @return this {@code Builder} instance for method chaining.
+ * @see #withPluginConfig(byte[])
+ */
+ public PluginFactory.Builder withPluginConfig(String pluginConfig) {
+ this.pluginConfig = bytes(pluginConfig);
+ return this;
+ }
+
+ /**
+ * Provides an explicit memory instance to be used by the WASM module.
+ *
+ * @param memory The {@link ImportMemory} instance to be used by the WASM module.
+ * @return this {@code Builder} instance for method chaining.
+ */
+ public PluginFactory.Builder withImportMemory(ImportMemory memory) {
+ proxyWasmBuilder.withImportMemory(memory);
+ return this;
+ }
+
+ /**
+ * Configures a custom factory for creating the {@link Machine} used to execute the WASM code.
+ * The {@link Machine} controls the low-level execution of WASM instructions.
+ * By default, an interpreter-based machine is used.
+ * Providing a custom factory allows using alternative execution strategies, such as
+ * wasm to bytecode compilation to improve execution performance.
+ *
+ * See the Chicory documentation (https://chicory.dev/docs/usage/runtime-compiler) for more details
+ * on WASM to bytecode compilation and execution.
+ *
+ * @param machineFactory A function that takes a WASM {@link Instance} and returns a {@link Machine}.
+ * @return this {@code Builder} instance for method chaining.
+ */
+ public PluginFactory.Builder withMachineFactory(
+ Function If {@code shared} is {@code true}, a single WASM instance will be created and reused.
+ * across multiple concurrent requests. Since Proxy-Wasm plugins are not thread-safe, the requests will
+ * contend on an access lock for the plugin. Using a shared plugin allows the plugin to maintain state
+ * between the requests. It will use less memory but will have a performance impact due to the contention.
+ *
+ * If {@code shared} is {@code false} (the default), the host will create a new, separate WASM instance for each
+ * request or context (depending on the host implementation and threading model). This provides better
+ * isolation, eliminates contention, but consumes more memory.
+ *
+ * @param shared {@code true} to indicate the plugin instance can be shared, {@code false} otherwise.
+ * @return this {@code Builder} instance for method chaining.
+ */
+ public PluginFactory.Builder withShared(boolean shared) {
+ this.shared = shared;
+ return this;
+ }
+
+ /**
+ * Constructs a {@link PluginFactory} instance that will create {@link Plugin} instances
+ * using the configuration provided to this builder.
+ *
+ * @return A {@link PluginFactory} that creates plugins with the specified configuration.
+ */
+ public PluginFactory build() {
+
+ // Create deep copies of builder fields for immutability in multi-threaded environments.
+ String name = this.name;
+ HashMap