diff --git a/examples/java/echo/ftl.toml b/examples/java/echo/ftl.toml
new file mode 100644
index 0000000000..700b9d8833
--- /dev/null
+++ b/examples/java/echo/ftl.toml
@@ -0,0 +1,2 @@
+module = "echo"
+language = "kotlin"
diff --git a/examples/java/echo/pom.xml b/examples/java/echo/pom.xml
new file mode 100644
index 0000000000..7389eb00df
--- /dev/null
+++ b/examples/java/echo/pom.xml
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+ xyz.block.ftl.examples
+ echo
+ 1.0-SNAPSHOT
+
+
+ xyz.block.ftl
+ ftl-build-parent-java
+ 1.0-SNAPSHOT
+
+
+
diff --git a/examples/java/echo/src/main/java/ftl/echo/Echo.java b/examples/java/echo/src/main/java/ftl/echo/Echo.java
new file mode 100644
index 0000000000..1339b91066
--- /dev/null
+++ b/examples/java/echo/src/main/java/ftl/echo/Echo.java
@@ -0,0 +1,15 @@
+package ftl.echo;
+
+import ftl.time.TimeClient;
+import xyz.block.ftl.Export;
+import xyz.block.ftl.Verb;
+
+public class Echo {
+
+ @Export
+ @Verb
+ public EchoResponse echo(EchoRequest req, TimeClient time) {
+ var response = time.call();
+ return new EchoResponse("Hello, " + req.name().orElse("anonymous") + "! The time is " + response.toString() + ".");
+ }
+}
diff --git a/examples/java/echo/src/main/java/ftl/echo/EchoRequest.java b/examples/java/echo/src/main/java/ftl/echo/EchoRequest.java
new file mode 100644
index 0000000000..0aa0a4b531
--- /dev/null
+++ b/examples/java/echo/src/main/java/ftl/echo/EchoRequest.java
@@ -0,0 +1,6 @@
+package ftl.echo;
+
+import java.util.Optional;
+
+public record EchoRequest(Optional name) {
+}
diff --git a/examples/java/echo/src/main/java/ftl/echo/EchoResponse.java b/examples/java/echo/src/main/java/ftl/echo/EchoResponse.java
new file mode 100644
index 0000000000..37db3e18bb
--- /dev/null
+++ b/examples/java/echo/src/main/java/ftl/echo/EchoResponse.java
@@ -0,0 +1,4 @@
+package ftl.echo;
+
+public record EchoResponse(String message) {
+}
diff --git a/examples/java/time/ftl.toml b/examples/java/time/ftl.toml
new file mode 100644
index 0000000000..48033f28f8
--- /dev/null
+++ b/examples/java/time/ftl.toml
@@ -0,0 +1,2 @@
+module = "time"
+language = "kotlin"
diff --git a/examples/java/time/pom.xml b/examples/java/time/pom.xml
new file mode 100644
index 0000000000..94132b0569
--- /dev/null
+++ b/examples/java/time/pom.xml
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+ xyz.block.ftl.examples
+ time
+ 1.0-SNAPSHOT
+
+
+ xyz.block.ftl
+ ftl-build-parent-java
+ 1.0-SNAPSHOT
+
+
+
diff --git a/examples/java/time/src/main/java/ftl/time/Time.java b/examples/java/time/src/main/java/ftl/time/Time.java
new file mode 100644
index 0000000000..b3951c0f4f
--- /dev/null
+++ b/examples/java/time/src/main/java/ftl/time/Time.java
@@ -0,0 +1,15 @@
+package ftl.time;
+
+import java.time.OffsetDateTime;
+
+import xyz.block.ftl.Export;
+import xyz.block.ftl.Verb;
+
+public class Time {
+
+ @Verb
+ @Export
+ public static TimeResponse time() {
+ return new TimeResponse(OffsetDateTime.now());
+ }
+}
diff --git a/examples/java/time/src/main/java/ftl/time/TimeResponse.java b/examples/java/time/src/main/java/ftl/time/TimeResponse.java
new file mode 100644
index 0000000000..6209efb493
--- /dev/null
+++ b/examples/java/time/src/main/java/ftl/time/TimeResponse.java
@@ -0,0 +1,6 @@
+package ftl.time;
+
+import java.time.OffsetDateTime;
+
+public record TimeResponse(OffsetDateTime time) {
+}
\ No newline at end of file
diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java
index 3d2627d4e8..f54c893ff7 100644
--- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java
+++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java
@@ -85,6 +85,7 @@ public class ModuleBuilder {
private final Map verbClients;
private final FTLRecorder recorder;
private final Map> comments;
+ private final List validationFailures = new ArrayList<>();
public ModuleBuilder(IndexView index, String moduleName, Map knownTopics,
Map verbClients, FTLRecorder recorder,
@@ -301,6 +302,11 @@ public Type buildType(org.jboss.jandex.Type type, boolean export) {
case CLASS -> {
var clazz = type.asClassType();
var info = index.getClassByName(clazz.name());
+ if (info.enclosingClass() != null && !Modifier.isStatic(info.flags())) {
+ // proceed as normal, we fail at the end
+ validationFailures.add(new ValidationFailure(clazz.name().toString(),
+ "Inner classes must be static"));
+ }
PrimitiveType unboxed = PrimitiveType.unbox(clazz);
if (unboxed != null) {
@@ -445,6 +451,14 @@ public ModuleBuilder addDecls(Decl decl) {
}
public void writeTo(OutputStream out) throws IOException {
+ if (!validationFailures.isEmpty()) {
+ StringBuilder sb = new StringBuilder();
+ for (var failure : validationFailures) {
+ sb.append("Validation failure: ").append(failure.className).append(": ").append(failure.message)
+ .append("\n");
+ }
+ throw new RuntimeException(sb.toString());
+ }
moduleBuilder.build().writeTo(out);
}
@@ -466,4 +480,7 @@ public enum BodyType {
ALLOWED,
REQUIRED
}
+
+ record ValidationFailure(String className, String message) {
+ }
}