1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 package com.qulice.maven;
32
33 import com.google.common.base.Predicate;
34 import com.google.common.base.Predicates;
35 import com.google.common.collect.Collections2;
36 import com.jcabi.log.Logger;
37 import com.qulice.spi.ValidationException;
38 import java.util.Collection;
39 import java.util.LinkedList;
40 import org.apache.maven.artifact.Artifact;
41 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalysis;
42 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzer;
43 import org.apache.maven.shared.dependency.analyzer.ProjectDependencyAnalyzerException;
44 import org.cactoos.text.Joined;
45 import org.codehaus.plexus.PlexusConstants;
46 import org.codehaus.plexus.PlexusContainer;
47 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
48 import org.codehaus.plexus.context.ContextException;
49
50
51
52
53
54
55
56 final class DependenciesValidator implements MavenValidator {
57
58
59
60
61 private static final String SEP = "\n\t";
62
63 @Override
64 @SuppressWarnings("PMD.OnlyOneReturn")
65 public void validate(final MavenEnvironment env)
66 throws ValidationException {
67 if (!env.outdir().exists() || "pom".equals(env.project().getPackaging())) {
68 Logger.info(this, "No dependency analysis in this project");
69 return;
70 }
71 final Collection<String> excludes = env.excludes("dependencies");
72 if (excludes.contains(".*")) {
73 Logger.info(this, "Dependency analysis suppressed in the project via pom.xml");
74 return;
75 }
76 final Collection<String> unused = Collections2.filter(
77 DependenciesValidator.unused(env),
78 Predicates.not(new DependenciesValidator.ExcludePredicate(excludes))
79 );
80 if (!unused.isEmpty()) {
81 Logger.warn(
82 this,
83 "Unused declared dependencies found:%s%s",
84 DependenciesValidator.SEP,
85 new Joined(DependenciesValidator.SEP, unused).toString()
86 );
87 }
88 final Collection<String> used = Collections2.filter(
89 DependenciesValidator.used(env),
90 Predicates.not(new DependenciesValidator.ExcludePredicate(excludes))
91 );
92 if (!used.isEmpty()) {
93 Logger.warn(
94 this,
95 "Used undeclared dependencies found:%s%s",
96 DependenciesValidator.SEP,
97 new Joined(DependenciesValidator.SEP, used)
98 );
99 }
100 if (!used.isEmpty() || !unused.isEmpty()) {
101 Logger.info(
102 this,
103 "You can suppress this message by <exclude>dependencies:...</exclude> in pom.xml, where <...> is what the dependency name starts with (not a regular expression!)"
104 );
105 }
106 final int failures = used.size() + unused.size();
107 if (failures > 0) {
108 throw new ValidationException(
109 "%d dependency problem(s) found",
110 failures
111 );
112 }
113 Logger.info(this, "No dependency problems found");
114 }
115
116
117
118
119
120
121 private static ProjectDependencyAnalysis analyze(
122 final MavenEnvironment env) {
123 try {
124 return ((ProjectDependencyAnalyzer)
125 ((PlexusContainer)
126 env.context().get(PlexusConstants.PLEXUS_KEY)
127 ).lookup(ProjectDependencyAnalyzer.class.getName(), "default")
128 ).analyze(env.project());
129 } catch (final ContextException | ComponentLookupException
130 | ProjectDependencyAnalyzerException ex) {
131 throw new IllegalStateException(ex);
132 }
133 }
134
135
136
137
138
139
140 private static Collection<String> used(final MavenEnvironment env) {
141 final ProjectDependencyAnalysis analysis =
142 DependenciesValidator.analyze(env);
143 final Collection<String> used = new LinkedList<>();
144 for (final Object artifact : analysis.getUsedUndeclaredArtifacts()) {
145 used.add(artifact.toString());
146 }
147 return used;
148 }
149
150
151
152
153
154
155 private static Collection<String> unused(final MavenEnvironment env) {
156 final ProjectDependencyAnalysis analysis =
157 DependenciesValidator.analyze(env);
158 final Collection<String> unused = new LinkedList<>();
159 for (final Object obj : analysis.getUnusedDeclaredArtifacts()) {
160 final Artifact artifact = (Artifact) obj;
161 if (!Artifact.SCOPE_COMPILE.equals(artifact.getScope())) {
162 continue;
163 }
164 unused.add(artifact.toString());
165 }
166 return unused;
167 }
168
169
170
171
172
173
174 private static class ExcludePredicate implements Predicate<String> {
175
176
177
178
179 private final Collection<String> excludes;
180
181
182
183
184
185 ExcludePredicate(final Collection<String> excludes) {
186 this.excludes = excludes;
187 }
188
189 @Override
190 public boolean apply(final String name) {
191 boolean ignore = false;
192 for (final String exclude : this.excludes) {
193 if (name.startsWith(exclude)) {
194 ignore = true;
195 break;
196 }
197 }
198 return ignore;
199 }
200 }
201 }