diff --git a/java/ql/integration-tests/java/maven-add-exports-module-flags/pom.xml b/java/ql/integration-tests/java/maven-add-exports-module-flags/pom.xml
new file mode 100644
index 000000000000..94f5f90981e1
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-add-exports-module-flags/pom.xml
@@ -0,0 +1,41 @@
+
+
+
+ 4.0.0
+
+ com.example
+ maven-add-exports-module-flags
+ 1.0-SNAPSHOT
+
+ maven-add-exports-module-flags
+ Test case: Project using --add-exports. Autobuilder should detect this and use --source/--target.
+
+
+ UTF-8
+ 11
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ ${java.version}
+ ${java.version}
+
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
+ --add-exports
+ jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
+
+
+
+
+
+
diff --git a/java/ql/integration-tests/java/maven-add-exports-module-flags/source_archive.expected b/java/ql/integration-tests/java/maven-add-exports-module-flags/source_archive.expected
new file mode 100644
index 000000000000..3d84bfa09ab3
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-add-exports-module-flags/source_archive.expected
@@ -0,0 +1,3 @@
+pom.xml
+src/main/java/com/example/CompilerUser.java
+target/maven-archiver/pom.properties
diff --git a/java/ql/integration-tests/java/maven-add-exports-module-flags/src/main/java/com/example/CompilerUser.java b/java/ql/integration-tests/java/maven-add-exports-module-flags/src/main/java/com/example/CompilerUser.java
new file mode 100644
index 000000000000..13685d097a48
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-add-exports-module-flags/src/main/java/com/example/CompilerUser.java
@@ -0,0 +1,15 @@
+package com.example;
+
+import com.sun.tools.javac.api.JavacTool;
+
+/**
+ * Simple class that uses JDK compiler internals.
+ * This requires --add-exports flags to compile.
+ */
+public class CompilerUser {
+ public static void main(String[] args) {
+ // Use JavacTool from jdk.compiler module
+ JavacTool tool = JavacTool.create();
+ System.out.println("Compiler tool: " + tool.getClass().getName());
+ }
+}
diff --git a/java/ql/integration-tests/java/maven-add-exports-module-flags/test.py b/java/ql/integration-tests/java/maven-add-exports-module-flags/test.py
new file mode 100644
index 000000000000..73c4b1415a11
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-add-exports-module-flags/test.py
@@ -0,0 +1,2 @@
+def test(codeql, java, actions_toolchains_file):
+ codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
diff --git a/java/ql/integration-tests/java/maven-execution-specific-java-version/pom.xml b/java/ql/integration-tests/java/maven-execution-specific-java-version/pom.xml
new file mode 100644
index 000000000000..fca47a5ba6d8
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-execution-specific-java-version/pom.xml
@@ -0,0 +1,51 @@
+
+
+
+ 4.0.0
+
+ com.example
+ maven-execution-specific-java-version
+ 1.0-SNAPSHOT
+
+ maven-execution-specific-java-version
+ Test case: Project with execution-specific Java versions (Java 11 for main, Java 17 for test). Maven.java should detect the highest version (17) and use it for compilation.
+
+
+ UTF-8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.13.0
+
+
+
+ default-compile
+ compile
+
+ compile
+
+
+ 11
+
+
+
+
+
+ default-testCompile
+ test-compile
+
+ testCompile
+
+
+ 17
+
+
+
+
+
+
+
diff --git a/java/ql/integration-tests/java/maven-execution-specific-java-version/source_archive.expected b/java/ql/integration-tests/java/maven-execution-specific-java-version/source_archive.expected
new file mode 100644
index 000000000000..16e83f3a7f6c
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-execution-specific-java-version/source_archive.expected
@@ -0,0 +1,4 @@
+pom.xml
+src/main/java/com/example/App.java
+src/test/java/com/example/AppTest.java
+target/maven-archiver/pom.properties
diff --git a/java/ql/integration-tests/java/maven-execution-specific-java-version/src/main/java/com/example/App.java b/java/ql/integration-tests/java/maven-execution-specific-java-version/src/main/java/com/example/App.java
new file mode 100644
index 000000000000..74ddfe583487
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-execution-specific-java-version/src/main/java/com/example/App.java
@@ -0,0 +1,12 @@
+package com.example;
+
+public class App {
+ public static void main(String[] args) {
+ var message = "Hello World! Running on Java " + System.getProperty("java.version");
+ System.out.println(message);
+ }
+
+ public String getMessage() {
+ return "Hello from App";
+ }
+}
diff --git a/java/ql/integration-tests/java/maven-execution-specific-java-version/src/test/java/com/example/AppTest.java b/java/ql/integration-tests/java/maven-execution-specific-java-version/src/test/java/com/example/AppTest.java
new file mode 100644
index 000000000000..4f8e04f7d78b
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-execution-specific-java-version/src/test/java/com/example/AppTest.java
@@ -0,0 +1,11 @@
+package com.example;
+
+public class AppTest {
+ public static void main(String[] args) {
+ var text = """
+ Hello
+ World
+ """;
+ System.out.println(text.strip());
+ }
+}
diff --git a/java/ql/integration-tests/java/maven-execution-specific-java-version/test.py b/java/ql/integration-tests/java/maven-execution-specific-java-version/test.py
new file mode 100644
index 000000000000..73c4b1415a11
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-execution-specific-java-version/test.py
@@ -0,0 +1,2 @@
+def test(codeql, java, actions_toolchains_file):
+ codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
diff --git a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/pom.xml b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/pom.xml
new file mode 100644
index 000000000000..e6a09518d8f2
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ 4.0.0
+
+ com.example
+ maven-java16-with-higher-jdk
+ 1.0-SNAPSHOT
+
+ maven-java16-with-higher-jdk
+ Test case: Java 16 target when only Java 17+ is available.
+
+
+ UTF-8
+ 16
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.11.0
+
+ ${java.version}
+
+
+
+
+
diff --git a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/source_archive.expected b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/source_archive.expected
new file mode 100644
index 000000000000..eb5dbc368eea
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/source_archive.expected
@@ -0,0 +1,3 @@
+pom.xml
+src/main/java/com/example/App.java
+target/maven-archiver/pom.properties
diff --git a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/src/main/java/com/example/App.java b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/src/main/java/com/example/App.java
new file mode 100644
index 000000000000..707e579ad68e
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/src/main/java/com/example/App.java
@@ -0,0 +1,15 @@
+package com.example;
+
+import java.util.List;
+
+/**
+ * Simple class using Java 16 features (e.g.,records).
+ */
+public class App {
+ public static void main(String[] args) {
+ Person person = new Person("Bob", 42);
+ System.out.println(person);
+ }
+}
+
+record Person(String name, int age) {}
diff --git a/java/ql/integration-tests/java/maven-java16-with-higher-jdk/test.py b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/test.py
new file mode 100644
index 000000000000..73c4b1415a11
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java16-with-higher-jdk/test.py
@@ -0,0 +1,2 @@
+def test(codeql, java, actions_toolchains_file):
+ codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
diff --git a/java/ql/integration-tests/java/maven-java8-java11-dependency/pom.xml b/java/ql/integration-tests/java/maven-java8-java11-dependency/pom.xml
new file mode 100644
index 000000000000..f50a3aadd235
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java8-java11-dependency/pom.xml
@@ -0,0 +1,41 @@
+
+
+
+ 4.0.0
+
+ com.example
+ maven-java8-java11-dependency
+ 1.0-SNAPSHOT
+
+ maven-java8-java11-dependency
+ Test case: Java 8 project with dependency requiring Java 11+
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+
+
+ org.testng
+ testng
+ 7.7.0
+ test
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+
+
diff --git a/java/ql/integration-tests/java/maven-java8-java11-dependency/source_archive.expected b/java/ql/integration-tests/java/maven-java8-java11-dependency/source_archive.expected
new file mode 100644
index 000000000000..5088f76cc380
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java8-java11-dependency/source_archive.expected
@@ -0,0 +1,4 @@
+pom.xml
+src/main/java/com/example/Calculator.java
+src/test/java/com/example/CalculatorTest.java
+target/maven-archiver/pom.properties
diff --git a/java/ql/integration-tests/java/maven-java8-java11-dependency/src/main/java/com/example/Calculator.java b/java/ql/integration-tests/java/maven-java8-java11-dependency/src/main/java/com/example/Calculator.java
new file mode 100644
index 000000000000..4cc529e4f0f2
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java8-java11-dependency/src/main/java/com/example/Calculator.java
@@ -0,0 +1,11 @@
+package com.example;
+
+public class Calculator {
+ public int add(int a, int b) {
+ return a + b;
+ }
+
+ public int multiply(int a, int b) {
+ return a * b;
+ }
+}
diff --git a/java/ql/integration-tests/java/maven-java8-java11-dependency/src/test/java/com/example/CalculatorTest.java b/java/ql/integration-tests/java/maven-java8-java11-dependency/src/test/java/com/example/CalculatorTest.java
new file mode 100644
index 000000000000..821330a32ccb
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java8-java11-dependency/src/test/java/com/example/CalculatorTest.java
@@ -0,0 +1,21 @@
+package com.example;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+/**
+ * Test class using TestNG 7.7.0 which requires Java 11+.
+ */
+public class CalculatorTest {
+ @Test
+ public void testAdd() {
+ Calculator calc = new Calculator();
+ Assert.assertEquals(calc.add(2, 3), 5);
+ }
+
+ @Test
+ public void testMultiply() {
+ Calculator calc = new Calculator();
+ Assert.assertEquals(calc.multiply(3, 4), 12);
+ }
+}
diff --git a/java/ql/integration-tests/java/maven-java8-java11-dependency/test.py b/java/ql/integration-tests/java/maven-java8-java11-dependency/test.py
new file mode 100644
index 000000000000..73c4b1415a11
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-java8-java11-dependency/test.py
@@ -0,0 +1,2 @@
+def test(codeql, java, actions_toolchains_file):
+ codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/main-module/pom.xml b/java/ql/integration-tests/java/maven-multimodule-test-java-version/main-module/pom.xml
new file mode 100644
index 000000000000..39586e4e0aba
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/main-module/pom.xml
@@ -0,0 +1,10 @@
+
+
+ 4.0.0
+
+ com.example
+ maven-multimodule-test-java-version
+ 1.0
+
+ main-module
+
diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/main-module/src/main/java/com/example/App.java b/java/ql/integration-tests/java/maven-multimodule-test-java-version/main-module/src/main/java/com/example/App.java
new file mode 100644
index 000000000000..d2397803d550
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/main-module/src/main/java/com/example/App.java
@@ -0,0 +1,7 @@
+package com.example;
+
+public class App {
+ public static void main(String[] args) {
+ System.out.println("Hello World!");
+ }
+}
\ No newline at end of file
diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/pom.xml b/java/ql/integration-tests/java/maven-multimodule-test-java-version/pom.xml
new file mode 100644
index 000000000000..ea5f8b5b03b3
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/pom.xml
@@ -0,0 +1,17 @@
+
+
+ 4.0.0
+ com.example
+ maven-multimodule-test-java-version
+ 1.0
+ pom
+
+
+ 17
+
+
+
+ main-module
+ test-module
+
+
diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/source_archive.expected b/java/ql/integration-tests/java/maven-multimodule-test-java-version/source_archive.expected
new file mode 100644
index 000000000000..08385ca91a0a
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/source_archive.expected
@@ -0,0 +1,7 @@
+main-module/pom.xml
+main-module/src/main/java/com/example/App.java
+main-module/target/maven-archiver/pom.properties
+pom.xml
+test-module/pom.xml
+test-module/src/main/java/com/example/tests/TestUtils.java
+test-module/target/maven-archiver/pom.properties
diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/test-module/pom.xml b/java/ql/integration-tests/java/maven-multimodule-test-java-version/test-module/pom.xml
new file mode 100644
index 000000000000..5895a91b2c19
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/test-module/pom.xml
@@ -0,0 +1,14 @@
+
+
+ 4.0.0
+
+ com.example
+ maven-multimodule-test-java-version
+ 1.0
+
+ test-module
+
+
+ 21
+
+
diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/test-module/src/main/java/com/example/tests/TestUtils.java b/java/ql/integration-tests/java/maven-multimodule-test-java-version/test-module/src/main/java/com/example/tests/TestUtils.java
new file mode 100644
index 000000000000..30e83856200f
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/test-module/src/main/java/com/example/tests/TestUtils.java
@@ -0,0 +1,12 @@
+package com.example.tests;
+
+// Requires Java 21: switch with pattern matching and guards
+public class TestUtils {
+ public static String analyze(Object obj) {
+ return switch (obj) {
+ case String s when s.length() > 5 -> "long";
+ case String s -> "short";
+ default -> "other";
+ };
+ }
+}
\ No newline at end of file
diff --git a/java/ql/integration-tests/java/maven-multimodule-test-java-version/test.py b/java/ql/integration-tests/java/maven-multimodule-test-java-version/test.py
new file mode 100644
index 000000000000..73c4b1415a11
--- /dev/null
+++ b/java/ql/integration-tests/java/maven-multimodule-test-java-version/test.py
@@ -0,0 +1,2 @@
+def test(codeql, java, actions_toolchains_file):
+ codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
diff --git a/java/ql/src/change-notes/2025-11-13-maven-default-java-17 b/java/ql/src/change-notes/2025-11-13-maven-default-java-17
new file mode 100644
index 000000000000..26425b103abe
--- /dev/null
+++ b/java/ql/src/change-notes/2025-11-13-maven-default-java-17
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Java analysis now selects the Java version to use informed by Maven POM files across all project modules. It also tries to use Java 17 or higher for all Maven projects if possible, for improved build compatibility.
\ No newline at end of file