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