Skip to content

Commit c8d2873

Browse files
committed
Fix NPE in setUpClassPathUsingMavenProject when project artifact is unresolvable (issue #504)
When a module has no buildable artifact (e.g. pom-packaged docs modules) and artifact resolution fails with an ignore flag set, resolveArtifact() returns null. That null was added to the set in getCompileArtifacts(), causing an NPE on the subsequent artifact.getFile() call. Guard the add with a null check, mirroring the pattern already used in resolveDependencyToFile(). Add a unit test that reproduces the failure path. https://claude.ai/code/session_01G4SYPaaYXCVf3zMfS8DRUn
1 parent 6e5f1f3 commit c8d2873

2 files changed

Lines changed: 43 additions & 1 deletion

File tree

japicmp-maven-plugin/src/main/java/japicmp/maven/JApiCmpProcessor.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,10 @@ private Set<Artifact> getCompileArtifacts(final MavenProject mavenProject) throw
756756
HashSet<Artifact> result = new HashSet<>(1 + projectDependencies.size());
757757
// Include the project artifact; use the reactor to resolve the project artifact in case it's not being built
758758
Artifact project = RepositoryUtils.toArtifact(mavenProject.getArtifact());
759-
result.add(resolveArtifact(project, ConfigurationVersion.NEW));
759+
Artifact resolvedProject = resolveArtifact(project, ConfigurationVersion.NEW);
760+
if (resolvedProject != null) {
761+
result.add(resolvedProject);
762+
}
760763
for (org.apache.maven.artifact.Artifact dep : projectDependencies) {
761764
if (dep.getArtifactHandler().isAddedToClasspath()) {
762765
if (org.apache.maven.artifact.Artifact.SCOPE_COMPILE.equals(dep.getScope())

japicmp-maven-plugin/src/test/java/japicmp/maven/JApiCmpProcessorTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import static org.hamcrest.MatcherAssert.assertThat;
44
import static org.hamcrest.core.StringContains.containsString;
5+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
56
import static org.junit.jupiter.api.Assertions.assertFalse;
67
import static org.junit.jupiter.api.Assertions.assertThrows;
78
import static org.junit.jupiter.api.Assertions.assertTrue;
89
import static org.junit.jupiter.api.Assertions.fail;
10+
import static org.mockito.ArgumentMatchers.any;
911
import static org.mockito.Mockito.mock;
12+
import static org.mockito.Mockito.when;
1013

1114
import japicmp.cmp.ClassesHelper;
1215
import japicmp.cmp.JarArchiveComparator;
@@ -18,13 +21,20 @@
1821
import japicmp.maven.util.CtMethodBuilder;
1922
import javassist.ClassPool;
2023
import javassist.CtClass;
24+
import org.apache.maven.plugin.MojoExecution;
2125
import org.apache.maven.plugin.MojoExecutionException;
2226
import org.apache.maven.plugin.MojoFailureException;
2327
import org.apache.maven.plugin.logging.Log;
28+
import org.apache.maven.project.MavenProject;
29+
import org.eclipse.aether.RepositorySystem;
30+
import org.eclipse.aether.RepositorySystemSession;
31+
import org.eclipse.aether.repository.RemoteRepository;
32+
import org.eclipse.aether.resolution.ArtifactResolutionException;
2433
import org.junit.jupiter.api.Assertions;
2534
import org.junit.jupiter.api.BeforeEach;
2635
import org.junit.jupiter.api.Test;
2736

37+
import java.lang.reflect.Method;
2838
import java.util.*;
2939

3040
/**
@@ -466,4 +476,33 @@ public List<CtClass> createNewClasses(ClassPool classPool) {
466476
assertThat(msg, containsString("japicmp.Test:SUPERCLASS_REMOVED"));
467477
}
468478
}
479+
480+
@Test
481+
void testSetUpClassPathUsingMavenProjectNoNPEWhenProjectArtifactUnresolvable() throws Exception {
482+
// Reproduce issue #504: when the project artifact cannot be resolved and the ignore flag
483+
// is set, resolveArtifact() returns null. Before the fix, that null was added to the set
484+
// and caused an NPE on the next artifact.getFile() call.
485+
configParams.setIgnoreNonResolvableArtifacts(Boolean.TRUE.toString());
486+
487+
RepositorySystem repoSystem = mock(RepositorySystem.class);
488+
when(repoSystem.resolveArtifact(any(), any()))
489+
.thenThrow(new ArtifactResolutionException(Collections.emptyList()));
490+
491+
RemoteRepository remoteRepo = new RemoteRepository.Builder("default", "releases",
492+
"https://repo.maven.apache.org/maven2").build();
493+
MavenParameters params = new MavenParameters(new ArrayList<>(), new MavenProject(),
494+
mock(MojoExecution.class), "", repoSystem,
495+
mock(RepositorySystemSession.class),
496+
Collections.singletonList(remoteRepo));
497+
498+
JApiCmpProcessor processor = new JApiCmpProcessor(
499+
createPluginParameters(configParams), params, logger);
500+
501+
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
502+
Method method = JApiCmpProcessor.class.getDeclaredMethod(
503+
"setUpClassPathUsingMavenProject", JarArchiveComparatorOptions.class);
504+
method.setAccessible(true);
505+
506+
assertDoesNotThrow(() -> method.invoke(processor, options));
507+
}
469508
}

0 commit comments

Comments
 (0)