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