Summary
When a JAX-RS resource class in src/main/java uses Java 14+ syntax (switch expressions, multi-label case, record, pattern matching, text blocks), the unknow-server-maven plugin's JavaParser fails to parse the file, the file is silently skipped, and the build still succeeds. The resulting JAR boots fine but every request to that resource returns 404 because no Jaxrs_<path>.java was generated.
The only signal is a single [WARNING] Failed to parse <File>.java: … line buried in the generate-sources log — easy to miss in CI output. Maven's exit code is 0.
Versions
io.github.unknow0.server:unknow-server-maven 0.9.9
maven-compiler-plugin 3.13.0 with maven.compiler.source/target = 17
- JDK 25 (Maven was invoked via JDK 25)
- Reproduced on Linux 6.17, Maven 3.8.7
Minimal repro
Single-module Maven project. JAX-RS impl with Java 14+ syntax:
package com.example.bridge;
import java.util.Map;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.*;
@Path("/mcp")
@Produces(MediaType.APPLICATION_JSON)
public class BridgeServiceImpl {
@POST
@Path("/execute/{name}")
@Consumes(MediaType.APPLICATION_JSON)
public Response execute(@PathParam("name") String name, Map<String, Object> args) {
// Switch expression — Java 14+
return switch (name) {
case "a", "b" -> Response.ok().build();
default -> Response.status(Response.Status.NOT_FOUND).build();
};
}
@GET
@Path("/tools")
public String listTools() {
return "{\"tools\":[]}";
}
}
mvn clean package -DskipTests output:
[INFO] --- unknow-server-maven:0.9.9:jaxrs-generator (jaxrs) @ example ---
[WARNING] Failed to parse BridgeServiceImpl.java: [(line 18,col 16) Switch expressions are not supported.
Pay attention that this feature is supported starting from 'JAVA_12' language level.]
[INFO] --- unknow-server-maven:0.9.9:servlet-generator (server) @ example ---
servlets: [Resource:/openapi.json] ← no Jaxrs_mcp_* — silent skip
[INFO] BUILD SUCCESS
At runtime: curl http://localhost:8080/mcp/tools → 404. target/jaxrs-generator/resources/openapi.json is empty ("paths":{}).
Root cause
AbstractGeneratorMojo constructs JavaParser without setting a language level:
// AbstractGeneratorMojo.java:131
parser = new JavaParser(new ParserConfiguration().setStoreTokens(true).setSymbolResolver(javaSymbolSolver));
JavaParser's default ParserConfiguration language level is POPULAR (currently mapped to JAVA_11), so anything from Java 12 onward fails. The SrcWalker (called from process(TypeConsumer)) swallows the parse error and continues with the remaining types.
Suggested fixes
Any one of these would have caught the issue in my project:
- Set the language level on the
ParserConfiguration — e.g. setLanguageLevel(LanguageLevel.JAVA_21) (or the highest released). Modern projects on JDK 21+ would be supported out of the box.
- Expose the language level as a plugin parameter so users can opt in:
<configuration>
<javaParserLanguageLevel>JAVA_21</javaParserLanguageLevel>
</configuration>
- Fail the build on parse error, or at least surface it as an
[ERROR] rather than [WARNING]. Silent skip of a JAX-RS class is much worse than a hard failure — the user can decide to downgrade to a warning if they intentionally have non-JAX-RS files that fail to parse.
(1) + (3) combined would be the most robust default: support modern syntax, and refuse to silently skip a class even if a future syntax breaks parsing.
Workaround documented for the meantime
For others hitting this, the workaround is to constrain JAX-RS resource sources to pre-Java-14 syntax (no switch expressions, no record, no multi-label case, no pattern matching, no text blocks). The compiled bytecode can still target any JDK; only the source files scanned by unknow-server-maven need to obey the restriction.
I've documented the trap and three related ones (@Path on interface vs impl, @Consumes at class level + GET → 415, - in @Path filename) in our team's onboarding rule. Happy to PR them as a KNOWN-ISSUES.md upstream if useful.
Thanks for the great plugin — the startup-time / image-size win over Spring Boot is real and we're shipping with it in production.
Summary
When a JAX-RS resource class in
src/main/javauses Java 14+ syntax (switch expressions, multi-labelcase,record, pattern matching, text blocks), theunknow-server-mavenplugin's JavaParser fails to parse the file, the file is silently skipped, and the build still succeeds. The resulting JAR boots fine but every request to that resource returns404because noJaxrs_<path>.javawas generated.The only signal is a single
[WARNING] Failed to parse <File>.java: …line buried in thegenerate-sourceslog — easy to miss in CI output. Maven's exit code is0.Versions
io.github.unknow0.server:unknow-server-maven0.9.9maven-compiler-plugin3.13.0 withmaven.compiler.source/target = 17Minimal repro
Single-module Maven project. JAX-RS impl with Java 14+ syntax:
mvn clean package -DskipTestsoutput:At runtime:
curl http://localhost:8080/mcp/tools→404.target/jaxrs-generator/resources/openapi.jsonis empty ("paths":{}).Root cause
AbstractGeneratorMojoconstructs JavaParser without setting a language level:JavaParser's default
ParserConfigurationlanguage level isPOPULAR(currently mapped toJAVA_11), so anything from Java 12 onward fails. TheSrcWalker(called fromprocess(TypeConsumer)) swallows the parse error and continues with the remaining types.Suggested fixes
Any one of these would have caught the issue in my project:
ParserConfiguration— e.g.setLanguageLevel(LanguageLevel.JAVA_21)(or the highest released). Modern projects on JDK 21+ would be supported out of the box.[ERROR]rather than[WARNING]. Silent skip of a JAX-RS class is much worse than a hard failure — the user can decide to downgrade to a warning if they intentionally have non-JAX-RS files that fail to parse.(1) + (3) combined would be the most robust default: support modern syntax, and refuse to silently skip a class even if a future syntax breaks parsing.
Workaround documented for the meantime
For others hitting this, the workaround is to constrain JAX-RS resource sources to pre-Java-14 syntax (no switch expressions, no
record, no multi-labelcase, no pattern matching, no text blocks). The compiled bytecode can still target any JDK; only the source files scanned byunknow-server-mavenneed to obey the restriction.I've documented the trap and three related ones (
@Pathon interface vs impl,@Consumesat class level + GET → 415,-in@Pathfilename) in our team's onboarding rule. Happy to PR them as aKNOWN-ISSUES.mdupstream if useful.Thanks for the great plugin — the startup-time / image-size win over Spring Boot is real and we're shipping with it in production.