diff --git a/proxy-wasm-jaxrs-jersey/src/test/java/io/roastedroot/proxywasm/jaxrs/example/tests/BaseTest.java b/proxy-wasm-jaxrs-jersey/src/test/java/io/roastedroot/proxywasm/jaxrs/example/tests/BaseTest.java index d5ea457..2c6f3a7 100644 --- a/proxy-wasm-jaxrs-jersey/src/test/java/io/roastedroot/proxywasm/jaxrs/example/tests/BaseTest.java +++ b/proxy-wasm-jaxrs-jersey/src/test/java/io/roastedroot/proxywasm/jaxrs/example/tests/BaseTest.java @@ -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; @@ -33,7 +33,7 @@ public void setUp() throws Exception { // Create mock Instance for WasmPluginFeature resourceConfig.register( new WasmPluginFeature( - new ServerAdaptor(), + new BlockingServerAdaptor(), App.headerTests(), App.headerTestsNotShared(), App.tickTests(), diff --git a/proxy-wasm-jaxrs/pom.xml b/proxy-wasm-jaxrs/pom.xml index 286bab9..0847265 100644 --- a/proxy-wasm-jaxrs/pom.xml +++ b/proxy-wasm-jaxrs/pom.xml @@ -71,6 +71,23 @@ + + + org.apache.maven.plugins + maven-javadoc-plugin + ${javadoc-plugin.version} + + + **/internal/** + + + + https://www.javadoc.io/doc/io.roastedroot/proxy-wasm-java-host/${project.version}/ + ${project.basedir}/../proxy-wasm-java-host/target/reports/apidocs + + + + diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java index 26ebb48..7b4bb2a 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPlugin.java @@ -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. + * + *

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(); } diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFeature.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFeature.java index 7ac60ea..ed005d8 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFeature.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFeature.java @@ -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}. + * + *

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. + * + *

If you are using a CDI container like quarkus, you will be using the + * {@link io.roastedroot.proxywasm.jaxrs.cdi.WasmPluginFeature} instead. + * + *

{@code
+ * public class MyApplication extends jakarta.ws.rs.core.Application {
+ *     @Override
+ *     public Set> getClasses() {
+ *         Set> resources = new HashSet<>();
+ *         resources.add(MyResource.class);
+ *         return resources;
+ *     }
+ *
+ *     @Override
+ *     public Set getSingletons() {
+ *         Set 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;
+ *     }
+ * }
+ * }
+ *
+ * @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 factories)
+            throws StartException {
+        init(factories, httpServer);
     }
 }
diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFilter.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFilter.java
index f97b57c..f469840 100644
--- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFilter.java
+++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/WasmPluginFilter.java
@@ -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.
+ *
+ * 

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 { @@ -32,10 +42,27 @@ public class WasmPluginFilter private final List pluginPools; - public WasmPluginFilter(List 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 pluginPools) { + this.pluginPools = List.copyOf(pluginPools); } + /** + * Intercepts incoming JAX-RS requests before they reach the resource method. + * + *

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) { @@ -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. + * + *

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) @@ -193,6 +233,20 @@ private void filter( } } + /** + * Intercepts the response body writing process. + * + *

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 { @@ -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) { diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/cdi/ServerAdaptor.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/cdi/ServerAdaptor.java index 8cca862..2dccb36 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/cdi/ServerAdaptor.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/cdi/ServerAdaptor.java @@ -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). + * + *

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. + * + *

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(); + } +} diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/cdi/WasmPluginFeature.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/cdi/WasmPluginFeature.java index 0853dc2..2e6988d 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/cdi/WasmPluginFeature.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/cdi/WasmPluginFeature.java @@ -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. + * + *

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. + * + *

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. + * + *

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 @@ -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(); diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/AbstractWasmPluginFeature.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/AbstractWasmPluginFeature.java index 8f21e92..771e1c8 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/AbstractWasmPluginFeature.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/AbstractWasmPluginFeature.java @@ -45,6 +45,9 @@ public void init(Iterable 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(); diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/ServerAdaptor.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/BlockingServerAdaptor.java similarity index 92% rename from proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/ServerAdaptor.java rename to proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/BlockingServerAdaptor.java index 0c90f55..900dfe7 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/ServerAdaptor.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/internal/BlockingServerAdaptor.java @@ -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; @@ -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); diff --git a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletJaxrsHttpRequestAdaptor.java b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletJaxrsHttpRequestAdaptor.java index 413d7a8..b9c4202 100644 --- a/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletJaxrsHttpRequestAdaptor.java +++ b/proxy-wasm-jaxrs/src/main/java/io/roastedroot/proxywasm/jaxrs/servlet/ServletJaxrsHttpRequestAdaptor.java @@ -3,14 +3,31 @@ import io.roastedroot.proxywasm.jaxrs.internal.JaxrsHttpRequestAdaptor; import jakarta.servlet.http.HttpServletRequest; +/** + * A {@link JaxrsHttpRequestAdaptor} specifically designed to work with the Jakarta Servlet API based + * JAX-RS implementations. + * + *

This adaptor implementation extracts request details (like remote/local addresses and ports, + * protocol) directly from the underlying {@link HttpServletRequest} object. + */ public class ServletJaxrsHttpRequestAdaptor extends JaxrsHttpRequestAdaptor { private final HttpServletRequest request; + /** + * Constructs a new adaptor. + * + * @param request The {@link HttpServletRequest} associated with the current request. + */ public ServletJaxrsHttpRequestAdaptor(HttpServletRequest request) { this.request = request; } + /** + * Retrieves the remote IP address from the underlying {@link HttpServletRequest}. + * + * @return The remote IP address, or an empty string if the request object is null. + */ @Override public String remoteAddress() { if (request == null) { @@ -19,6 +36,11 @@ public String remoteAddress() { return request.getRemoteAddr(); } + /** + * Retrieves the remote port from the underlying {@link HttpServletRequest}. + * + * @return The remote port number, or 0 if the request object is null. + */ @Override public int remotePort() { if (request == null) { @@ -27,6 +49,11 @@ public int remotePort() { return request.getRemotePort(); } + /** + * Retrieves the local IP address from the underlying {@link HttpServletRequest}. + * + * @return The local IP address, or an empty string if the request object is null. + */ @Override public String localAddress() { if (request == null) { @@ -35,6 +62,11 @@ public String localAddress() { return request.getLocalAddr(); } + /** + * Retrieves the local port from the underlying {@link HttpServletRequest}. + * + * @return The local port number, or 0 if the request object is null. + */ @Override public int localPort() { if (request == null) { @@ -43,6 +75,11 @@ public int localPort() { return request.getLocalPort(); } + /** + * Retrieves the protocol string (e.g., "HTTP/1.1") from the underlying {@link HttpServletRequest}. + * + * @return The protocol string. + */ @Override public String protocol() { return request.getProtocol(); diff --git a/quarkus-proxy-wasm/deployment/pom.xml b/quarkus-proxy-wasm/deployment/pom.xml index 7e58a8c..d87fc12 100644 --- a/quarkus-proxy-wasm/deployment/pom.xml +++ b/quarkus-proxy-wasm/deployment/pom.xml @@ -64,6 +64,15 @@ + + + org.apache.maven.plugins + maven-javadoc-plugin + ${javadoc-plugin.version} + + true + +