From 417a4e8a44ee1c32413d53b13c1c7fd8deb15d2d Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 00:11:59 -0500
Subject: [PATCH 01/17] Add a default validation exception handler
---
http-inject-plugin/pom.xml | 22 ++++++
.../io/avaje/http/inject/HelidonHandler.java | 34 ++++++++++
.../http/inject/HttpValidatorHandler.java | 67 +++++++++++++++++++
.../io/avaje/http/inject/JavalinHandler.java | 38 +++++++++++
.../java/io/avaje/http/inject/JexHandler.java | 40 +++++++++++
.../avaje/http/inject/ValidationResponse.java | 28 ++++++++
.../src/main/java/module-info.java | 9 ++-
7 files changed, 236 insertions(+), 2 deletions(-)
create mode 100644 http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
create mode 100644 http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
create mode 100644 http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
create mode 100644 http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
create mode 100644 http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
diff --git a/http-inject-plugin/pom.xml b/http-inject-plugin/pom.xml
index 93c2b6966..d77bf018d 100644
--- a/http-inject-plugin/pom.xml
+++ b/http-inject-plugin/pom.xml
@@ -38,6 +38,28 @@
provided
true
+
+ io.javalin
+ javalin
+ 6.7.0
+ provided
+ true
+
+
+ io.avaje
+ avaje-jex
+ 3.4-RC1
+ provided
+ true
+
+
+ io.helidon.webserver
+ helidon-webserver
+ 4.3.2
+ provided
+ true
+
+
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
new file mode 100644
index 000000000..394555a7f
--- /dev/null
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
@@ -0,0 +1,34 @@
+package io.avaje.http.inject;
+
+import static java.util.stream.Collectors.joining;
+
+import io.avaje.http.api.ValidationException;
+import io.helidon.webserver.http.HttpFeature;
+import io.helidon.webserver.http.HttpRouting.Builder;
+import io.helidon.webserver.http.ServerRequest;
+import io.helidon.webserver.http.ServerResponse;
+
+public class HelidonHandler implements HttpFeature {
+
+ @Override
+ public void setup(Builder routing) {
+
+ routing.error(ValidationException.class, this::handle);
+ }
+
+ private void handle(ServerRequest req, ServerResponse res, ValidationException exception) {
+ var violations = exception.getErrors();
+
+ int violationCount = violations.size();
+ String violationList =
+ violations.stream()
+ .map(violation -> "'" + violation.getField() + "' " + violation.getMessage())
+ .collect(joining("\n"));
+
+ res.status(exception.getStatus())
+ .send(
+ String.format(
+ "Bad Request [%s validation violations: \n%s]", violationCount, violationList)
+ .getBytes());
+ }
+}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
new file mode 100644
index 000000000..9afea2ec4
--- /dev/null
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
@@ -0,0 +1,67 @@
+package io.avaje.http.inject;
+
+import io.avaje.http.api.AvajeJavalinPlugin;
+import io.avaje.inject.BeanScopeBuilder;
+import io.avaje.inject.spi.InjectPlugin;
+import io.avaje.inject.spi.PluginProvides;
+import io.avaje.jex.Routing.HttpService;
+import io.avaje.spi.ServiceProvider;
+import io.helidon.webserver.http.HttpFeature;
+
+/** Plugin for avaje inject that provides a default Validator Handler */
+@ServiceProvider
+@PluginProvides(
+ providesStrings = {
+ "io.helidon.webserver.http.HttpFeature",
+ "io.avaje.http.api.AvajeJavalinPlugin",
+ "io.avaje.jex.Routing.HttpService",
+ })
+public final class HttpValidatorHandler implements InjectPlugin {
+
+ enum Server {
+ HELIDON("io.helidon.webserver.http.HttpFeature"),
+ JAVALIN("io.javalin.plugin.Plugin"),
+ JEX("io.avaje.jex.Routing.HttpService");
+
+ String register;
+
+ Server(String register) {
+ this.register = register;
+ }
+ }
+
+ private static final Server type = server();
+
+ private static Server server() {
+
+ for (var register : Server.values()) {
+
+ try {
+ Class.forName(register.register);
+ return register;
+ } catch (ClassNotFoundException e) {
+ continue;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void apply(BeanScopeBuilder builder) {
+ if (type == null) {
+ return;
+ }
+ switch (type) {
+ case HELIDON:
+ builder.bean(HttpFeature.class, new HelidonHandler());
+ break;
+ case JAVALIN:
+ builder.bean(AvajeJavalinPlugin.class, new JavalinHandler());
+ break;
+ case JEX:
+ builder.bean(HttpService.class, new JexHandler());
+ break;
+ }
+ }
+}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
new file mode 100644
index 000000000..b03ac718a
--- /dev/null
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
@@ -0,0 +1,38 @@
+package io.avaje.http.inject;
+
+import static java.util.stream.Collectors.joining;
+
+import java.util.List;
+
+import io.avaje.http.api.AvajeJavalinPlugin;
+import io.avaje.http.api.ValidationException;
+import io.javalin.config.JavalinConfig;
+import io.javalin.http.Context;
+
+public class JavalinHandler extends AvajeJavalinPlugin {
+ @Override
+ public void onStart(JavalinConfig config) {
+ config.router.mount(r -> r.exception(ValidationException.class, this::handler));
+ }
+
+ private void handler(ValidationException ex, Context ctx) {
+
+ var json = ctx.status(ex.getStatus()).jsonMapper();
+
+ List violations = ex.getErrors();
+ if (json == null) {
+ int violationCount = violations.size();
+ String violationList =
+ violations.stream()
+ .map(violation -> "'" + violation.getField() + "' " + violation.getMessage())
+ .collect(joining("\n"));
+
+ // return a plain-text error message
+ ctx.result(
+ String.format(
+ "Bad Request [%s validation violations: \n%s]", violationCount, violationList));
+ return;
+ }
+ ctx.json(new ValidationResponse(violations));
+ }
+}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
new file mode 100644
index 000000000..de685491a
--- /dev/null
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
@@ -0,0 +1,40 @@
+package io.avaje.http.inject;
+
+import static java.util.stream.Collectors.joining;
+
+import java.util.List;
+
+import io.avaje.http.api.ValidationException;
+import io.avaje.jex.Routing;
+import io.avaje.jex.Routing.HttpService;
+import io.avaje.jex.http.Context;
+
+public class JexHandler implements HttpService {
+
+ @Override
+ public void add(Routing arg0) {
+
+ arg0.error(ValidationException.class, this::handler);
+ }
+
+ private void handler(Context ctx, ValidationException ex) {
+
+ var json = ctx.status(ex.getStatus()).jsonService();
+
+ List violations = ex.getErrors();
+ if (json == null) {
+ int violationCount = violations.size();
+ String violationList =
+ violations.stream()
+ .map(violation -> "'" + violation.getField() + "' " + violation.getMessage())
+ .collect(joining("\n"));
+
+ // return a plain-text error message
+ ctx.text(
+ String.format(
+ "Bad Request [%s validation violations: \n%s]", violationCount, violationList));
+ return;
+ }
+ ctx.json(new ValidationResponse(violations));
+ }
+}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
new file mode 100644
index 000000000..435b5c38a
--- /dev/null
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
@@ -0,0 +1,28 @@
+package io.avaje.http.inject;
+
+import java.util.List;
+
+import io.avaje.http.api.ValidationException.Violation;
+
+public class ValidationResponse {
+
+ private static String type = "https://avaje.io/http/#bean-validation";
+ private static String title = "Failed Constraints";
+ private List errors;
+
+ public ValidationResponse(List errors) {
+ this.errors = errors;
+ }
+
+ public String type() {
+ return type;
+ }
+
+ public String title() {
+ return title;
+ }
+
+ public List errors() {
+ return errors;
+ }
+}
diff --git a/http-inject-plugin/src/main/java/module-info.java b/http-inject-plugin/src/main/java/module-info.java
index e13c5e735..501507f3e 100644
--- a/http-inject-plugin/src/main/java/module-info.java
+++ b/http-inject-plugin/src/main/java/module-info.java
@@ -1,8 +1,13 @@
+import io.avaje.http.inject.DefaultResolverProvider;
+import io.avaje.http.inject.HttpValidatorHandler;
+
module io.avaje.http.plugin {
requires io.avaje.http.api;
requires io.avaje.inject;
requires static io.avaje.spi;
-
- provides io.avaje.inject.spi.InjectExtension with io.avaje.http.inject.DefaultResolverProvider;
+ requires static io.avaje.jex;
+ requires static io.javalin;
+ requires static io.helidon.webserver;
+ provides io.avaje.inject.spi.InjectExtension with DefaultResolverProvider, HttpValidatorHandler;
}
From 6e65e69b56cb248345474c573a9212b841db34b1 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 00:17:36 -0500
Subject: [PATCH 02/17] Update pom.xml
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index df6722420..a7f5fa50c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,7 +44,6 @@
http-client
http-client-gson-adapter
http-client-moshi-adapter
- http-inject-plugin
http-generator-core
http-generator-javalin
http-generator-sigma
@@ -67,6 +66,7 @@
[21,)
+ http-inject-plugin
htmx-nima
htmx-nima-jstache
http-generator-helidon
From d86f6dd4558a8891547ac5de3b4b603b2a0f391b Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 00:19:23 -0500
Subject: [PATCH 03/17] Update pom.xml
---
http-inject-plugin/pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/http-inject-plugin/pom.xml b/http-inject-plugin/pom.xml
index d77bf018d..b25fca158 100644
--- a/http-inject-plugin/pom.xml
+++ b/http-inject-plugin/pom.xml
@@ -48,7 +48,7 @@
io.avaje
avaje-jex
- 3.4-RC1
+ 3.3
provided
true
From 1c86eeb94bcb7c9166cd10440b236cb94aa73eda Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 00:31:27 -0500
Subject: [PATCH 04/17] Update HttpValidatorHandler.java
---
.../java/io/avaje/http/inject/HttpValidatorHandler.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
index 9afea2ec4..748702a42 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
@@ -54,13 +54,13 @@ public void apply(BeanScopeBuilder builder) {
}
switch (type) {
case HELIDON:
- builder.bean(HttpFeature.class, new HelidonHandler());
+ builder.provideDefault(HttpFeature.class, HelidonHandler::new);
break;
case JAVALIN:
- builder.bean(AvajeJavalinPlugin.class, new JavalinHandler());
+ builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
break;
case JEX:
- builder.bean(HttpService.class, new JexHandler());
+ builder.provideDefault(HttpService.class, JexHandler::new);
break;
}
}
From bba90b3d4d2c1385cb41a4ec83eae2d8cae5079d Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 13:06:49 -0500
Subject: [PATCH 05/17] follow spec
---
.../io/avaje/http/inject/JavalinHandler.java | 6 ++++-
.../java/io/avaje/http/inject/JexHandler.java | 3 ++-
.../avaje/http/inject/ValidationResponse.java | 26 ++++++++++++++++---
3 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
index b03ac718a..ec47f4e5f 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
@@ -33,6 +33,10 @@ private void handler(ValidationException ex, Context ctx) {
"Bad Request [%s validation violations: \n%s]", violationCount, violationList));
return;
}
- ctx.json(new ValidationResponse(violations));
+ ctx.contentType("application/problem+json")
+ .result(
+ json.toJsonString(
+ new ValidationResponse(ex.getStatus(), violations, ctx.path()),
+ ValidationResponse.class));
}
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
index de685491a..d19fd3e7a 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
@@ -35,6 +35,7 @@ private void handler(Context ctx, ValidationException ex) {
"Bad Request [%s validation violations: \n%s]", violationCount, violationList));
return;
}
- ctx.json(new ValidationResponse(violations));
+ ctx.contentType("application/problem+json")
+ .write(json.toJsonString(new ValidationResponse(ex.getStatus(), violations, ctx.path())));
}
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
index 435b5c38a..a41b1c829 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
@@ -7,11 +7,17 @@
public class ValidationResponse {
private static String type = "https://avaje.io/http/#bean-validation";
- private static String title = "Failed Constraints";
- private List errors;
-
- public ValidationResponse(List errors) {
+ private static String title = "Request Failed Validation";
+ private static String detail =
+ "You tried to call this endpoint, but your data failed the vibe check";
+ private final int status;
+ private final List errors;
+ private final String instance;
+
+ public ValidationResponse(int status, List errors, String instance) {
+ this.status = status;
this.errors = errors;
+ this.instance = instance;
}
public String type() {
@@ -22,6 +28,18 @@ public String title() {
return title;
}
+ public String detail() {
+ return detail;
+ }
+
+ public String instance() {
+ return instance;
+ }
+
+ public int status() {
+ return status;
+ }
+
public List errors() {
return errors;
}
From d42502a04c4b808eee18c361454036d53e0e0662 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 13:07:08 -0500
Subject: [PATCH 06/17] Update ValidationResponse.java
---
.../src/main/java/io/avaje/http/inject/ValidationResponse.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
index a41b1c829..af2b7f5c9 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
@@ -9,7 +9,7 @@ public class ValidationResponse {
private static String type = "https://avaje.io/http/#bean-validation";
private static String title = "Request Failed Validation";
private static String detail =
- "You tried to call this endpoint, but your data failed the vibe check";
+ "You tried to call this endpoint, but your data failed validation";
private final int status;
private final List errors;
private final String instance;
From d55a7a6bbae41d303bdbdb7fbfb80a1bebf0742f Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 16:36:22 -0500
Subject: [PATCH 07/17] Update ValidationResponse.java
---
.../src/main/java/io/avaje/http/inject/ValidationResponse.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
index af2b7f5c9..a204b82cc 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
@@ -6,7 +6,7 @@
public class ValidationResponse {
- private static String type = "https://avaje.io/http/#bean-validation";
+ private static String type = "tag:io.avaje.http.api.Validator";
private static String title = "Request Failed Validation";
private static String detail =
"You tried to call this endpoint, but your data failed validation";
From a9d93ff7dce1a6fe31248f1475d11bb84c0299da Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 17:31:46 -0500
Subject: [PATCH 08/17] always json
---
.../io/avaje/http/inject/HelidonHandler.java | 18 +----
.../io/avaje/http/inject/JavalinHandler.java | 25 +------
.../java/io/avaje/http/inject/JexHandler.java | 22 +-----
.../avaje/http/inject/ValidationResponse.java | 75 ++++++++++++++++++-
4 files changed, 78 insertions(+), 62 deletions(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
index 394555a7f..e2a517e02 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
@@ -1,7 +1,5 @@
package io.avaje.http.inject;
-import static java.util.stream.Collectors.joining;
-
import io.avaje.http.api.ValidationException;
import io.helidon.webserver.http.HttpFeature;
import io.helidon.webserver.http.HttpRouting.Builder;
@@ -16,19 +14,9 @@ public void setup(Builder routing) {
routing.error(ValidationException.class, this::handle);
}
- private void handle(ServerRequest req, ServerResponse res, ValidationException exception) {
- var violations = exception.getErrors();
-
- int violationCount = violations.size();
- String violationList =
- violations.stream()
- .map(violation -> "'" + violation.getField() + "' " + violation.getMessage())
- .collect(joining("\n"));
+ private void handle(ServerRequest req, ServerResponse res, ValidationException ex) {
- res.status(exception.getStatus())
- .send(
- String.format(
- "Bad Request [%s validation violations: \n%s]", violationCount, violationList)
- .getBytes());
+ res.status(ex.getStatus()).header("Content-Type", "application/problem+json")
+ .send(new ValidationResponse(ex.getStatus(), ex.getErrors(), req.path().rawPath()).toJson());
}
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
index ec47f4e5f..1d599d286 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
@@ -1,9 +1,5 @@
package io.avaje.http.inject;
-import static java.util.stream.Collectors.joining;
-
-import java.util.List;
-
import io.avaje.http.api.AvajeJavalinPlugin;
import io.avaje.http.api.ValidationException;
import io.javalin.config.JavalinConfig;
@@ -17,26 +13,7 @@ public void onStart(JavalinConfig config) {
private void handler(ValidationException ex, Context ctx) {
- var json = ctx.status(ex.getStatus()).jsonMapper();
-
- List violations = ex.getErrors();
- if (json == null) {
- int violationCount = violations.size();
- String violationList =
- violations.stream()
- .map(violation -> "'" + violation.getField() + "' " + violation.getMessage())
- .collect(joining("\n"));
-
- // return a plain-text error message
- ctx.result(
- String.format(
- "Bad Request [%s validation violations: \n%s]", violationCount, violationList));
- return;
- }
ctx.contentType("application/problem+json")
- .result(
- json.toJsonString(
- new ValidationResponse(ex.getStatus(), violations, ctx.path()),
- ValidationResponse.class));
+ .result(new ValidationResponse(ex.getStatus(), ex.getErrors(), ctx.path()).toJson());
}
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
index d19fd3e7a..f5fe551a9 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
@@ -1,9 +1,5 @@
package io.avaje.http.inject;
-import static java.util.stream.Collectors.joining;
-
-import java.util.List;
-
import io.avaje.http.api.ValidationException;
import io.avaje.jex.Routing;
import io.avaje.jex.Routing.HttpService;
@@ -19,23 +15,7 @@ public void add(Routing arg0) {
private void handler(Context ctx, ValidationException ex) {
- var json = ctx.status(ex.getStatus()).jsonService();
-
- List violations = ex.getErrors();
- if (json == null) {
- int violationCount = violations.size();
- String violationList =
- violations.stream()
- .map(violation -> "'" + violation.getField() + "' " + violation.getMessage())
- .collect(joining("\n"));
-
- // return a plain-text error message
- ctx.text(
- String.format(
- "Bad Request [%s validation violations: \n%s]", violationCount, violationList));
- return;
- }
ctx.contentType("application/problem+json")
- .write(json.toJsonString(new ValidationResponse(ex.getStatus(), violations, ctx.path())));
+ .write(new ValidationResponse(ex.getStatus(), ex.getErrors(), ctx.path()).toJson());
}
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
index a204b82cc..2beb262b6 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
@@ -8,8 +8,7 @@ public class ValidationResponse {
private static String type = "tag:io.avaje.http.api.Validator";
private static String title = "Request Failed Validation";
- private static String detail =
- "You tried to call this endpoint, but your data failed validation";
+ private static String detail = "You tried to call this endpoint, but your data failed validation";
private final int status;
private final List errors;
private final String instance;
@@ -43,4 +42,76 @@ public int status() {
public List errors() {
return errors;
}
+
+ // Custom serialize so we don't need any json lib
+ public String toJson() {
+ StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ sb.append("\"type\":").append(escapeJson(type)).append(",");
+ sb.append("\"title\":").append(escapeJson(title)).append(",");
+ sb.append("\"detail\":").append(escapeJson(detail)).append(",");
+ sb.append("\"instance\":").append(escapeJson(instance)).append(",");
+ sb.append("\"status\":").append(status).append(',');
+
+ sb.append("\"errors\":[");
+ for (int i = 0; i < errors().size(); i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ var e = errors.get(i);
+ sb.append('{');
+ sb.append("\"path\":").append(escapeJson(e.getPath())).append(",");
+ sb.append("\"field\":").append(escapeJson(e.getField())).append(",");
+ sb.append("\"message\":").append(escapeJson(e.getMessage()));
+ sb.append('}');
+ }
+
+ sb.append(']');
+
+ sb.append('}');
+ return sb.toString();
+ }
+
+ private static String escapeJson(String s) {
+ if (s == null) {
+ return "null";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append('"');
+ for (int i = 0; i < s.length(); i++) {
+ char ch = s.charAt(i);
+ switch (ch) {
+ case '"':
+ sb.append("\\\"");
+ break;
+ case '\\':
+ sb.append("\\\\");
+ break;
+ case '\b':
+ sb.append("\\b");
+ break;
+ case '\f':
+ sb.append("\\f");
+ break;
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ default:
+ // Handle control characters or just append
+ if (ch < ' ' || ch > '~') {
+ sb.append(String.format("\\u%04x", (int) ch));
+ } else {
+ sb.append(ch);
+ }
+ }
+ }
+ sb.append('"');
+ return sb.toString();
+ }
}
From c4659e7e50eb7cc878466ba6a18858b520d3780b Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Thu, 20 Nov 2025 22:23:22 -0500
Subject: [PATCH 09/17] streaming
---
.../io/avaje/http/inject/HelidonHandler.java | 14 ++-
.../io/avaje/http/inject/JavalinHandler.java | 12 +-
.../java/io/avaje/http/inject/JexHandler.java | 12 +-
.../avaje/http/inject/ValidationResponse.java | 103 +++++++++++-------
4 files changed, 94 insertions(+), 47 deletions(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
index e2a517e02..5e7c3c92b 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
@@ -1,5 +1,8 @@
package io.avaje.http.inject;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+
import io.avaje.http.api.ValidationException;
import io.helidon.webserver.http.HttpFeature;
import io.helidon.webserver.http.HttpRouting.Builder;
@@ -15,8 +18,13 @@ public void setup(Builder routing) {
}
private void handle(ServerRequest req, ServerResponse res, ValidationException ex) {
-
- res.status(ex.getStatus()).header("Content-Type", "application/problem+json")
- .send(new ValidationResponse(ex.getStatus(), ex.getErrors(), req.path().rawPath()).toJson());
+ try (var os =
+ res.status(ex.getStatus())
+ .header("Content-Type", "application/problem+json")
+ .outputStream()) {
+ new ValidationResponse(ex.getStatus(), ex.getErrors(), req.path().rawPath()).toJson(os);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
index 1d599d286..8120e52fc 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
@@ -1,5 +1,8 @@
package io.avaje.http.inject;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+
import io.avaje.http.api.AvajeJavalinPlugin;
import io.avaje.http.api.ValidationException;
import io.javalin.config.JavalinConfig;
@@ -12,8 +15,11 @@ public void onStart(JavalinConfig config) {
}
private void handler(ValidationException ex, Context ctx) {
-
- ctx.contentType("application/problem+json")
- .result(new ValidationResponse(ex.getStatus(), ex.getErrors(), ctx.path()).toJson());
+ try (var os =
+ ctx.contentType("application/problem+json").status(ex.getStatus()).outputStream()) {
+ new ValidationResponse(ex.getStatus(), ex.getErrors(), ctx.path()).toJson(os);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
index f5fe551a9..3284444ef 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
@@ -1,5 +1,8 @@
package io.avaje.http.inject;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+
import io.avaje.http.api.ValidationException;
import io.avaje.jex.Routing;
import io.avaje.jex.Routing.HttpService;
@@ -9,13 +12,16 @@ public class JexHandler implements HttpService {
@Override
public void add(Routing arg0) {
-
arg0.error(ValidationException.class, this::handler);
}
private void handler(Context ctx, ValidationException ex) {
- ctx.contentType("application/problem+json")
- .write(new ValidationResponse(ex.getStatus(), ex.getErrors(), ctx.path()).toJson());
+ try (var os =
+ ctx.contentType("application/problem+json").status(ex.getStatus()).outputStream()) {
+ new ValidationResponse(ex.getStatus(), ex.getErrors(), ctx.path()).toJson(os);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
index 2beb262b6..818f68aaa 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
@@ -1,5 +1,9 @@
package io.avaje.http.inject;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
import java.util.List;
import io.avaje.http.api.ValidationException.Violation;
@@ -43,75 +47,98 @@ public List errors() {
return errors;
}
- // Custom serialize so we don't need any json lib
- public String toJson() {
- StringBuilder sb = new StringBuilder();
- sb.append('{');
- sb.append("\"type\":").append(escapeJson(type)).append(",");
- sb.append("\"title\":").append(escapeJson(title)).append(",");
- sb.append("\"detail\":").append(escapeJson(detail)).append(",");
- sb.append("\"instance\":").append(escapeJson(instance)).append(",");
- sb.append("\"status\":").append(status).append(',');
-
- sb.append("\"errors\":[");
- for (int i = 0; i < errors().size(); i++) {
+ // custom serialize as this is a simple class
+ public void toJson(OutputStream os) throws IOException {
+ try (Writer writer = new OutputStreamWriter(os, "UTF-8")) {
+ writeJsonInternal(writer);
+ }
+ }
+
+ private void writeJsonInternal(Writer writer) throws IOException {
+ writer.write('{');
+ writeKeyValue("type", type, writer);
+ writer.write(',');
+ writeKeyValue("title", title, writer);
+ writer.write(',');
+ writeKeyValue("detail", detail, writer);
+ writer.write(',');
+ writeKeyValue("instance", instance, writer);
+ writer.write(',');
+ // status is a number, so no quotes or escaping needed
+ writer.write("\"status\":");
+ writer.write(String.valueOf(status));
+ writer.write(",\"errors\":[");
+ for (int i = 0; i < errors.size(); i++) {
if (i > 0) {
- sb.append(',');
+ writer.write(',');
}
var e = errors.get(i);
- sb.append('{');
- sb.append("\"path\":").append(escapeJson(e.getPath())).append(",");
- sb.append("\"field\":").append(escapeJson(e.getField())).append(",");
- sb.append("\"message\":").append(escapeJson(e.getMessage()));
- sb.append('}');
+ writer.write('{');
+ writeKeyValue("path", e.getPath(), writer);
+ writer.write(',');
+ writeKeyValue("field", e.getField(), writer);
+ writer.write(',');
+ writeKeyValue("message", e.getMessage(), writer);
+ writer.write('}');
}
- sb.append(']');
+ writer.write(']');
+ writer.write('}');
+ writer.flush();
+ }
- sb.append('}');
- return sb.toString();
+ /** Writes a JSON key-value pair where the value is a string, handling quotes and escaping. */
+ private void writeKeyValue(String key, String value, Writer writer) throws IOException {
+ writer.write('"');
+ writer.write(key);
+ writer.write("\":");
+ writeEscapedJsonString(value, writer);
}
- private static String escapeJson(String s) {
+ /**
+ * Writes the given string to the writer, JSON-escaping it and wrapping it in quotes. Writes
+ * 'null' (the JSON literal) if the input string is null.
+ */
+ private static void writeEscapedJsonString(String s, Writer writer) throws IOException {
if (s == null) {
- return "null";
+ writer.write("null");
+ return;
}
- StringBuilder sb = new StringBuilder();
- sb.append('"');
+
+ writer.write('"');
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
switch (ch) {
case '"':
- sb.append("\\\"");
+ writer.write("\\\"");
break;
case '\\':
- sb.append("\\\\");
+ writer.write("\\\\");
break;
case '\b':
- sb.append("\\b");
+ writer.write("\\b");
break;
case '\f':
- sb.append("\\f");
+ writer.write("\\f");
break;
case '\n':
- sb.append("\\n");
+ writer.write("\\n");
break;
case '\r':
- sb.append("\\r");
+ writer.write("\\r");
break;
case '\t':
- sb.append("\\t");
+ writer.write("\\t");
break;
default:
- // Handle control characters or just append
- if (ch < ' ' || ch > '~') {
- sb.append(String.format("\\u%04x", (int) ch));
+ // Check for control characters that must be escaped
+ if (ch < ' ' || ch >= 0x7F && ch <= 0x9F) {
+ writer.write(String.format("\\u%04x", (int) ch));
} else {
- sb.append(ch);
+ writer.write(ch);
}
}
}
- sb.append('"');
- return sb.toString();
+ writer.write('"');
}
}
From c02568e19983ebb21d65307d91f0539bee97510a Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Fri, 21 Nov 2025 00:13:21 -0500
Subject: [PATCH 10/17] fix helidon plugin overridding all
---
.../src/main/java/io/avaje/http/inject/HelidonHandler.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
index 5e7c3c92b..9cc241ab1 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
@@ -4,11 +4,13 @@
import java.io.UncheckedIOException;
import io.avaje.http.api.ValidationException;
+import io.helidon.common.Weight;
import io.helidon.webserver.http.HttpFeature;
import io.helidon.webserver.http.HttpRouting.Builder;
import io.helidon.webserver.http.ServerRequest;
import io.helidon.webserver.http.ServerResponse;
+@Weight(-67) // execute first so that it can be overridden by a custom error handler.
public class HelidonHandler implements HttpFeature {
@Override
From 2570ab8f4db002a6d61715fae229b8c56cd1ebc2 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Fri, 21 Nov 2025 00:18:37 -0500
Subject: [PATCH 11/17] final
---
.../src/main/java/io/avaje/http/inject/HelidonHandler.java | 2 +-
.../java/io/avaje/http/inject/HttpValidatorHandler.java | 7 +------
.../src/main/java/io/avaje/http/inject/JavalinHandler.java | 2 +-
.../src/main/java/io/avaje/http/inject/JexHandler.java | 2 +-
4 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
index 9cc241ab1..c18f97b8f 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HelidonHandler.java
@@ -11,7 +11,7 @@
import io.helidon.webserver.http.ServerResponse;
@Weight(-67) // execute first so that it can be overridden by a custom error handler.
-public class HelidonHandler implements HttpFeature {
+final class HelidonHandler implements HttpFeature {
@Override
public void setup(Builder routing) {
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
index 748702a42..e4d32c684 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
@@ -22,9 +22,7 @@ enum Server {
HELIDON("io.helidon.webserver.http.HttpFeature"),
JAVALIN("io.javalin.plugin.Plugin"),
JEX("io.avaje.jex.Routing.HttpService");
-
String register;
-
Server(String register) {
this.register = register;
}
@@ -33,17 +31,14 @@ enum Server {
private static final Server type = server();
private static Server server() {
-
for (var register : Server.values()) {
-
try {
Class.forName(register.register);
return register;
} catch (ClassNotFoundException e) {
- continue;
+ // nothing
}
}
-
return null;
}
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
index 8120e52fc..84dc6ca8b 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JavalinHandler.java
@@ -8,7 +8,7 @@
import io.javalin.config.JavalinConfig;
import io.javalin.http.Context;
-public class JavalinHandler extends AvajeJavalinPlugin {
+final class JavalinHandler extends AvajeJavalinPlugin {
@Override
public void onStart(JavalinConfig config) {
config.router.mount(r -> r.exception(ValidationException.class, this::handler));
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
index 3284444ef..a89678fad 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/JexHandler.java
@@ -8,7 +8,7 @@
import io.avaje.jex.Routing.HttpService;
import io.avaje.jex.http.Context;
-public class JexHandler implements HttpService {
+final class JexHandler implements HttpService {
@Override
public void add(Routing arg0) {
From 2c16b20b775b53908a7e5086991e24255708661c Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Fri, 21 Nov 2025 00:25:10 -0500
Subject: [PATCH 12/17] Update ValidationResponse.java
---
.../src/main/java/io/avaje/http/inject/ValidationResponse.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
index 818f68aaa..fe9a60e3e 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
@@ -10,7 +10,7 @@
public class ValidationResponse {
- private static String type = "tag:io.avaje.http.api.Validator";
+ private static String type = "tag:io.avaje.http.api.ValidationException";
private static String title = "Request Failed Validation";
private static String detail = "You tried to call this endpoint, but your data failed validation";
private final int status;
From 0aa5af3b80089b1671b6cf73c4cd98e148ff6056 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Fri, 21 Nov 2025 08:01:07 -0500
Subject: [PATCH 13/17] Remove unused getter methods in ValidationResponse
Removed unused getter methods from ValidationResponse.
---
.../avaje/http/inject/ValidationResponse.java | 26 +------------------
1 file changed, 1 insertion(+), 25 deletions(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
index fe9a60e3e..c0f4b50ea 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/ValidationResponse.java
@@ -22,31 +22,7 @@ public ValidationResponse(int status, List errors, String instance) {
this.errors = errors;
this.instance = instance;
}
-
- public String type() {
- return type;
- }
-
- public String title() {
- return title;
- }
-
- public String detail() {
- return detail;
- }
-
- public String instance() {
- return instance;
- }
-
- public int status() {
- return status;
- }
-
- public List errors() {
- return errors;
- }
-
+
// custom serialize as this is a simple class
public void toJson(OutputStream os) throws IOException {
try (Writer writer = new OutputStreamWriter(os, "UTF-8")) {
From 4e36dc51b046ca66f15a92a88fc50f57a066b350 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:18:05 -0500
Subject: [PATCH 14/17] pivot to NCDF
---
http-inject-plugin/pom.xml | 2 +-
.../http/inject/HttpValidatorHandler.java | 48 ++++---------------
.../src/main/java/module-info.java | 1 -
3 files changed, 11 insertions(+), 40 deletions(-)
diff --git a/http-inject-plugin/pom.xml b/http-inject-plugin/pom.xml
index b25fca158..329d0281e 100644
--- a/http-inject-plugin/pom.xml
+++ b/http-inject-plugin/pom.xml
@@ -19,7 +19,7 @@
io.avaje
- avaje-inject
+ avaje-inject-generator
12.0
provided
true
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
index e4d32c684..1d068af7a 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
@@ -5,11 +5,9 @@
import io.avaje.inject.spi.InjectPlugin;
import io.avaje.inject.spi.PluginProvides;
import io.avaje.jex.Routing.HttpService;
-import io.avaje.spi.ServiceProvider;
import io.helidon.webserver.http.HttpFeature;
/** Plugin for avaje inject that provides a default Validator Handler */
-@ServiceProvider
@PluginProvides(
providesStrings = {
"io.helidon.webserver.http.HttpFeature",
@@ -18,45 +16,19 @@
})
public final class HttpValidatorHandler implements InjectPlugin {
- enum Server {
- HELIDON("io.helidon.webserver.http.HttpFeature"),
- JAVALIN("io.javalin.plugin.Plugin"),
- JEX("io.avaje.jex.Routing.HttpService");
- String register;
- Server(String register) {
- this.register = register;
- }
- }
-
- private static final Server type = server();
-
- private static Server server() {
- for (var register : Server.values()) {
- try {
- Class.forName(register.register);
- return register;
- } catch (ClassNotFoundException e) {
- // nothing
- }
- }
- return null;
- }
-
@Override
public void apply(BeanScopeBuilder builder) {
- if (type == null) {
- return;
+ try {
+ builder.provideDefault(HttpFeature.class, HelidonHandler::new);
+ } catch (NoClassDefFoundError e) {
+ }
+ try {
+ builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
+ } catch (NoClassDefFoundError e) {
}
- switch (type) {
- case HELIDON:
- builder.provideDefault(HttpFeature.class, HelidonHandler::new);
- break;
- case JAVALIN:
- builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
- break;
- case JEX:
- builder.provideDefault(HttpService.class, JexHandler::new);
- break;
+ try {
+ builder.provideDefault(HttpService.class, JexHandler::new);
+ } catch (NoClassDefFoundError e) {
}
}
}
diff --git a/http-inject-plugin/src/main/java/module-info.java b/http-inject-plugin/src/main/java/module-info.java
index 501507f3e..a3fcd3080 100644
--- a/http-inject-plugin/src/main/java/module-info.java
+++ b/http-inject-plugin/src/main/java/module-info.java
@@ -5,7 +5,6 @@
requires io.avaje.http.api;
requires io.avaje.inject;
- requires static io.avaje.spi;
requires static io.avaje.jex;
requires static io.javalin;
requires static io.helidon.webserver;
From f1829403e59fc88209cddec0b1f0323df669ada3 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Sat, 22 Nov 2025 18:19:45 -0500
Subject: [PATCH 15/17] Update DefaultResolverProvider.java
---
.../java/io/avaje/http/inject/DefaultResolverProvider.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/DefaultResolverProvider.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/DefaultResolverProvider.java
index b22eb7714..e44ab9d84 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/DefaultResolverProvider.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/DefaultResolverProvider.java
@@ -4,10 +4,10 @@
import io.avaje.http.api.context.ThreadLocalRequestContextResolver;
import io.avaje.inject.BeanScopeBuilder;
import io.avaje.inject.spi.InjectPlugin;
-import io.avaje.spi.ServiceProvider;
+import io.avaje.inject.spi.PluginProvides;
/** Plugin for avaje inject that provides a default RequestContextResolver instance. */
-@ServiceProvider
+@PluginProvides
public final class DefaultResolverProvider implements InjectPlugin {
@Override
From eada8ba11901c87e476fe136023869320557f299 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Sat, 22 Nov 2025 19:43:00 -0500
Subject: [PATCH 16/17] module-check
---
...ler.java => HttpValidatorErrorPlugin.java} | 27 ++++++++++++++++---
.../src/main/java/module-info.java | 4 +--
2 files changed, 25 insertions(+), 6 deletions(-)
rename http-inject-plugin/src/main/java/io/avaje/http/inject/{HttpValidatorHandler.java => HttpValidatorErrorPlugin.java} (59%)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorErrorPlugin.java
similarity index 59%
rename from http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
rename to http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorErrorPlugin.java
index 1d068af7a..9b7b4ea31 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorHandler.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorErrorPlugin.java
@@ -14,21 +14,40 @@
"io.avaje.http.api.AvajeJavalinPlugin",
"io.avaje.jex.Routing.HttpService",
})
-public final class HttpValidatorHandler implements InjectPlugin {
+public final class HttpValidatorErrorPlugin implements InjectPlugin {
@Override
public void apply(BeanScopeBuilder builder) {
- try {
+
+ ModuleLayer bootLayer = ModuleLayer.boot();
+
+ if (bootLayer.findModule("io.avaje.jex").isPresent()) {
+ builder.provideDefault(HttpService.class, JexHandler::new);
+ return;
+ } else if (bootLayer.findModule("io.helidon.webserver").isPresent()) {
builder.provideDefault(HttpFeature.class, HelidonHandler::new);
+ return;
+ } else if (bootLayer.findModule("io.javalin").isPresent()) {
+ builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
+ return;
+ }
+
+ try {
+ builder.provideDefault(HttpService.class, JexHandler::new);
+ return;
} catch (NoClassDefFoundError e) {
+ // not present
}
try {
- builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
+ builder.provideDefault(HttpFeature.class, HelidonHandler::new);
+ return;
} catch (NoClassDefFoundError e) {
+ // not present
}
try {
- builder.provideDefault(HttpService.class, JexHandler::new);
+ builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
} catch (NoClassDefFoundError e) {
+ // not present
}
}
}
diff --git a/http-inject-plugin/src/main/java/module-info.java b/http-inject-plugin/src/main/java/module-info.java
index a3fcd3080..8c6058dfd 100644
--- a/http-inject-plugin/src/main/java/module-info.java
+++ b/http-inject-plugin/src/main/java/module-info.java
@@ -1,5 +1,5 @@
import io.avaje.http.inject.DefaultResolverProvider;
-import io.avaje.http.inject.HttpValidatorHandler;
+import io.avaje.http.inject.HttpValidatorErrorPlugin;
module io.avaje.http.plugin {
@@ -8,5 +8,5 @@
requires static io.avaje.jex;
requires static io.javalin;
requires static io.helidon.webserver;
- provides io.avaje.inject.spi.InjectExtension with DefaultResolverProvider, HttpValidatorHandler;
+ provides io.avaje.inject.spi.InjectExtension with DefaultResolverProvider, HttpValidatorErrorPlugin;
}
From f31599f2d12fae0316ef5efc7634ffc3d69df3b8 Mon Sep 17 00:00:00 2001
From: Josiah Noel <32279667+SentryMan@users.noreply.github.com>
Date: Sat, 22 Nov 2025 20:17:42 -0500
Subject: [PATCH 17/17] optimize even further beyond
---
.../http/inject/HttpValidatorErrorPlugin.java | 59 ++++++++++---------
1 file changed, 31 insertions(+), 28 deletions(-)
diff --git a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorErrorPlugin.java b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorErrorPlugin.java
index 9b7b4ea31..627daad64 100644
--- a/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorErrorPlugin.java
+++ b/http-inject-plugin/src/main/java/io/avaje/http/inject/HttpValidatorErrorPlugin.java
@@ -21,33 +21,36 @@ public void apply(BeanScopeBuilder builder) {
ModuleLayer bootLayer = ModuleLayer.boot();
- if (bootLayer.findModule("io.avaje.jex").isPresent()) {
- builder.provideDefault(HttpService.class, JexHandler::new);
- return;
- } else if (bootLayer.findModule("io.helidon.webserver").isPresent()) {
- builder.provideDefault(HttpFeature.class, HelidonHandler::new);
- return;
- } else if (bootLayer.findModule("io.javalin").isPresent()) {
- builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
- return;
- }
-
- try {
- builder.provideDefault(HttpService.class, JexHandler::new);
- return;
- } catch (NoClassDefFoundError e) {
- // not present
- }
- try {
- builder.provideDefault(HttpFeature.class, HelidonHandler::new);
- return;
- } catch (NoClassDefFoundError e) {
- // not present
- }
- try {
- builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
- } catch (NoClassDefFoundError e) {
- // not present
- }
+ bootLayer
+ .findModule("io.avaje.http.plugin")
+ .ifPresentOrElse(
+ m -> {
+ if (bootLayer.findModule("io.avaje.jex").isPresent()) {
+ builder.provideDefault(HttpService.class, JexHandler::new);
+ } else if (bootLayer.findModule("io.helidon.webserver").isPresent()) {
+ builder.provideDefault(HttpFeature.class, HelidonHandler::new);
+ } else if (bootLayer.findModule("io.javalin").isPresent()) {
+ builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
+ }
+ },
+ () -> {
+ try {
+ builder.provideDefault(HttpService.class, JexHandler::new);
+ return;
+ } catch (NoClassDefFoundError e) {
+ // not present
+ }
+ try {
+ builder.provideDefault(HttpFeature.class, HelidonHandler::new);
+ return;
+ } catch (NoClassDefFoundError e) {
+ // not present
+ }
+ try {
+ builder.provideDefault(AvajeJavalinPlugin.class, JavalinHandler::new);
+ } catch (NoClassDefFoundError e) {
+ // not present
+ }
+ });
}
}