Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import io.roastedroot.proxywasm.jaxrs.WasmPluginFeature;
import io.roastedroot.proxywasm.jaxrs.example.App;
import io.roastedroot.proxywasm.jaxrs.example.Resources;
import io.roastedroot.proxywasm.jaxrs.internal.ServerAdaptor;
import io.roastedroot.proxywasm.jaxrs.internal.BlockingServerAdaptor;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
Expand Down Expand Up @@ -33,7 +33,7 @@ public void setUp() throws Exception {
// Create mock Instance<PluginFactory> for WasmPluginFeature
resourceConfig.register(
new WasmPluginFeature(
new ServerAdaptor(),
new BlockingServerAdaptor(),
App.headerTests(),
App.headerTestsNotShared(),
App.tickTests(),
Expand Down
17 changes: 17 additions & 0 deletions proxy-wasm-jaxrs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,23 @@
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${javadoc-plugin.version}</version>
<configuration>
<sourceFileExcludes>
<sourceFileExclude>**/internal/**</sourceFileExclude>
</sourceFileExcludes>
<offlineLinks>
<offlineLink>
<url>https://www.javadoc.io/doc/io.roastedroot/proxy-wasm-java-host/${project.version}/</url>
<location>${project.basedir}/../proxy-wasm-java-host/target/reports/apidocs</location>
</offlineLink>
</offlineLinks>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,25 @@
import java.lang.annotation.Target;

/**
* This annotation is used to mark a JAX-RS resource class or method
* as being filters by one or more Proxy-Wasm plugins.
* A JAX-RS {@link NameBinding} annotation used to mark resource classes or methods
* that should be intercepted and processed by the Proxy-Wasm plugins.
*
* <p>Apply this annotation to JAX-RS resource classes or methods to enable filtering
* by the Proxy-Wasm plugins identified by the names specified in the {@link #value()} attribute.
* The {@link WasmPluginFeature} must be registered for this annotation to have effect.
*
* @see WasmPluginFeature
* @see WasmPluginFilter
*/
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface WasmPlugin {
/**
* Specifies the names of the Proxy-Wasm plugins that should filter the annotated
* resource class or method.
*
* @return An array of plugin names.
*/
String[] value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,78 @@
import io.roastedroot.proxywasm.internal.ServerAdaptor;
import io.roastedroot.proxywasm.jaxrs.internal.AbstractWasmPluginFeature;
import java.util.Arrays;
import java.util.List;

/**
* WasmPluginFeature is a JAX-RS feature that allows the use of Proxy-Wasm plugins to filter JAX-RS
* requests.
* A JAX-RS {@link jakarta.ws.rs.core.Feature} that enables Proxy-Wasm plugin filtering
* for JAX-RS applications. This feature registers the necessary {@link WasmPluginFilter}
* to intercept requests and responses for resources annotated with {@link WasmPlugin}.
*
* <p>To use this feature, register an instance of it with your JAX-RS application, providing
* the required {@link ServerAdaptor} and a list of {@link PluginFactory} instances.
*
* <p>If you are using a CDI container like quarkus, you will be using the
* {@link io.roastedroot.proxywasm.jaxrs.cdi.WasmPluginFeature} instead.
*
* <pre>{@code
* public class MyApplication extends jakarta.ws.rs.core.Application {
* @Override
* public Set<Class<?>> getClasses() {
* Set<Class<?>> resources = new HashSet<>();
* resources.add(MyResource.class);
* return resources;
* }
*
* @Override
* public Set<Object> getSingletons() {
* Set<Object> singletons = new HashSet<>();
* try {
* // Assuming a ServerAdaptor and PluginFactory are available
* ServerAdaptor serverAdaptor = ...;
* PluginFactory myPluginFactory = ...;
* singletons.add(new WasmPluginFeature(serverAdaptor, myPluginFactory));
* } catch (StartException e) {
* throw new RuntimeException("Failed to initialize WasmPluginFeature", e);
* }
* return singletons;
* }
* }
* }</pre>
*
* @see WasmPlugin
* @see WasmPluginFilter
* @see PluginFactory
* @see ServerAdaptor
*/
public class WasmPluginFeature extends AbstractWasmPluginFeature {

/**
* Constructs a new WasmPluginFeature.
*
* @param httpServer The {@link ServerAdaptor} used to adapt JAX-RS specific request/response
* objects for the Proxy-Wasm host.
* @param factories One or more {@link PluginFactory} instances used to create and manage
* the Proxy-Wasm plugins.
* @throws StartException If an error occurs during the initialization or startup of the
* underlying Proxy-Wasm plugins.
*/
public WasmPluginFeature(ServerAdaptor httpServer, PluginFactory... factories)
throws StartException {
init(Arrays.asList(factories), httpServer);
this(httpServer, Arrays.asList(factories));
}

/**
* Constructs a new WasmPluginFeature with a list of factories.
*
* @param httpServer The {@link ServerAdaptor} used to adapt JAX-RS specific request/response
* objects for the Proxy-Wasm host.
* @param factories A list of {@link PluginFactory} instances used to create and manage
* the Proxy-Wasm plugins.
* @throws StartException If an error occurs during the initialization or startup of the
* underlying Proxy-Wasm plugins.
*/
public WasmPluginFeature(ServerAdaptor httpServer, List<PluginFactory> factories)
throws StartException {
init(factories, httpServer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,18 @@
import java.util.List;

/**
* This class implements the JAX-RS filters to intercept requests and responses by
* one or more Proxy-Wasm plugins.
* Implements the JAX-RS {@link ContainerRequestFilter}, {@link ContainerResponseFilter},
* and {@link WriterInterceptor} interfaces to intercept HTTP requests and responses,
* allowing Proxy-Wasm plugins to process them.
*
* <p>This filter is registered by the {@link WasmPluginFeature}. It interacts with
* {@link Plugin} instances obtained from configured {@link Pool}s to execute the
* appropriate Proxy-Wasm ABI functions (e.g., {@code on_http_request_headers},
* {@code on_http_response_body}) at different stages of the JAX-RS request/response lifecycle.
*
* @see WasmPluginFeature
* @see WasmPlugin
* @see Plugin
*/
public class WasmPluginFilter
implements ContainerRequestFilter, WriterInterceptor, ContainerResponseFilter {
Expand All @@ -32,10 +42,27 @@ public class WasmPluginFilter

private final List<Pool> pluginPools;

public WasmPluginFilter(List<Pool> pluginPool) {
this.pluginPools = List.copyOf(pluginPool);
/**
* Constructs a WasmPluginFilter.
*
* @param pluginPools A list of {@link Pool} instances, each managing a pool of {@link Plugin}
* instances for a specific Wasm module.
*/
public WasmPluginFilter(List<Pool> pluginPools) {
this.pluginPools = List.copyOf(pluginPools);
}

/**
* Intercepts incoming JAX-RS requests before they reach the resource method.
*
* <p>This method iterates through the configured plugin pools, borrows a {@link Plugin}
* instance from each, creates a {@link PluginHttpContext}, and calls the plugin's
* {@code on_http_request_headers} and potentially {@code on_http_request_body} functions.
* It handles potential early responses or modifications dictated by the plugins.
*
* @param requestContext The JAX-RS request context.
* @throws IOException If an I/O error occurs, typically during body processing.
*/
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
for (var pluginPool : pluginPools) {
Expand Down Expand Up @@ -116,6 +143,19 @@ private static Response interalServerError() {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}

/**
* Intercepts outgoing JAX-RS responses before the entity is written.
*
* <p>This method iterates through the configured plugin pools, retrieves the
* {@link PluginHttpContext} created during the request phase, and calls the plugin's
* {@code on_http_response_headers} function. It handles potential modifications to the
* response headers dictated by the plugins. If the response has no entity but the plugin
* implements {@code on_http_response_body}, it invokes that callback as well.
*
* @param requestContext The JAX-RS request context.
* @param responseContext The JAX-RS response context.
* @throws IOException If an I/O error occurs.
*/
@Override
public void filter(
ContainerRequestContext requestContext, ContainerResponseContext responseContext)
Expand Down Expand Up @@ -193,6 +233,20 @@ private void filter(
}
}

/**
* Intercepts the response body writing process.
*
* <p>This method is called when the JAX-RS framework is about to serialize and write
* the response entity. It captures the original response body, allows plugins
* (via {@code on_http_response_body}) to inspect or modify it, and then writes the
* potentially modified body to the original output stream. It handles potential
* early responses dictated by the plugins during body processing.
*
* @param ctx The JAX-RS writer interceptor context.
* @throws IOException If an I/O error occurs during stream processing.
* @throws WebApplicationException If a plugin decides to abort processing and send an
* alternative response during body filtering.
*/
@Override
public void aroundWriteTo(WriterInterceptorContext ctx)
throws IOException, WebApplicationException {
Expand Down Expand Up @@ -258,7 +312,7 @@ public void aroundWriteTo(WriterInterceptorContext ctx)
}
}

public Response toResponse(SendResponse other) {
Response toResponse(SendResponse other) {
Response.ResponseBuilder builder =
Response.status(other.statusCode(), string(other.statusCodeDetails()));
if (other.headers() != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
package io.roastedroot.proxywasm.jaxrs.cdi;

import io.roastedroot.proxywasm.jaxrs.internal.BlockingServerAdaptor;
import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Alternative;

/**
* ServerAdaptor is a CDI alternative for the {@link io.roastedroot.proxywasm.jaxrs.internal.ServerAdaptor}
* A CDI {@link Alternative} bean providing an implementation of
* {@link io.roastedroot.proxywasm.internal.ServerAdaptor} tailored for JAX-RS environments
* managed by CDI (Contexts and Dependency Injection).
*
* <p>This bean allows the Proxy-Wasm host integration to be seamlessly managed within
* a CDI container (like Quarkus or Helidon). By being an {@code @Alternative} with a
* specific {@code @Priority}, it can be selected or overridden as needed within the
* CDI application configuration.
*
* <p>It extends {@link BlockingServerAdaptor}, inheriting the
* base JAX-RS adaptation logic.
*
* @see io.roastedroot.proxywasm.internal.ServerAdaptor
* @see Alternative
* @see ApplicationScoped
*/
@Alternative
@Priority(100)
@ApplicationScoped
public class ServerAdaptor extends io.roastedroot.proxywasm.jaxrs.internal.ServerAdaptor {}
public class ServerAdaptor extends BlockingServerAdaptor {

/**
* Default constructor required by CDI for proxying and bean management.
*/
public ServerAdaptor() {
super();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,30 @@
import jakarta.ws.rs.ext.Provider;

/**
* This class is a CDI provider for the WasmPluginFeature.
* It initializes the plugin factories and server adaptor.
* It also handles the lifecycle of the feature.
* A CDI-managed JAX-RS {@link Provider} and {@link jakarta.ws.rs.core.Feature} that automatically
* configures and registers Proxy-Wasm plugin filtering within a CDI environment.
*
* <p>This bean acts as the bridge between the CDI container and the Proxy-Wasm JAX-RS integration.
* It automatically discovers and injects {@link PluginFactory} beans your application provides.
*
* It will use the best {@link ServerAdaptor} bean alternative that it can find for your CDI environment.
*
* <p>Simply ensure this bean is available in your CDI application (e.g., through scanning or
* explicit declaration), and any required {@link PluginFactory} beans are also defined. This feature
* will then automatically register the necessary filters.
*
* <p>Note: This class is intended for use in CDI environments. If you are not using CDI, you can
* use the {@link io.roastedroot.proxywasm.jaxrs.WasmPluginFeature} class directly to register the
* feature with your JAX-RS application.
*
* @see io.roastedroot.proxywasm.jaxrs.WasmPluginFeature
* @see io.roastedroot.proxywasm.jaxrs.WasmPluginFilter
* @see PluginFactory
* @see ServerAdaptor
* @see Provider
* @see ApplicationScoped
* @see PostConstruct
* @see PreDestroy
*/
@Provider
@ApplicationScoped
Expand All @@ -25,12 +46,29 @@ public class WasmPluginFeature extends AbstractWasmPluginFeature {

@Inject @Any ServerAdaptor serverAdaptor;

/**
* Creates a new instance of the WasmPluginFeature.
*/
public WasmPluginFeature() {}

/**
* Initializes the WasmPluginFeature using injected CDI dependencies.
* This method is automatically called by the CDI container after dependency injection
* is complete.
*
* @throws StartException If an error occurs during the initialization or startup of the
* underlying Proxy-Wasm plugins obtained from the injected factories.
*/
@Inject
@PostConstruct
public void init() throws StartException {
init(factories, serverAdaptor);
}

/**
* Cleans up resources used by the feature, such as stopping the underlying plugins.
* This method is automatically called by the CDI container before the bean is destroyed.
*/
@PreDestroy
public void destroy() {
super.destroy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public void init(Iterable<PluginFactory> factories, ServerAdaptor serverAdaptor)
}
}

/**
* Destroy all plugin pools. This method should be called when the application is shutting down
*/
public void destroy() {
for (var pool : pluginPools.values()) {
pool.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.roastedroot.proxywasm.internal.HttpCallResponseHandler;
import io.roastedroot.proxywasm.internal.HttpRequestAdaptor;
import io.roastedroot.proxywasm.internal.ProxyMap;
import io.roastedroot.proxywasm.internal.ServerAdaptor;
import jakarta.ws.rs.core.UriBuilder;
import java.net.URI;
import java.net.http.HttpClient;
Expand All @@ -15,7 +16,12 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ServerAdaptor implements io.roastedroot.proxywasm.internal.ServerAdaptor {
/**
* The BlockingServerAdaptor implements a ServerAdaptor suitable for blocking
* HTTP servers like servlet based http servers. It uses thread pools to handle
* performing tick events and http/grpc request for the Proxy-Wasm plugins.
*/
public class BlockingServerAdaptor implements ServerAdaptor {

ScheduledExecutorService tickExecutorService = Executors.newScheduledThreadPool(1);
ExecutorService executorService = Executors.newWorkStealingPool(5);
Expand Down
Loading