From e60e738c05978f7091f7e75e2aab1e6088ac0043 Mon Sep 17 00:00:00 2001 From: Eric Blanquer Date: Wed, 18 Feb 2026 14:19:11 +0100 Subject: [PATCH] Fix NPE when Model.getLocation() returns null Maven core extensions such as maven-git-versioning-extension can modify the Maven model at runtime without preserving InputLocation metadata. This causes Model.getLocation("") to return null, leading to NullPointerException in AnnotationMappingMetadataSource and SourceLocationHelper. Add null checks for InputLocation and InputSource in all code paths that dereference Model.getLocation() results. Fixes #1310 --- .../.mvn/extensions.xml | 8 +++++ .../projects/gitVersioningExtension/pom.xml | 6 ++++ .../org/eclipse/m2e/core/ExtensionsTest.java | 19 ++++++++++++ .../AnnotationMappingMetadataSource.java | 16 ++++++++-- .../markers/SourceLocationHelper.java | 29 +++++++++++++++---- 5 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 org.eclipse.m2e.core.tests/resources/projects/gitVersioningExtension/.mvn/extensions.xml create mode 100644 org.eclipse.m2e.core.tests/resources/projects/gitVersioningExtension/pom.xml diff --git a/org.eclipse.m2e.core.tests/resources/projects/gitVersioningExtension/.mvn/extensions.xml b/org.eclipse.m2e.core.tests/resources/projects/gitVersioningExtension/.mvn/extensions.xml new file mode 100644 index 0000000000..4291456896 --- /dev/null +++ b/org.eclipse.m2e.core.tests/resources/projects/gitVersioningExtension/.mvn/extensions.xml @@ -0,0 +1,8 @@ + + + + me.qoomon + maven-git-versioning-extension + 9.11.0 + + diff --git a/org.eclipse.m2e.core.tests/resources/projects/gitVersioningExtension/pom.xml b/org.eclipse.m2e.core.tests/resources/projects/gitVersioningExtension/pom.xml new file mode 100644 index 0000000000..479083cb46 --- /dev/null +++ b/org.eclipse.m2e.core.tests/resources/projects/gitVersioningExtension/pom.xml @@ -0,0 +1,6 @@ + + 4.0.0 + test + git-versioning-test + 1.0.0-SNAPSHOT + diff --git a/org.eclipse.m2e.core.tests/src/org/eclipse/m2e/core/ExtensionsTest.java b/org.eclipse.m2e.core.tests/src/org/eclipse/m2e/core/ExtensionsTest.java index 8c728d1783..e191c3296b 100644 --- a/org.eclipse.m2e.core.tests/src/org/eclipse/m2e/core/ExtensionsTest.java +++ b/org.eclipse.m2e.core.tests/src/org/eclipse/m2e/core/ExtensionsTest.java @@ -13,6 +13,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.io.File; import java.io.IOException; import java.util.Collection; @@ -104,6 +105,24 @@ public void testCopyResourcesWithMVNFolder() throws Exception { assertEquals("foo-bar-content", new String(file.getContents().readAllBytes())); } + @Test + public void testGitVersioningExtension() throws Exception { + // Pre-initialize git repo in workspace (required by the extension to activate) + File projectDir = new File(workspace.getRoot().getLocation().toFile(), "gitVersioningExtension"); + copyDir(new File("resources/projects/gitVersioningExtension"), projectDir); + assertEquals(0, new ProcessBuilder("git", "init").directory(projectDir).start().waitFor()); + assertEquals(0, new ProcessBuilder("git", "add", ".").directory(projectDir).start().waitFor()); + assertEquals(0, + new ProcessBuilder("git", "-c", "user.email=test@test.com", "-c", "user.name=Test", "commit", "-m", + "init").directory(projectDir).start().waitFor()); + // Import project - the extension modifies the model, stripping InputLocation metadata + IProject project = importProject("resources/projects/gitVersioningExtension/pom.xml"); + waitForJobsToComplete(monitor); + // Without the fix, Model.getLocation("") returns null causing NPE in + // AnnotationMappingMetadataSource and SourceLocationHelper + assertNoErrors(project); + } + private IProject importPomlessProject(String rootProject, String... poms) throws IOException, CoreException { IProject[] projects = importProjects("resources/projects/" + rootProject + "/", poms, new ResolverConfiguration(), false); diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/AnnotationMappingMetadataSource.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/AnnotationMappingMetadataSource.java index 9f67733d66..f13a015f90 100644 --- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/AnnotationMappingMetadataSource.java +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/lifecyclemapping/AnnotationMappingMetadataSource.java @@ -79,7 +79,12 @@ public static AnnotationMappingMetadataSource get(MavenProject project) { private AnnotationMappingMetadataSource(MavenProject project, List pis) { this.project = project; this.pis = pis; - projectId = project.getModel().getLocation(SELF).getSource().getModelId(); + InputLocation selfLocation = project.getModel().getLocation(SELF); + if(selfLocation != null && selfLocation.getSource() != null) { + projectId = selfLocation.getSource().getModelId(); + } else { + projectId = project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion(); + } } @Override @@ -171,7 +176,14 @@ private PluginExecutionMetadata createMetadata(Xpp3Dom action) { } private static List parsePIs(MavenProject project) { - InputSource source = project.getModel().getLocation(SELF).getSource(); + InputLocation selfLocation = project.getModel().getLocation(SELF); + if(selfLocation == null) { + return List.of(); + } + InputSource source = selfLocation.getSource(); + if(source == null) { + return List.of(); + } File pom = project.getFile(); if((pom == null || !pom.isFile()) && source.getLocation() != null) { pom = new File(source.getLocation()); diff --git a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/markers/SourceLocationHelper.java b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/markers/SourceLocationHelper.java index b9ef82895c..be8cde02a8 100644 --- a/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/markers/SourceLocationHelper.java +++ b/org.eclipse.m2e.core/src/org/eclipse/m2e/core/internal/markers/SourceLocationHelper.java @@ -117,13 +117,16 @@ public static SourceLocation findLocation(MavenProject mavenProject, MojoExecuti } File pomFile = mavenProject.getFile(); - if(pomFile.getAbsolutePath().equals(inputLocation.getSource().getLocation())) { + if(inputLocation.getSource() != null && pomFile.getAbsolutePath().equals(inputLocation.getSource().getLocation())) { // Plugin/execution is specified in current pom return getSourceLocation(inputLocation, elementName); } // Plugin/execution is specified in some parent pom SourceLocation causeLocation = getSourceLocation(inputLocation, elementName); + if(mavenProject.getModel().getParent() == null) { + return new SourceLocation(1, 1, 1, causeLocation); + } inputLocation = mavenProject.getModel().getParent().getLocation(SELF); if(inputLocation == null) { // parent location cannot be determined for participant-added parents @@ -156,8 +159,10 @@ private static org.apache.maven.model.Dependency getMavenDependency(MavenProject if(found != null) { // missing transitive managed dependency - String projectId = mavenProject.getModel().getLocation(SELF).getSource().getModelId(); - String depId = found.getLocation(SELF).getSource().getModelId(); + InputLocation projectSelfLoc = mavenProject.getModel().getLocation(SELF); + InputLocation depSelfLoc = found.getLocation(SELF); + String projectId = projectSelfLoc != null && projectSelfLoc.getSource() != null ? projectSelfLoc.getSource().getModelId() : null; + String depId = depSelfLoc != null && depSelfLoc.getSource() != null ? depSelfLoc.getSource().getModelId() : null; if(!projectId.equals(depId)) { // let's see if it comes from a directly imported pom @@ -224,32 +229,44 @@ public static SourceLocation findLocation(MavenProject mavenProject, org.apache. if(inputLocation == null) { // Should never happen inputLocation = mavenProject.getModel().getLocation(SELF); + if(inputLocation == null) { + return new SourceLocation(1, 1, 1); + } return new SourceLocation(inputLocation.getLineNumber(), 1, inputLocation.getColumnNumber() - COLUMN_END_OFFSET); } File pomFile = mavenProject.getFile(); - if(pomFile.getAbsolutePath().equals(inputLocation.getSource().getLocation())) { + if(inputLocation.getSource() != null && pomFile.getAbsolutePath().equals(inputLocation.getSource().getLocation())) { // Dependency is specified in current pom return getSourceLocation(inputLocation, DEPENDENCY); } - // Plugin/execution is specified in some parent pom + // Dependency is specified in some parent pom SourceLocation causeLocation = getSourceLocation(inputLocation, DEPENDENCY); + if(mavenProject.getModel().getParent() == null) { + return new SourceLocation(1, 1, 1, causeLocation); + } inputLocation = mavenProject.getModel().getParent().getLocation(SELF); return getSourceLocation(inputLocation, PARENT, causeLocation); } private static SourceLocation getSourceLocation(InputLocation inputLocation, String elementName) { + if(inputLocation == null) { + return new SourceLocation(1, 1, 1); + } InputSource source = inputLocation.getSource(); return new SourceLocation( // source != null ? source.getLocation() : null, source != null ? source.getModelId() : null, - inputLocation.getLineNumber(), // + inputLocation.getLineNumber(), // inputLocation.getColumnNumber() - elementName.length() - COLUMN_START_OFFSET, inputLocation.getColumnNumber() - COLUMN_END_OFFSET); } private static SourceLocation getSourceLocation(InputLocation inputLocation, String elementName, SourceLocation causeLocation) { + if(inputLocation == null) { + return new SourceLocation(1, 1, 1, causeLocation); + } return new SourceLocation(inputLocation.getLineNumber(), inputLocation.getColumnNumber() - elementName.length() - COLUMN_START_OFFSET, inputLocation.getColumnNumber() - COLUMN_END_OFFSET, causeLocation);