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 try {
302 XmlPlexusConfiguration configuration = new XmlPlexusConfiguration(ruleProducer.getRulesConfig());
303 getLog().info(String.format("Rule Config Provider %s executed", getRuleName(ruleDesc)));
304
305 return configuration;
306 } catch (EnforcerRuleException e) {
307 throw new EnforcerRuleManagerException("Rules Provider error for: " + getRuleName(ruleDesc), e);
308 }
309 }
310
311 private void executeRule(int ruleIndex, EnforcerRuleDesc ruleDesc, EnforcerRuleHelper helper)
312 throws EnforcerRuleException {
313
314 if (getLog().isDebugEnabled()) {
315 getLog().debug(String.format("Executing Rule %d: %s", ruleIndex, ruleDesc));
316 }
317
318 long startTime = System.currentTimeMillis();
319
320 try {
321 if (ruleDesc.getRule() instanceof EnforcerRule) {
322 executeRuleOld(ruleIndex, ruleDesc, helper);
323 } else if (ruleDesc.getRule() instanceof AbstractEnforcerRule) {
324 executeRuleNew(ruleIndex, ruleDesc);
325 }
326 } finally {
327 if (getLog().isDebugEnabled()) {
328 long workTime = System.currentTimeMillis() - startTime;
329 getLog().debug(String.format(
330 "Finish Rule %d: %s takes %d ms", ruleIndex, getRuleName(ruleDesc), workTime));
331 }
332 }
333 }
334
335 private void executeRuleOld(int ruleIndex, EnforcerRuleDesc ruleDesc, EnforcerRuleHelper helper)
336 throws EnforcerRuleException {
337
338 EnforcerRule rule = (EnforcerRule) ruleDesc.getRule();
339
340 if (ignoreCache || shouldExecute(rule)) {
341 rule.execute(helper);
342 getLog().info(String.format("Rule %d: %s passed", ruleIndex, getRuleName(ruleDesc)));
343 }
344 }
345
346 private void executeRuleNew(int ruleIndex, EnforcerRuleDesc ruleDesc) throws EnforcerRuleException {
347
348 AbstractEnforcerRule rule = (AbstractEnforcerRule) ruleDesc.getRule();
349 if (ignoreCache || !ruleCache.isCached(rule)) {
350 rule.execute();
351 getLog().info(String.format("Rule %d: %s passed", ruleIndex, getRuleName(ruleDesc)));
352 }
353 }
354
355
356
357
358
359
360 private Optional<PlexusConfiguration> createRulesFromCommandLineOptions() {
361
362 if (rulesToExecute == null || rulesToExecute.isEmpty()) {
363 return Optional.empty();
364 }
365
366 PlexusConfiguration configuration = new DefaultPlexusConfiguration("rules");
367 for (String rule : rulesToExecute) {
368 PlexusConfiguration configuredRule = null;
369
370 if (rules != null) {
371
372 String ruleLower = Character.toLowerCase(rule.charAt(0)) + rule.substring(1);
373 String ruleUpper = Character.toUpperCase(rule.charAt(0)) + rule.substring(1);
374 configuredRule = rules.getChild(ruleLower, false);
375 if (configuredRule == null) {
376 configuredRule = rules.getChild(ruleUpper, false);
377 }
378 }
379
380 if (configuredRule != null) {
381 configuration.addChild(configuredRule);
382 } else {
383 configuration.addChild(new DefaultPlexusConfiguration(rule));
384 }
385 }
386 return Optional.of(configuration);
387 }
388
389
390
391
392
393
394
395 private List<EnforcerRuleDesc> filterOutSkippedRules(List<EnforcerRuleDesc> allRules) {
396 if (rulesToSkip == null || rulesToSkip.isEmpty()) {
397 return allRules;
398 }
399 return allRules.stream()
400 .filter(ruleDesc -> !rulesToSkip.contains(ruleDesc.getName()))
401 .collect(Collectors.toList());
402 }
403
404
405
406
407
408
409
410 protected boolean shouldExecute(EnforcerRule rule) {
411 if (rule.isCacheable()) {
412 Log log = this.getLog();
413 log.debug("Rule " + rule.getClass().getName() + " is cacheable.");
414 String key = rule.getClass().getName() + " " + rule.getCacheId();
415 if (EnforceMojo.cache.containsKey(key)) {
416 log.debug("Key " + key + " was found in the cache");
417 if (rule.isResultValid(cache.get(key))) {
418 log.debug("The cached results are still valid. Skipping the rule: "
419 + rule.getClass().getName());
420 return false;
421 }
422 }
423
424
425 EnforceMojo.cache.put(key, rule);
426 }
427 return true;
428 }
429
430
431
432
433
434
435 public void setRulesToSkip(List<String> rulesToSkip) {
436 if (rulesToSkip == null) {
437 return;
438 }
439
440 this.rulesToSkip = rulesToSkip.stream()
441 .filter(Objects::nonNull)
442 .map(StringUtils::lowercaseFirstLetter)
443 .collect(Collectors.toList());
444 }
445
446
447
448
449 public void setFail(boolean theFail) {
450 this.fail = theFail;
451 }
452
453
454
455
456 public void setFailFast(boolean theFailFast) {
457 this.failFast = theFailFast;
458 }
459
460 private String createRuleMessage(
461 int ruleIndex, EnforcerRuleDesc ruleDesc, EnforcerLevel level, EnforcerRuleException e) {
462
463 StringBuilder result = new StringBuilder();
464 result.append("Rule ").append(ruleIndex).append(": ").append(getRuleName(ruleDesc));
465
466 if (level == EnforcerLevel.ERROR) {
467 result.append(" failed");
468 } else {
469 result.append(" warned");
470 }
471
472 if (e.getMessage() != null) {
473 result.append(" with message:").append(System.lineSeparator()).append(e.getMessage());
474 } else {
475 result.append(" without a message");
476 }
477
478 return result.toString();
479 }
480
481 private String getRuleName(EnforcerRuleDesc ruleDesc) {
482
483 Class<? extends EnforcerRuleBase> ruleClass = ruleDesc.getRule().getClass();
484
485 String ruleName = ruleClass.getName();
486
487 if (!ruleClass.getSimpleName().equalsIgnoreCase(ruleDesc.getName())) {
488 ruleName += "(" + ruleDesc.getName() + ")";
489 }
490
491 return ruleName;
492 }
493
494
495
496
497 public void setFailIfNoRules(boolean thefailIfNoRules) {
498 this.failIfNoRules = thefailIfNoRules;
499 }
500 }