1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.basepom.mojo.duplicatefinder.artifact;
16
17 import static com.google.common.base.Preconditions.checkNotNull;
18 import static com.google.common.base.Preconditions.checkState;
19 import static java.lang.String.format;
20 import static org.basepom.mojo.duplicatefinder.artifact.ArtifactHelper.getOutputDirectory;
21 import static org.basepom.mojo.duplicatefinder.artifact.ArtifactHelper.getTestOutputDirectory;
22 import static org.basepom.mojo.duplicatefinder.artifact.ArtifactHelper.isTestArtifact;
23
24 import org.basepom.mojo.duplicatefinder.ClasspathElement;
25 import org.basepom.mojo.duplicatefinder.ClasspathElement.ClasspathArtifact;
26 import org.basepom.mojo.duplicatefinder.ClasspathElement.ClasspathLocalFolder;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.Objects;
34 import java.util.Set;
35
36 import com.google.common.annotations.VisibleForTesting;
37 import com.google.common.base.MoreObjects;
38 import com.google.common.collect.ImmutableMap;
39 import com.google.common.collect.ImmutableMultimap;
40 import com.google.common.collect.ImmutableSet;
41 import com.google.common.collect.ImmutableSortedSet;
42 import com.google.common.collect.Multimap;
43 import com.google.common.collect.MultimapBuilder;
44 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
45 import org.apache.maven.artifact.Artifact;
46 import org.apache.maven.artifact.DefaultArtifact;
47 import org.apache.maven.artifact.DependencyResolutionRequiredException;
48 import org.apache.maven.artifact.versioning.VersionRange;
49 import org.apache.maven.project.MavenProject;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53
54
55
56
57
58 @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
59 public class ArtifactFileResolver {
60
61 private static final Logger LOG = LoggerFactory.getLogger(ArtifactFileResolver.class);
62
63
64 private final Multimap<File, Artifact> localFileArtifactCache;
65 private final Map<Artifact, File> localArtifactFileCache;
66
67 private final Map<Artifact, File> repoArtifactCache;
68
69
70
71 private final Multimap<File, Artifact> repoFileCache = MultimapBuilder
72 .hashKeys()
73 .hashSetValues()
74 .build();
75 private final boolean preferLocal;
76
77 public ArtifactFileResolver(final MavenProject project,
78 final boolean preferLocal) throws DependencyResolutionRequiredException, IOException {
79 checkNotNull(project, "project is null");
80 this.preferLocal = preferLocal;
81
82
83
84
85 ImmutableMultimap.Builder<File, Artifact> localFileArtifactCacheBuilder = ImmutableMultimap.builder();
86
87
88 this.repoArtifactCache = new HashMap<>(project.getArtifacts().size());
89
90 for (final Artifact artifact : project.getArtifacts()) {
91 final File repoPath = artifact.getFile().getCanonicalFile();
92 final Artifact canonicalizedArtifact = ArtifactFileResolver.canonicalizeArtifact(artifact);
93
94 checkState(repoPath.exists(), "Repository Path '%s' does not exist.", repoPath);
95 final File oldFile = repoArtifactCache.put(canonicalizedArtifact, repoPath);
96 checkState(oldFile == null || oldFile.equals(repoPath), "Already encountered a file for %s: %s", canonicalizedArtifact, oldFile);
97 repoFileCache.put(repoPath, canonicalizedArtifact);
98 }
99
100 for (final MavenProject referencedProject : project.getProjectReferences().values()) {
101
102 final Set<Artifact> repoArtifacts = findRepoArtifacts(referencedProject, repoArtifactCache);
103
104
105
106
107 if (repoArtifacts.isEmpty()) {
108 LOG.debug(
109 format("Found project reference to %s but no repo reference, probably used in a plugin dependency.", referencedProject.getArtifact()));
110 }
111
112 for (final Artifact artifact : repoArtifacts) {
113
114 final File outputDir = isTestArtifact(artifact) ? getTestOutputDirectory(referencedProject) : getOutputDirectory(referencedProject);
115
116 if (outputDir.exists()) {
117 localFileArtifactCacheBuilder.put(outputDir, artifact);
118 }
119 }
120 }
121
122 this.localFileArtifactCache = localFileArtifactCacheBuilder.build();
123
124
125
126
127 ImmutableMap.Builder<Artifact, File> localArtifactFileCacheBuilder = ImmutableMap.builder();
128 for (Map.Entry<File, Artifact> entry : localFileArtifactCache.entries()) {
129 localArtifactFileCacheBuilder.put(entry.getValue(), entry.getKey());
130 }
131
132 this.localArtifactFileCache = localArtifactFileCacheBuilder.build();
133 }
134
135 public ImmutableMultimap<File, Artifact> resolveArtifactsForScopes(final Set<String> scopes) {
136 checkNotNull(scopes, "scopes is null");
137
138 final ImmutableMultimap.Builder<File, Artifact> inScopeBuilder = ImmutableMultimap.builder();
139 for (final Artifact artifact : listArtifacts()) {
140 if (artifact.getArtifactHandler().isAddedToClasspath()) {
141 if (scopes.isEmpty() || scopes.contains(artifact.getScope())) {
142 final File file = resolveFileForArtifact(artifact);
143 checkState(file != null, "No file for artifact '%s' found!", artifact);
144 inScopeBuilder.put(file, artifact);
145 }
146 }
147 }
148
149 return inScopeBuilder.build();
150 }
151
152 public ImmutableSortedSet<ClasspathElement> getClasspathElementsForElements(final Collection<File> elements) {
153 final ImmutableSortedSet.Builder<ClasspathElement> builder = ImmutableSortedSet.naturalOrder();
154
155 for (final File element : elements) {
156 resolveClasspathElementsForFile(element, builder);
157 }
158 return builder.build();
159 }
160
161 private void resolveClasspathElementsForFile(final File file, ImmutableSet.Builder<ClasspathElement> builder) {
162 checkNotNull(file, "file is null");
163
164 if (preferLocal && localFileArtifactCache.containsKey(file)) {
165 for (Artifact artifact : localFileArtifactCache.get(file)) {
166 builder.add(new ClasspathArtifact(artifact));
167 }
168 return;
169 }
170
171 if (repoFileCache.containsKey(file)) {
172 for (Artifact artifact : repoFileCache.get(file)) {
173 builder.add(new ClasspathArtifact(artifact));
174 }
175 return;
176 }
177
178 if (localFileArtifactCache.containsKey(file)) {
179 for (Artifact artifact : localFileArtifactCache.get(file)) {
180 builder.add(new ClasspathArtifact(artifact));
181 }
182 return;
183 }
184
185 builder.add(new ClasspathLocalFolder(file));
186 }
187
188 private File resolveFileForArtifact(final Artifact artifact) {
189 checkNotNull(artifact, "artifact is null");
190
191 if (preferLocal && localArtifactFileCache.containsKey(artifact)) {
192 return localArtifactFileCache.get(artifact);
193 }
194
195 if (repoArtifactCache.containsKey(artifact)) {
196 return repoArtifactCache.get(artifact);
197 }
198
199 return localArtifactFileCache.get(artifact);
200 }
201
202 @VisibleForTesting
203 static DefaultArtifact canonicalizeArtifact(final Artifact artifact) {
204 final VersionRange versionRange =
205 artifact.getVersionRange() == null ? VersionRange.createFromVersion(artifact.getVersion()) : artifact.getVersionRange();
206 String type = MoreObjects.firstNonNull(artifact.getType(), "jar");
207 String classifier = artifact.getClassifier();
208
209 if ("test-jar".equals(type) && (classifier == null || "tests".equals(classifier))) {
210 type = "jar";
211 classifier = "tests";
212 }
213
214 return new DefaultArtifact(artifact.getGroupId(),
215 artifact.getArtifactId(),
216 versionRange,
217 artifact.getScope(),
218 type,
219 classifier,
220 artifact.getArtifactHandler(),
221 artifact.isOptional());
222 }
223
224 private Set<Artifact> listArtifacts() {
225 return ImmutableSet.<Artifact>builder().addAll(localArtifactFileCache.keySet()).addAll(repoArtifactCache.keySet()).build();
226 }
227
228 private static Set<Artifact> findRepoArtifacts(final MavenProject project, final Map<Artifact, File> repoArtifactCache) {
229 final ImmutableSet.Builder<Artifact> builder = ImmutableSet.builder();
230
231 for (final Artifact artifact : repoArtifactCache.keySet()) {
232 if (Objects.equals(project.getArtifact().getGroupId(), artifact.getGroupId())
233 && Objects.equals(project.getArtifact().getArtifactId(), artifact.getArtifactId())
234 && Objects.equals(project.getArtifact().getBaseVersion(), artifact.getBaseVersion())) {
235 builder.add(artifact);
236 }
237 }
238 return builder.build();
239 }
240 }