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