View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.buildcache.xml;
20  
21  import javax.annotation.Nonnull;
22  import javax.annotation.Nullable;
23  import javax.inject.Inject;
24  import javax.inject.Named;
25  import javax.inject.Provider;
26  
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.nio.file.Paths;
30  import java.util.ArrayList;
31  import java.util.Collections;
32  import java.util.List;
33  import java.util.Optional;
34  import java.util.regex.Pattern;
35  
36  import org.apache.commons.lang3.StringUtils;
37  import org.apache.commons.lang3.Strings;
38  import org.apache.maven.SessionScoped;
39  import org.apache.maven.buildcache.DefaultPluginScanConfig;
40  import org.apache.maven.buildcache.PluginScanConfig;
41  import org.apache.maven.buildcache.PluginScanConfigImpl;
42  import org.apache.maven.buildcache.hash.HashFactory;
43  import org.apache.maven.buildcache.xml.config.AttachedOutputs;
44  import org.apache.maven.buildcache.xml.config.CacheConfig;
45  import org.apache.maven.buildcache.xml.config.Configuration;
46  import org.apache.maven.buildcache.xml.config.CoordinatesBase;
47  import org.apache.maven.buildcache.xml.config.DirName;
48  import org.apache.maven.buildcache.xml.config.Exclude;
49  import org.apache.maven.buildcache.xml.config.Executables;
50  import org.apache.maven.buildcache.xml.config.ExecutionConfigurationScan;
51  import org.apache.maven.buildcache.xml.config.ExecutionControl;
52  import org.apache.maven.buildcache.xml.config.ExecutionIdsList;
53  import org.apache.maven.buildcache.xml.config.GoalReconciliation;
54  import org.apache.maven.buildcache.xml.config.GoalsList;
55  import org.apache.maven.buildcache.xml.config.Include;
56  import org.apache.maven.buildcache.xml.config.Input;
57  import org.apache.maven.buildcache.xml.config.Local;
58  import org.apache.maven.buildcache.xml.config.MultiModule;
59  import org.apache.maven.buildcache.xml.config.PathSet;
60  import org.apache.maven.buildcache.xml.config.PluginConfigurationScan;
61  import org.apache.maven.buildcache.xml.config.PluginSet;
62  import org.apache.maven.buildcache.xml.config.ProjectVersioning;
63  import org.apache.maven.buildcache.xml.config.PropertyName;
64  import org.apache.maven.buildcache.xml.config.Remote;
65  import org.apache.maven.buildcache.xml.config.TrackedProperty;
66  import org.apache.maven.execution.MavenSession;
67  import org.apache.maven.model.Plugin;
68  import org.apache.maven.model.PluginExecution;
69  import org.apache.maven.plugin.MojoExecution;
70  import org.apache.maven.rtinfo.RuntimeInformation;
71  import org.slf4j.Logger;
72  import org.slf4j.LoggerFactory;
73  
74  import static java.lang.Boolean.TRUE;
75  import static org.apache.maven.buildcache.CacheUtils.getMultimoduleRoot;
76  
77  /**
78   * CacheConfigImpl
79   */
80  @SessionScoped
81  @Named
82  @SuppressWarnings("unused")
83  public class CacheConfigImpl implements org.apache.maven.buildcache.xml.CacheConfig {
84  
85      public static final String CONFIG_PATH_PROPERTY_NAME = "maven.build.cache.configPath";
86      public static final String CACHE_ENABLED_PROPERTY_NAME = "maven.build.cache.enabled";
87      public static final String CACHE_LOCATION_PROPERTY_NAME = "maven.build.cache.location";
88      public static final String REMOTE_ENABLED_PROPERTY_NAME = "maven.build.cache.remote.enabled";
89      public static final String REMOTE_URL_PROPERTY_NAME = "maven.build.cache.remote.url";
90      public static final String REMOTE_SERVER_ID_PROPERTY_NAME = "maven.build.cache.remote.server.id";
91      public static final String SAVE_TO_REMOTE_PROPERTY_NAME = "maven.build.cache.remote.save.enabled";
92      public static final String SAVE_NON_OVERRIDEABLE_NAME = "maven.build.cache.remote.save.final";
93      public static final String FAIL_FAST_PROPERTY_NAME = "maven.build.cache.failFast";
94      public static final String BASELINE_BUILD_URL_PROPERTY_NAME = "maven.build.cache.baselineUrl";
95      public static final String LAZY_RESTORE_PROPERTY_NAME = "maven.build.cache.lazyRestore";
96      public static final String RESTORE_ON_DISK_ARTIFACTS_PROPERTY_NAME = "maven.build.cache.restoreOnDiskArtifacts";
97      public static final String RESTORE_GENERATED_SOURCES_PROPERTY_NAME = "maven.build.cache.restoreGeneratedSources";
98      public static final String ALWAYS_RUN_PLUGINS = "maven.build.cache.alwaysRunPlugins";
99      public static final String MANDATORY_CLEAN = "maven.build.cache.mandatoryClean";
100 
101     /**
102      * Flag to control if we should skip lookup for cached artifacts globally or for a particular project even if
103      * qualifying artifacts exist in build cache.
104      * E.g. to trigger a forced build (full or for a particular module)
105      * May be also activated via properties for projects via a profile e.g. on CI when some files produced by the build
106      * are required (e.g. smth. from target folder as additional CI build artifacts):
107      * {@code <maven.build.cache.skipCache>true<maven.build.cache.skipCache/>}
108      */
109     public static final String CACHE_SKIP = "maven.build.cache.skipCache";
110 
111     /**
112      * Flag to disable cache saving
113      */
114     public static final String SKIP_SAVE = "maven.build.cache.skipSave";
115 
116     private static final Logger LOGGER = LoggerFactory.getLogger(CacheConfigImpl.class);
117 
118     private final XmlService xmlService;
119     private final Provider<MavenSession> providerSession;
120     private final RuntimeInformation rtInfo;
121 
122     private volatile CacheState state;
123     private CacheConfig cacheConfig;
124     private HashFactory hashFactory;
125     private List<Pattern> excludePatterns;
126 
127     @Inject
128     public CacheConfigImpl(XmlService xmlService, Provider<MavenSession> providerSession, RuntimeInformation rtInfo) {
129         this.xmlService = xmlService;
130         this.providerSession = providerSession;
131         this.rtInfo = rtInfo;
132     }
133 
134     @Nonnull
135     @Override
136     public CacheState initialize() {
137         if (state == null) {
138             synchronized (this) {
139                 if (state == null) {
140                     final boolean enabled = getProperty(CACHE_ENABLED_PROPERTY_NAME, true);
141 
142                     if (!rtInfo.isMavenVersion("[3.9.0,)")) {
143                         LOGGER.warn(
144                                 "Cache requires Maven >= 3.9, but version is {}. Disabling cache.",
145                                 rtInfo.getMavenVersion());
146                         state = CacheState.DISABLED;
147                     } else if (!enabled) {
148                         LOGGER.info("Cache disabled by command line flag, project will be built fully and not cached");
149                         state = CacheState.DISABLED;
150                     } else {
151                         Path configPath;
152 
153                         String configPathText = getProperty(CONFIG_PATH_PROPERTY_NAME, null);
154                         if (StringUtils.isNotBlank(configPathText)) {
155                             configPath = Paths.get(configPathText);
156                         } else {
157                             final MavenSession session = providerSession.get();
158                             configPath =
159                                     getMultimoduleRoot(session).resolve(".mvn").resolve("maven-build-cache-config.xml");
160                         }
161 
162                         if (!Files.exists(configPath)) {
163                             LOGGER.info(
164                                     "Cache configuration is not available at configured path {}, "
165                                             + "cache is enabled with defaults",
166                                     configPath);
167                             cacheConfig = new CacheConfig();
168                         } else {
169                             try {
170                                 LOGGER.info("Loading cache configuration from {}", configPath);
171                                 cacheConfig = xmlService.loadCacheConfig(configPath.toFile());
172                             } catch (Exception e) {
173                                 throw new IllegalArgumentException(
174                                         "Cannot initialize cache because xml config is not valid or not available", e);
175                             }
176                         }
177                         fillWithDefaults(cacheConfig);
178 
179                         // `maven.build.cache.enabled` overrides the `enabled` of the XML file
180                         // to allow a disabled configuration to be enabled on the command line
181                         boolean cacheEnabled = getProperty(
182                                 CACHE_ENABLED_PROPERTY_NAME, getConfiguration().isEnabled());
183 
184                         if (!cacheEnabled) {
185                             state = CacheState.DISABLED;
186                         } else {
187                             String hashAlgorithm = null;
188                             try {
189                                 hashAlgorithm = getConfiguration().getHashAlgorithm();
190                                 hashFactory = HashFactory.of(hashAlgorithm);
191                                 LOGGER.info("Using {} hash algorithm for cache", hashAlgorithm);
192                             } catch (Exception e) {
193                                 throw new IllegalArgumentException(
194                                         "Unsupported hashing algorithm: " + hashAlgorithm, e);
195                             }
196 
197                             excludePatterns = compileExcludePatterns();
198                             state = CacheState.INITIALIZED;
199                         }
200                     }
201                 }
202             }
203         }
204         return state;
205     }
206 
207     private void fillWithDefaults(CacheConfig cacheConfig) {
208         if (cacheConfig.getConfiguration() == null) {
209             cacheConfig.setConfiguration(new Configuration());
210         }
211         Configuration configuration = cacheConfig.getConfiguration();
212         if (configuration.getLocal() == null) {
213             configuration.setLocal(new Local());
214         }
215         if (configuration.getRemote() == null) {
216             configuration.setRemote(new Remote());
217         }
218         if (cacheConfig.getInput() == null) {
219             cacheConfig.setInput(new Input());
220         }
221         Input input = cacheConfig.getInput();
222         if (input.getGlobal() == null) {
223             input.setGlobal(new PathSet());
224         }
225     }
226 
227     @Nonnull
228     @Override
229     public List<TrackedProperty> getTrackedProperties(MojoExecution mojoExecution) {
230         checkInitializedState();
231         final GoalReconciliation reconciliationConfig = findReconciliationConfig(mojoExecution);
232         if (reconciliationConfig != null) {
233             return reconciliationConfig.getReconciles();
234         } else {
235             return Collections.emptyList();
236         }
237     }
238 
239     @Override
240     public boolean isLogAllProperties(MojoExecution mojoExecution) {
241         final GoalReconciliation reconciliationConfig = findReconciliationConfig(mojoExecution);
242         if (reconciliationConfig != null && reconciliationConfig.isLogAll()) {
243             return true;
244         }
245         return cacheConfig.getExecutionControl() != null
246                 && cacheConfig.getExecutionControl().getReconcile() != null
247                 && cacheConfig.getExecutionControl().getReconcile().isLogAllProperties();
248     }
249 
250     private GoalReconciliation findReconciliationConfig(MojoExecution mojoExecution) {
251         if (cacheConfig.getExecutionControl() == null) {
252             return null;
253         }
254 
255         final ExecutionControl executionControl = cacheConfig.getExecutionControl();
256         if (executionControl.getReconcile() == null) {
257             return null;
258         }
259 
260         final List<GoalReconciliation> reconciliation =
261                 executionControl.getReconcile().getPlugins();
262 
263         for (GoalReconciliation goalReconciliationConfig : reconciliation) {
264             final String goal = mojoExecution.getGoal();
265 
266             if (isPluginMatch(mojoExecution.getPlugin(), goalReconciliationConfig)
267                     && Strings.CS.equals(goal, goalReconciliationConfig.getGoal())) {
268                 return goalReconciliationConfig;
269             }
270         }
271         return null;
272     }
273 
274     @Nonnull
275     @Override
276     public List<PropertyName> getLoggedProperties(MojoExecution mojoExecution) {
277         checkInitializedState();
278 
279         final GoalReconciliation reconciliationConfig = findReconciliationConfig(mojoExecution);
280         if (reconciliationConfig != null) {
281             return reconciliationConfig.getLogs();
282         } else {
283             return Collections.emptyList();
284         }
285     }
286 
287     @Nonnull
288     @Override
289     public List<PropertyName> getNologProperties(MojoExecution mojoExecution) {
290         checkInitializedState();
291         final GoalReconciliation reconciliationConfig = findReconciliationConfig(mojoExecution);
292         if (reconciliationConfig != null) {
293             return reconciliationConfig.getNologs();
294         } else {
295             return Collections.emptyList();
296         }
297     }
298 
299     @Nonnull
300     @Override
301     public List<String> getEffectivePomExcludeProperties(Plugin plugin) {
302         checkInitializedState();
303         final PluginConfigurationScan pluginConfig = findPluginScanConfig(plugin);
304 
305         if (pluginConfig != null && pluginConfig.getEffectivePom() != null) {
306             return pluginConfig.getEffectivePom().getExcludeProperties();
307         }
308         return Collections.emptyList();
309     }
310 
311     @Override
312     public boolean isPluginDependenciesExcluded(Plugin plugin) {
313         checkInitializedState();
314         final PluginConfigurationScan pluginConfig = findPluginScanConfig(plugin);
315 
316         if (pluginConfig != null) {
317             return pluginConfig.isExcludeDependencies();
318         }
319         return false;
320     }
321 
322     @Nullable
323     @Override
324     public MultiModule getMultiModule() {
325         checkInitializedState();
326         return cacheConfig.getConfiguration().getMultiModule();
327     }
328 
329     private PluginConfigurationScan findPluginScanConfig(Plugin plugin) {
330         if (cacheConfig.getInput() == null) {
331             return null;
332         }
333 
334         final List<PluginConfigurationScan> pluginConfigs =
335                 cacheConfig.getInput().getPlugins();
336         for (PluginConfigurationScan pluginConfig : pluginConfigs) {
337             if (isPluginMatch(plugin, pluginConfig)) {
338                 return pluginConfig;
339             }
340         }
341         return null;
342     }
343 
344     private boolean isPluginMatch(Plugin plugin, CoordinatesBase pluginConfig) {
345         return Strings.CS.equals(pluginConfig.getArtifactId(), plugin.getArtifactId())
346                 && (pluginConfig.getGroupId() == null
347                         || Strings.CS.equals(pluginConfig.getGroupId(), plugin.getGroupId()));
348     }
349 
350     @Nonnull
351     @Override
352     public PluginScanConfig getPluginDirScanConfig(Plugin plugin) {
353         checkInitializedState();
354         final PluginConfigurationScan pluginConfig = findPluginScanConfig(plugin);
355         if (pluginConfig == null || pluginConfig.getDirScan() == null) {
356             return new DefaultPluginScanConfig();
357         }
358 
359         return new PluginScanConfigImpl(pluginConfig.getDirScan());
360     }
361 
362     @Nonnull
363     @Override
364     public PluginScanConfig getExecutionDirScanConfig(Plugin plugin, PluginExecution exec) {
365         checkInitializedState();
366         final PluginConfigurationScan pluginScanConfig = findPluginScanConfig(plugin);
367 
368         if (pluginScanConfig != null) {
369             final ExecutionConfigurationScan executionScanConfig =
370                     findExecutionScanConfig(exec, pluginScanConfig.getExecutions());
371             if (executionScanConfig != null && executionScanConfig.getDirScan() != null) {
372                 return new PluginScanConfigImpl(executionScanConfig.getDirScan());
373             }
374         }
375 
376         return new DefaultPluginScanConfig();
377     }
378 
379     private ExecutionConfigurationScan findExecutionScanConfig(
380             PluginExecution execution, List<ExecutionConfigurationScan> scanConfigs) {
381         for (ExecutionConfigurationScan executionScanConfig : scanConfigs) {
382             if (executionScanConfig.getExecIds().contains(execution.getId())) {
383                 return executionScanConfig;
384             }
385         }
386         return null;
387     }
388 
389     @Override
390     public String isProcessPlugins() {
391         checkInitializedState();
392         return TRUE.toString();
393     }
394 
395     @Override
396     public String getDefaultGlob() {
397         checkInitializedState();
398         return StringUtils.trim(cacheConfig.getInput().getGlobal().getGlob());
399     }
400 
401     @Nonnull
402     @Override
403     public List<Include> getGlobalIncludePaths() {
404         checkInitializedState();
405         return cacheConfig.getInput().getGlobal().getIncludes();
406     }
407 
408     @Nonnull
409     @Override
410     public List<Exclude> getGlobalExcludePaths() {
411         checkInitializedState();
412         return cacheConfig.getInput().getGlobal().getExcludes();
413     }
414 
415     @Nonnull
416     @Override
417     public HashFactory getHashFactory() {
418         checkInitializedState();
419         return hashFactory;
420     }
421 
422     @Override
423     public boolean canIgnore(MojoExecution mojoExecution) {
424         checkInitializedState();
425         if (cacheConfig.getExecutionControl() == null
426                 || cacheConfig.getExecutionControl().getIgnoreMissing() == null) {
427             return false;
428         }
429 
430         return executionMatches(mojoExecution, cacheConfig.getExecutionControl().getIgnoreMissing());
431     }
432 
433     @Override
434     public boolean isForcedExecution(MojoExecution execution) {
435         checkInitializedState();
436         if (cacheConfig.getExecutionControl() == null
437                 || cacheConfig.getExecutionControl().getRunAlways() == null) {
438             return false;
439         }
440 
441         return executionMatches(execution, cacheConfig.getExecutionControl().getRunAlways());
442     }
443 
444     private boolean executionMatches(MojoExecution execution, Executables executablesType) {
445         final List<PluginSet> pluginConfigs = executablesType.getPlugins();
446         for (PluginSet pluginConfig : pluginConfigs) {
447             if (isPluginMatch(execution.getPlugin(), pluginConfig)) {
448                 return true;
449             }
450         }
451 
452         final List<ExecutionIdsList> executionIds = executablesType.getExecutions();
453         for (ExecutionIdsList executionConfig : executionIds) {
454             if (isPluginMatch(execution.getPlugin(), executionConfig)
455                     && executionConfig.getExecIds().contains(execution.getExecutionId())) {
456                 return true;
457             }
458         }
459 
460         final List<GoalsList> pluginsGoalsList = executablesType.getGoalsLists();
461         for (GoalsList pluginGoals : pluginsGoalsList) {
462             if (isPluginMatch(execution.getPlugin(), pluginGoals)
463                     && pluginGoals.getGoals().contains(execution.getGoal())) {
464                 return true;
465             }
466         }
467 
468         return false;
469     }
470 
471     @Override
472     public boolean isEnabled() {
473         return state == CacheState.INITIALIZED;
474     }
475 
476     @Override
477     public boolean isRemoteCacheEnabled() {
478         checkInitializedState();
479         return getUrl() != null
480                 && getProperty(REMOTE_ENABLED_PROPERTY_NAME, getRemote().isEnabled());
481     }
482 
483     @Override
484     public boolean isSaveToRemote() {
485         return isRemoteCacheEnabled()
486                 && getProperty(SAVE_TO_REMOTE_PROPERTY_NAME, getRemote().isSaveToRemote());
487     }
488 
489     @Override
490     public boolean isSaveToRemoteFinal() {
491         return isSaveToRemote() && getProperty(SAVE_NON_OVERRIDEABLE_NAME, false);
492     }
493 
494     @Override
495     public boolean isSkipCache() {
496         return getProperty(CACHE_SKIP, false);
497     }
498 
499     @Override
500     public boolean isFailFast() {
501         return getProperty(FAIL_FAST_PROPERTY_NAME, false);
502     }
503 
504     @Override
505     public boolean isBaselineDiffEnabled() {
506         return getProperty(BASELINE_BUILD_URL_PROPERTY_NAME, null) != null;
507     }
508 
509     @Override
510     public String getBaselineCacheUrl() {
511         return getProperty(BASELINE_BUILD_URL_PROPERTY_NAME, null);
512     }
513 
514     @Override
515     public boolean isLazyRestore() {
516         return getProperty(LAZY_RESTORE_PROPERTY_NAME, false);
517     }
518 
519     @Override
520     public boolean isRestoreGeneratedSources() {
521         return getProperty(RESTORE_GENERATED_SOURCES_PROPERTY_NAME, true);
522     }
523 
524     @Override
525     public boolean isRestoreOnDiskArtifacts() {
526         return getProperty(RESTORE_ON_DISK_ARTIFACTS_PROPERTY_NAME, true);
527     }
528 
529     @Override
530     public String getAlwaysRunPlugins() {
531         return getProperty(ALWAYS_RUN_PLUGINS, null);
532     }
533 
534     @Override
535     public boolean isSkipSave() {
536         return getProperty(SKIP_SAVE, false);
537     }
538 
539     @Override
540     public boolean isMandatoryClean() {
541         return getProperty(MANDATORY_CLEAN, getConfiguration().isMandatoryClean());
542     }
543 
544     @Override
545     public String getId() {
546         checkInitializedState();
547         return getProperty(REMOTE_SERVER_ID_PROPERTY_NAME, getRemote().getId());
548     }
549 
550     @Override
551     public String getUrl() {
552         checkInitializedState();
553         return getProperty(REMOTE_URL_PROPERTY_NAME, getRemote().getUrl());
554     }
555 
556     @Override
557     public String getTransport() {
558         checkInitializedState();
559         return getRemote().getTransport();
560     }
561 
562     @Override
563     public int getMaxLocalBuildsCached() {
564         checkInitializedState();
565         return getLocal().getMaxBuildsCached();
566     }
567 
568     @Override
569     public String getLocalRepositoryLocation() {
570         checkInitializedState();
571         return getProperty(CACHE_LOCATION_PROPERTY_NAME, getLocal().getLocation());
572     }
573 
574     @Override
575     public List<DirName> getAttachedOutputs() {
576         checkInitializedState();
577         final AttachedOutputs attachedOutputs = getConfiguration().getAttachedOutputs();
578         return attachedOutputs == null ? Collections.emptyList() : attachedOutputs.getDirNames();
579     }
580 
581     @Override
582     public boolean isPreservePermissions() {
583         checkInitializedState();
584         final AttachedOutputs attachedOutputs = getConfiguration().getAttachedOutputs();
585         return attachedOutputs == null || attachedOutputs.isPreservePermissions();
586     }
587 
588     @Override
589     public boolean adjustMetaInfVersion() {
590         if (isEnabled()) {
591             return Optional.ofNullable(getConfiguration().getProjectVersioning())
592                     .map(ProjectVersioning::isAdjustMetaInf)
593                     .orElse(false);
594         } else {
595             return false;
596         }
597     }
598 
599     @Override
600     public boolean calculateProjectVersionChecksum() {
601         if (isEnabled()) {
602             return Optional.ofNullable(getConfiguration().getProjectVersioning())
603                     .map(ProjectVersioning::isCalculateProjectVersionChecksum)
604                     .orElse(false);
605         } else {
606             return false;
607         }
608     }
609 
610     @Nonnull
611     @Override
612     public List<Pattern> getExcludePatterns() {
613         checkInitializedState();
614         return excludePatterns;
615     }
616 
617     private List<Pattern> compileExcludePatterns() {
618         if (cacheConfig.getOutput() != null && cacheConfig.getOutput().getExclude() != null) {
619             List<Pattern> patterns = new ArrayList<>();
620             for (String pattern : cacheConfig.getOutput().getExclude().getPatterns()) {
621                 patterns.add(Pattern.compile(pattern));
622             }
623             return patterns;
624         }
625         return Collections.emptyList();
626     }
627 
628     private Remote getRemote() {
629         return getConfiguration().getRemote();
630     }
631 
632     private Local getLocal() {
633         return getConfiguration().getLocal();
634     }
635 
636     private Configuration getConfiguration() {
637         return cacheConfig.getConfiguration();
638     }
639 
640     private void checkInitializedState() {
641         if (state != CacheState.INITIALIZED) {
642             throw new IllegalStateException("Cache is not initialized. Actual state: " + state);
643         }
644     }
645 
646     private String getProperty(String key, String defaultValue) {
647         MavenSession session = providerSession.get();
648         String value = session.getUserProperties().getProperty(key);
649         if (value == null) {
650             value = session.getSystemProperties().getProperty(key);
651             if (value == null) {
652                 value = defaultValue;
653             }
654         }
655         return value;
656     }
657 
658     private boolean getProperty(String key, boolean defaultValue) {
659         MavenSession session = providerSession.get();
660         String value = session.getUserProperties().getProperty(key);
661         if (value == null) {
662             value = session.getSystemProperties().getProperty(key);
663             if (value == null) {
664                 return defaultValue;
665             }
666         }
667         return Boolean.parseBoolean(value);
668     }
669 }