1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.enforcer;
20
21 import java.util.ArrayList;
22 import java.util.Hashtable;
23 import java.util.List;
24 import java.util.Objects;
25 import java.util.Optional;
26 import java.util.stream.Collectors;
27
28 import org.apache.maven.enforcer.rule.api.AbstractEnforcerRule;
29 import org.apache.maven.enforcer.rule.api.AbstractEnforcerRuleConfigProvider;
30 import org.apache.maven.enforcer.rule.api.EnforcerLevel;
31 import org.apache.maven.enforcer.rule.api.EnforcerRule;
32 import org.apache.maven.enforcer.rule.api.EnforcerRuleBase;
33 import org.apache.maven.enforcer.rule.api.EnforcerRuleError;
34 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
35 import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
36 import org.apache.maven.execution.MavenSession;
37 import org.apache.maven.plugin.AbstractMojo;
38 import org.apache.maven.plugin.MojoExecution;
39 import org.apache.maven.plugin.MojoExecutionException;
40 import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
41 import org.apache.maven.plugin.logging.Log;
42 import org.apache.maven.plugins.annotations.Component;
43 import org.apache.maven.plugins.annotations.LifecyclePhase;
44 import org.apache.maven.plugins.annotations.Mojo;
45 import org.apache.maven.plugins.annotations.Parameter;
46 import org.apache.maven.plugins.annotations.ResolutionScope;
47 import org.apache.maven.plugins.enforcer.internal.DefaultEnforcementRuleHelper;
48 import org.apache.maven.plugins.enforcer.internal.EnforcerRuleCache;
49 import org.apache.maven.plugins.enforcer.internal.EnforcerRuleDesc;
50 import org.apache.maven.plugins.enforcer.internal.EnforcerRuleManager;
51 import org.apache.maven.plugins.enforcer.internal.EnforcerRuleManagerException;
52 import org.apache.maven.project.MavenProject;
53 import org.codehaus.plexus.PlexusContainer;
54 import org.codehaus.plexus.configuration.DefaultPlexusConfiguration;
55 import org.codehaus.plexus.configuration.PlexusConfiguration;
56 import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
57 import org.codehaus.plexus.util.StringUtils;
58
59
60
61
62
63
64 @Mojo(
65 name = "enforce",
66 defaultPhase = LifecyclePhase.VALIDATE,
67 requiresDependencyCollection = ResolutionScope.TEST,
68 threadSafe = true)
69 public class EnforceMojo extends AbstractMojo {
70
71
72
73 protected static Hashtable<String, EnforcerRule> cache = new Hashtable<>();
74
75
76
77
78 @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
79 protected MojoExecution mojoExecution;
80
81
82
83
84 @Parameter(defaultValue = "${session}", readonly = true, required = true)
85 protected MavenSession session;
86
87
88
89
90 @Parameter(defaultValue = "${project}", readonly = true, required = true)
91 protected MavenProject project;
92
93
94
95
96 @Parameter(property = "enforcer.skip", defaultValue = "false")
97 protected boolean skip = false;
98
99
100
101
102 @Parameter(property = "enforcer.fail", defaultValue = "true")
103 private boolean fail = true;
104
105
106
107
108 @Parameter(property = "enforcer.failFast", defaultValue = "false")
109 private boolean failFast = false;
110
111
112
113
114
115
116 @Parameter(property = "enforcer.failIfNoRules", defaultValue = "true")
117 private boolean failIfNoRules = true;
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136 @Parameter
137 private PlexusConfiguration rules;
138
139
140
141
142
143
144 @Parameter(required = false, property = "enforcer.skipRules")
145 private List<String> rulesToSkip;
146
147
148
149
150
151 @Parameter(property = "enforcer.ignoreCache", defaultValue = "false")
152 protected boolean ignoreCache = false;
153
154 @Component
155 private PlexusContainer container;
156
157 @Component
158 private EnforcerRuleManager enforcerRuleManager;
159
160 @Component
161 private EnforcerRuleCache ruleCache;
162
163 private List<String> rulesToExecute;
164
165
166
167
168
169
170
171
172 @Parameter(required = false, property = "enforcer.rules")
173 public void setRulesToExecute(List<String> rulesToExecute) throws MojoExecutionException {
174 if (rulesToExecute != null && !rulesToExecute.isEmpty()) {
175 if (this.rulesToExecute != null && !this.rulesToExecute.isEmpty()) {
176 throw new MojoExecutionException("Detected the usage of both '-Drules' (which is deprecated) "
177 + "and '-Denforcer.rules'. Please use only one of them, preferably '-Denforcer.rules'.");
178 }
179 this.rulesToExecute = rulesToExecute;
180 }
181 }
182
183
184
185
186
187
188
189
190 @Parameter(required = false, property = "rules")
191 @Deprecated
192 public void setCommandLineRules(List<String> rulesToExecute) throws MojoExecutionException {
193 if (rulesToExecute != null && !rulesToExecute.isEmpty()) {
194 getLog().warn(
195 "Detected the usage of property '-Drules' which is deprecated. Use '-Denforcer.rules' instead.");
196 }
197 setRulesToExecute(rulesToExecute);
198 }
199
200 @Override
201 public void execute() throws MojoExecutionException {
202 Log log = this.getLog();
203
204 if (skip) {
205 log.info("Skipping Rule Enforcement.");
206 return;
207 }
208
209 Optional<PlexusConfiguration> rulesFromCommandLine = createRulesFromCommandLineOptions();
210 List<EnforcerRuleDesc> rulesList;
211
212
213 List<EnforcerRuleDesc> allRules = enforcerRuleManager.createRules(rulesFromCommandLine.orElse(rules), log);
214 rulesList = filterOutSkippedRules(allRules);
215
216 List<EnforcerRuleDesc> additionalRules = processRuleConfigProviders(rulesList);
217 rulesList = filterOutRuleConfigProviders(rulesList);
218 rulesList.addAll(additionalRules);
219
220 if (rulesList.isEmpty()) {
221 if (failIfNoRules) {
222 throw new MojoExecutionException(
223 "No rules are configured. Use the skip flag if you want to disable execution.");
224 } else {
225 log.warn("No rules are configured.");
226 return;
227 }
228 }
229
230
231 PluginParameterExpressionEvaluator evaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
232 EnforcerRuleHelper helper = new DefaultEnforcementRuleHelper(session, evaluator, log, container);
233
234
235
236 if (!fail) {
237 failFast = false;
238 }
239
240 List<String> errorMessages = new ArrayList<>();
241
242
243 for (int ruleIndex = 0; ruleIndex < rulesList.size(); ruleIndex++) {
244
245 EnforcerRuleDesc ruleDesc = rulesList.get(ruleIndex);
246 EnforcerLevel level = ruleDesc.getLevel();
247 try {
248 executeRule(ruleIndex, ruleDesc, helper);
249 } catch (EnforcerRuleError e) {
250 String ruleMessage = createRuleMessage(ruleIndex, ruleDesc, EnforcerLevel.ERROR, e);
251 throw new MojoExecutionException(System.lineSeparator() + ruleMessage, e);
252 } catch (EnforcerRuleException e) {
253
254 String ruleMessage = createRuleMessage(ruleIndex, ruleDesc, level, e);
255
256 if (failFast && level == EnforcerLevel.ERROR) {
257 throw new MojoExecutionException(System.lineSeparator() + ruleMessage, e);
258 }
259
260 if (level == EnforcerLevel.ERROR) {
261 errorMessages.add(ruleMessage);
262 } else {
263 log.warn(ruleMessage);
264 }
265 }
266 }
267
268 if (!errorMessages.isEmpty()) {
269 if (fail) {
270 throw new MojoExecutionException(
271 System.lineSeparator() + String.join(System.lineSeparator(), errorMessages));
272 } else {
273 errorMessages.forEach(log::warn);
274 }
275 }
276 }
277
278 private List<EnforcerRuleDesc> processRuleConfigProviders(List<EnforcerRuleDesc> rulesList) {
279 return rulesList.stream()
280 .filter(Objects::nonNull)
281 .filter(rd -> rd.getRule() instanceof AbstractEnforcerRuleConfigProvider)
282 .map(this::executeRuleConfigProvider)
283 .flatMap(xml -> enforcerRuleManager.createRules(xml, getLog()).stream())
284 .collect(Collectors.toList());
285 }
286
287 private List<EnforcerRuleDesc> filterOutRuleConfigProviders(List<EnforcerRuleDesc> rulesList) {
288 return rulesList.stream()
289 .filter(Objects::nonNull)
290 .filter(rd -> !(rd.getRule() instanceof AbstractEnforcerRuleConfigProvider))
291 .collect(Collectors.toList());
292 }
293
294 private XmlPlexusConfiguration executeRuleConfigProvider(EnforcerRuleDesc ruleDesc) {
295 AbstractEnforcerRuleConfigProvider ruleProducer = (AbstractEnforcerRuleConfigProvider) ruleDesc.getRule();
296
297 if (getLog().isDebugEnabled()) {
298 getLog().debug(String.format("Executing Rule Config Provider %s", ruleDesc.getRule()));
299 }
300
301 XmlPlexusConfiguration configuration = null;
302 try {
303 configuration = new XmlPlexusConfiguration(ruleProducer.getRulesConfig());
304 } catch (EnforcerRuleException e) {
305 throw new EnforcerRuleManagerException("Rules Provider error for: " + getRuleName(ruleDesc), e);
306 }
307 getLog().info(String.format("Rule Config Provider %s executed", getRuleName(ruleDesc)));
308
309 return configuration;
310 }
311
312 private void executeRule(int ruleIndex, EnforcerRuleDesc ruleDesc, EnforcerRuleHelper helper)
313 throws EnforcerRuleException {
314
315 if (getLog().isDebugEnabled()) {
316 getLog().debug(String.format("Executing Rule %d: %s", ruleIndex, ruleDesc));
317 }
318
319 long startTime = System.currentTimeMillis();
320
321 try {
322 if (ruleDesc.getRule() instanceof EnforcerRule) {
323 executeRuleOld(ruleIndex, ruleDesc, helper);
324 } else if (ruleDesc.getRule() instanceof AbstractEnforcerRule) {
325 executeRuleNew(ruleIndex, ruleDesc);
326 }
327 } finally {
328 if (getLog().isDebugEnabled()) {
329 long workTime = System.currentTimeMillis() - startTime;
330 getLog().debug(String.format(
331 "Finish Rule %d: %s takes %d ms", ruleIndex, getRuleName(ruleDesc), workTime));
332 }
333 }
334 }
335
336 private void executeRuleOld(int ruleIndex, EnforcerRuleDesc ruleDesc, EnforcerRuleHelper helper)
337 throws EnforcerRuleException {
338
339 EnforcerRule rule = (EnforcerRule) ruleDesc.getRule();
340
341 if (ignoreCache || shouldExecute(rule)) {
342 rule.execute(helper);
343 getLog().info(String.format("Rule %d: %s passed", ruleIndex, getRuleName(ruleDesc)));
344 }
345 }
346
347 private void executeRuleNew(int ruleIndex, EnforcerRuleDesc ruleDesc) throws EnforcerRuleException {
348
349 AbstractEnforcerRule rule = (AbstractEnforcerRule) ruleDesc.getRule();
350 if (ignoreCache || !ruleCache.isCached(rule)) {
351 rule.execute();
352 getLog().info(String.format("Rule %d: %s passed", ruleIndex, getRuleName(ruleDesc)));
353 }
354 }
355
356
357
358
359
360
361 private Optional<PlexusConfiguration> createRulesFromCommandLineOptions() {
362
363 if (rulesToExecute == null || rulesToExecute.isEmpty()) {
364 return Optional.empty();
365 }
366
367 PlexusConfiguration configuration = new DefaultPlexusConfiguration("rules");
368 for (String rule : rulesToExecute) {
369 configuration.addChild(new DefaultPlexusConfiguration(rule));
370 }
371 return Optional.of(configuration);
372 }
373
374
375
376
377
378
379
380 private List<EnforcerRuleDesc> filterOutSkippedRules(List<EnforcerRuleDesc> allRules) {
381 if (rulesToSkip == null || rulesToSkip.isEmpty()) {
382 return allRules;
383 }
384 return allRules.stream()
385 .filter(ruleDesc -> !rulesToSkip.contains(ruleDesc.getName()))
386 .collect(Collectors.toList());
387 }
388
389
390
391
392
393
394
395 protected boolean shouldExecute(EnforcerRule rule) {
396 if (rule.isCacheable()) {
397 Log log = this.getLog();
398 log.debug("Rule " + rule.getClass().getName() + " is cacheable.");
399 String key = rule.getClass().getName() + " " + rule.getCacheId();
400 if (EnforceMojo.cache.containsKey(key)) {
401 log.debug("Key " + key + " was found in the cache");
402 if (rule.isResultValid(cache.get(key))) {
403 log.debug("The cached results are still valid. Skipping the rule: "
404 + rule.getClass().getName());
405 return false;
406 }
407 }
408
409
410 EnforceMojo.cache.put(key, rule);
411 }
412 return true;
413 }
414
415
416
417
418
419
420 public void setRulesToSkip(List<String> rulesToSkip) {
421 if (rulesToSkip == null) {
422 return;
423 }
424
425 this.rulesToSkip = rulesToSkip.stream()
426 .filter(Objects::nonNull)
427 .map(StringUtils::lowercaseFirstLetter)
428 .collect(Collectors.toList());
429 }
430
431
432
433
434 public void setFail(boolean theFail) {
435 this.fail = theFail;
436 }
437
438
439
440
441 public void setFailFast(boolean theFailFast) {
442 this.failFast = theFailFast;
443 }
444
445 private String createRuleMessage(
446 int ruleIndex, EnforcerRuleDesc ruleDesc, EnforcerLevel level, EnforcerRuleException e) {
447
448 StringBuilder result = new StringBuilder();
449 result.append("Rule ").append(ruleIndex).append(": ").append(getRuleName(ruleDesc));
450
451 if (level == EnforcerLevel.ERROR) {
452 result.append(" failed");
453 } else {
454 result.append(" warned");
455 }
456
457 if (e.getMessage() != null) {
458 result.append(" with message:").append(System.lineSeparator()).append(e.getMessage());
459 } else {
460 result.append(" without a message");
461 }
462
463 return result.toString();
464 }
465
466 private String getRuleName(EnforcerRuleDesc ruleDesc) {
467
468 Class<? extends EnforcerRuleBase> ruleClass = ruleDesc.getRule().getClass();
469
470 String ruleName = ruleClass.getName();
471
472 if (!ruleClass.getSimpleName().equalsIgnoreCase(ruleDesc.getName())) {
473 ruleName += "(" + ruleDesc.getName() + ")";
474 }
475
476 return ruleName;
477 }
478
479
480
481
482 public void setFailIfNoRules(boolean thefailIfNoRules) {
483 this.failIfNoRules = thefailIfNoRules;
484 }
485 }