blob: dae58d587433dc85bee83804df3c6b6c315975e4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.buildcache.xml;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.SessionScoped;
import org.apache.maven.buildcache.DefaultPluginScanConfig;
import org.apache.maven.buildcache.PluginScanConfig;
import org.apache.maven.buildcache.PluginScanConfigImpl;
import org.apache.maven.buildcache.hash.HashFactory;
import org.apache.maven.buildcache.xml.config.AttachedOutputs;
import org.apache.maven.buildcache.xml.config.CacheConfig;
import org.apache.maven.buildcache.xml.config.Configuration;
import org.apache.maven.buildcache.xml.config.CoordinatesBase;
import org.apache.maven.buildcache.xml.config.Exclude;
import org.apache.maven.buildcache.xml.config.Executables;
import org.apache.maven.buildcache.xml.config.ExecutionConfigurationScan;
import org.apache.maven.buildcache.xml.config.ExecutionControl;
import org.apache.maven.buildcache.xml.config.ExecutionIdsList;
import org.apache.maven.buildcache.xml.config.GoalReconciliation;
import org.apache.maven.buildcache.xml.config.GoalsList;
import org.apache.maven.buildcache.xml.config.Include;
import org.apache.maven.buildcache.xml.config.Input;
import org.apache.maven.buildcache.xml.config.Local;
import org.apache.maven.buildcache.xml.config.MultiModule;
import org.apache.maven.buildcache.xml.config.PathSet;
import org.apache.maven.buildcache.xml.config.PluginConfigurationScan;
import org.apache.maven.buildcache.xml.config.PluginSet;
import org.apache.maven.buildcache.xml.config.ProjectVersioning;
import org.apache.maven.buildcache.xml.config.PropertyName;
import org.apache.maven.buildcache.xml.config.Remote;
import org.apache.maven.buildcache.xml.config.TrackedProperty;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.MojoExecution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.lang.Boolean.TRUE;
import static org.apache.maven.buildcache.CacheUtils.getMultimoduleRoot;
/**
* CacheConfigImpl
*/
@SessionScoped
@Named
@SuppressWarnings( "unused" )
public class CacheConfigImpl implements org.apache.maven.buildcache.xml.CacheConfig
{
public static final String CONFIG_PATH_PROPERTY_NAME = "maven.build.cache.configPath";
public static final String CACHE_ENABLED_PROPERTY_NAME = "maven.build.cache.enabled";
public static final String SAVE_TO_REMOTE_PROPERTY_NAME = "maven.build.cache.remote.save.enabled";
public static final String SAVE_NON_OVERRIDEABLE_NAME = "maven.build.cache.remote.save.final";
public static final String FAIL_FAST_PROPERTY_NAME = "maven.build.cache.failFast";
public static final String BASELINE_BUILD_URL_PROPERTY_NAME = "maven.build.cache.baselineUrl";
public static final String LAZY_RESTORE_PROPERTY_NAME = "maven.build.cache.lazyRestore";
public static final String RESTORE_GENERATED_SOURCES_PROPERTY_NAME = "maven.build.cache.restoreGeneratedSources";
public static final String ALWAYS_RUN_PLUGINS = "maven.build.cache.alwaysRunPlugins";
private static final Logger LOGGER = LoggerFactory.getLogger( CacheConfigImpl.class );
private final XmlService xmlService;
private final MavenSession session;
private CacheState state;
private CacheConfig cacheConfig;
private HashFactory hashFactory;
private List<Pattern> excludePatterns;
@Inject
public CacheConfigImpl( XmlService xmlService, MavenSession session )
{
this.xmlService = xmlService;
this.session = session;
}
@Nonnull
@Override
public CacheState initialize()
{
if ( state == null )
{
final String enabled = getProperty( CACHE_ENABLED_PROPERTY_NAME, "true" );
if ( !Boolean.parseBoolean( enabled ) )
{
LOGGER.info( "Cache disabled by command line flag, project will be built fully and not cached" );
state = CacheState.DISABLED;
}
else
{
Path configPath;
String configPathText = getProperty( CONFIG_PATH_PROPERTY_NAME, null );
if ( StringUtils.isNotBlank( configPathText ) )
{
configPath = Paths.get( configPathText );
}
else
{
configPath = getMultimoduleRoot( session ).resolve( ".mvn" )
.resolve( "maven-build-cache-config.xml" );
}
if ( !Files.exists( configPath ) )
{
LOGGER.info( "Cache configuration is not available at configured path {}, "
+ "cache is enabled with defaults", configPath );
cacheConfig = new CacheConfig();
}
else
{
try
{
LOGGER.info( "Loading cache configuration from {}", configPath );
cacheConfig = xmlService.loadCacheConfig( configPath.toFile() );
fillWithDefaults( cacheConfig );
}
catch ( Exception e )
{
throw new IllegalArgumentException(
"Cannot initialize cache because xml config is not valid or not available", e );
}
}
fillWithDefaults( cacheConfig );
if ( !cacheConfig.getConfiguration().isEnabled() )
{
state = CacheState.DISABLED;
}
else
{
String hashAlgorithm = null;
try
{
hashAlgorithm = getConfiguration().getHashAlgorithm();
hashFactory = HashFactory.of( hashAlgorithm );
LOGGER.info( "Using {} hash algorithm for cache", hashAlgorithm );
}
catch ( Exception e )
{
throw new IllegalArgumentException( "Unsupported hashing algorithm: " + hashAlgorithm, e );
}
excludePatterns = compileExcludePatterns();
state = CacheState.INITIALIZED;
}
}
}
return state;
}
private void fillWithDefaults( CacheConfig cacheConfig )
{
if ( cacheConfig.getConfiguration() == null )
{
cacheConfig.setConfiguration( new Configuration() );
}
Configuration configuration = cacheConfig.getConfiguration();
if ( configuration.getLocal() == null )
{
configuration.setLocal( new Local() );
}
if ( configuration.getRemote() == null )
{
configuration.setRemote( new Remote() );
}
if ( cacheConfig.getInput() == null )
{
cacheConfig.setInput( new Input() );
}
Input input = cacheConfig.getInput();
if ( input.getGlobal() == null )
{
input.setGlobal( new PathSet() );
}
}
@Nonnull
@Override
public List<TrackedProperty> getTrackedProperties( MojoExecution mojoExecution )
{
checkInitializedState();
final GoalReconciliation reconciliationConfig = findReconciliationConfig( mojoExecution );
if ( reconciliationConfig != null )
{
return reconciliationConfig.getReconciles();
}
else
{
return Collections.emptyList();
}
}
@Override
public boolean isLogAllProperties( MojoExecution mojoExecution )
{
final GoalReconciliation reconciliationConfig = findReconciliationConfig( mojoExecution );
if ( reconciliationConfig != null && reconciliationConfig.isLogAll() )
{
return true;
}
return cacheConfig.getExecutionControl() != null && cacheConfig.getExecutionControl().getReconcile() != null
&& cacheConfig.getExecutionControl().getReconcile().isLogAllProperties();
}
private GoalReconciliation findReconciliationConfig( MojoExecution mojoExecution )
{
if ( cacheConfig.getExecutionControl() == null )
{
return null;
}
final ExecutionControl executionControl = cacheConfig.getExecutionControl();
if ( executionControl.getReconcile() == null )
{
return null;
}
final List<GoalReconciliation> reconciliation = executionControl.getReconcile().getPlugins();
for ( GoalReconciliation goalReconciliationConfig : reconciliation )
{
final String goal = mojoExecution.getGoal();
if ( isPluginMatch( mojoExecution.getPlugin(), goalReconciliationConfig ) && StringUtils.equals( goal,
goalReconciliationConfig.getGoal() ) )
{
return goalReconciliationConfig;
}
}
return null;
}
@Nonnull
@Override
public List<PropertyName> getLoggedProperties( MojoExecution mojoExecution )
{
checkInitializedState();
final GoalReconciliation reconciliationConfig = findReconciliationConfig( mojoExecution );
if ( reconciliationConfig != null )
{
return reconciliationConfig.getLogs();
}
else
{
return Collections.emptyList();
}
}
@Nonnull
@Override
public List<PropertyName> getNologProperties( MojoExecution mojoExecution )
{
checkInitializedState();
final GoalReconciliation reconciliationConfig = findReconciliationConfig( mojoExecution );
if ( reconciliationConfig != null )
{
return reconciliationConfig.getNologs();
}
else
{
return Collections.emptyList();
}
}
@Nonnull
@Override
public List<String> getEffectivePomExcludeProperties( Plugin plugin )
{
checkInitializedState();
final PluginConfigurationScan pluginConfig = findPluginScanConfig( plugin );
if ( pluginConfig != null && pluginConfig.getEffectivePom() != null )
{
return pluginConfig.getEffectivePom().getExcludeProperties();
}
return Collections.emptyList();
}
@Nullable
@Override
public MultiModule getMultiModule()
{
checkInitializedState();
return cacheConfig.getConfiguration().getMultiModule();
}
private PluginConfigurationScan findPluginScanConfig( Plugin plugin )
{
if ( cacheConfig.getInput() == null )
{
return null;
}
final List<PluginConfigurationScan> pluginConfigs = cacheConfig.getInput().getPlugins();
for ( PluginConfigurationScan pluginConfig : pluginConfigs )
{
if ( isPluginMatch( plugin, pluginConfig ) )
{
return pluginConfig;
}
}
return null;
}
private boolean isPluginMatch( Plugin plugin, CoordinatesBase pluginConfig )
{
return StringUtils.equals( pluginConfig.getArtifactId(),
plugin.getArtifactId() )
&& ( pluginConfig.getGroupId() == null || StringUtils.equals(
pluginConfig.getGroupId(), plugin.getGroupId() ) );
}
@Nonnull
@Override
public PluginScanConfig getPluginDirScanConfig( Plugin plugin )
{
checkInitializedState();
final PluginConfigurationScan pluginConfig = findPluginScanConfig( plugin );
if ( pluginConfig == null || pluginConfig.getDirScan() == null )
{
return new DefaultPluginScanConfig();
}
return new PluginScanConfigImpl( pluginConfig.getDirScan() );
}
@Nonnull
@Override
public PluginScanConfig getExecutionDirScanConfig( Plugin plugin, PluginExecution exec )
{
checkInitializedState();
final PluginConfigurationScan pluginScanConfig = findPluginScanConfig( plugin );
if ( pluginScanConfig != null )
{
final ExecutionConfigurationScan executionScanConfig = findExecutionScanConfig( exec,
pluginScanConfig.getExecutions() );
if ( executionScanConfig != null && executionScanConfig.getDirScan() != null )
{
return new PluginScanConfigImpl( executionScanConfig.getDirScan() );
}
}
return new DefaultPluginScanConfig();
}
private ExecutionConfigurationScan findExecutionScanConfig( PluginExecution execution,
List<ExecutionConfigurationScan> scanConfigs )
{
for ( ExecutionConfigurationScan executionScanConfig : scanConfigs )
{
if ( executionScanConfig.getExecIds().contains( execution.getId() ) )
{
return executionScanConfig;
}
}
return null;
}
@Override
public String isProcessPlugins()
{
checkInitializedState();
return TRUE.toString();
}
@Override
public String getDefaultGlob()
{
checkInitializedState();
return StringUtils.trim( cacheConfig.getInput().getGlobal().getGlob() );
}
@Nonnull
@Override
public List<Include> getGlobalIncludePaths()
{
checkInitializedState();
return cacheConfig.getInput().getGlobal().getIncludes();
}
@Nonnull
@Override
public List<Exclude> getGlobalExcludePaths()
{
checkInitializedState();
return cacheConfig.getInput().getGlobal().getExcludes();
}
@Nonnull
@Override
public HashFactory getHashFactory()
{
checkInitializedState();
return hashFactory;
}
@Override
public boolean canIgnore( MojoExecution mojoExecution )
{
checkInitializedState();
if ( cacheConfig.getExecutionControl() == null || cacheConfig.getExecutionControl().getIgnoreMissing() == null )
{
return false;
}
return executionMatches( mojoExecution, cacheConfig.getExecutionControl().getIgnoreMissing() );
}
@Override
public boolean isForcedExecution( MojoExecution execution )
{
checkInitializedState();
if ( cacheConfig.getExecutionControl() == null || cacheConfig.getExecutionControl().getRunAlways() == null )
{
return false;
}
return executionMatches( execution, cacheConfig.getExecutionControl().getRunAlways() );
}
private boolean executionMatches( MojoExecution execution, Executables executablesType )
{
final List<PluginSet> pluginConfigs = executablesType.getPlugins();
for ( PluginSet pluginConfig : pluginConfigs )
{
if ( isPluginMatch( execution.getPlugin(), pluginConfig ) )
{
return true;
}
}
final List<ExecutionIdsList> executionIds = executablesType.getExecutions();
for ( ExecutionIdsList executionConfig : executionIds )
{
if ( isPluginMatch( execution.getPlugin(), executionConfig ) && executionConfig.getExecIds().contains(
execution.getExecutionId() ) )
{
return true;
}
}
final List<GoalsList> pluginsGoalsList = executablesType.getGoalsLists();
for ( GoalsList pluginGoals : pluginsGoalsList )
{
if ( isPluginMatch( execution.getPlugin(), pluginGoals ) && pluginGoals.getGoals().contains(
execution.getGoal() ) )
{
return true;
}
}
return false;
}
@Override
public boolean isEnabled()
{
return state == CacheState.INITIALIZED;
}
@Override
public boolean isRemoteCacheEnabled()
{
checkInitializedState();
return getRemote().getUrl() != null && getRemote().isEnabled();
}
@Override
public boolean isSaveToRemote()
{
checkInitializedState();
return Boolean.getBoolean( SAVE_TO_REMOTE_PROPERTY_NAME ) || getRemote().isSaveToRemote();
}
@Override
public boolean isSaveFinal()
{
return Boolean.getBoolean( SAVE_NON_OVERRIDEABLE_NAME );
}
@Override
public boolean isFailFast()
{
return Boolean.getBoolean( FAIL_FAST_PROPERTY_NAME );
}
@Override
public boolean isBaselineDiffEnabled()
{
return getProperty( BASELINE_BUILD_URL_PROPERTY_NAME, null ) != null;
}
@Override
public String getBaselineCacheUrl()
{
return getProperty( BASELINE_BUILD_URL_PROPERTY_NAME, null );
}
@Override
public boolean isLazyRestore()
{
final String lazyRestore = getProperty( LAZY_RESTORE_PROPERTY_NAME, "false" );
return Boolean.parseBoolean( lazyRestore );
}
@Override
public boolean isRestoreGeneratedSources()
{
final String restoreGeneratedSources = getProperty( RESTORE_GENERATED_SOURCES_PROPERTY_NAME, "true" );
return Boolean.parseBoolean( restoreGeneratedSources );
}
@Override
public String getAlwaysRunPlugins()
{
return getProperty( ALWAYS_RUN_PLUGINS, null );
}
@Override
public String getId()
{
checkInitializedState();
return getRemote().getId();
}
@Override
public String getUrl()
{
checkInitializedState();
return getRemote().getUrl();
}
@Override
public String getTransport()
{
checkInitializedState();
return getRemote().getTransport();
}
@Override
public int getMaxLocalBuildsCached()
{
checkInitializedState();
return getLocal().getMaxBuildsCached();
}
@Override
public List<String> getAttachedOutputs()
{
checkInitializedState();
final AttachedOutputs attachedOutputs = getConfiguration().getAttachedOutputs();
return attachedOutputs == null ? Collections.emptyList() : attachedOutputs.getDirNames();
}
@Override
public boolean adjustMetaInfVersion()
{
return Optional.ofNullable( getConfiguration().getProjectVersioning() )
.map( ProjectVersioning::isAdjustMetaInf )
.orElse( false );
}
@Nonnull
@Override
public List<Pattern> getExcludePatterns()
{
checkInitializedState();
return excludePatterns;
}
private List<Pattern> compileExcludePatterns()
{
if ( cacheConfig.getOutput() != null && cacheConfig.getOutput().getExclude() != null )
{
List<Pattern> patterns = new ArrayList<>();
for ( String pattern : cacheConfig.getOutput().getExclude().getPatterns() )
{
patterns.add( Pattern.compile( pattern ) );
}
return patterns;
}
return Collections.emptyList();
}
private Remote getRemote()
{
return getConfiguration().getRemote();
}
private Local getLocal()
{
return getConfiguration().getLocal();
}
private Configuration getConfiguration()
{
return cacheConfig.getConfiguration();
}
private void checkInitializedState()
{
if ( state != CacheState.INITIALIZED )
{
throw new IllegalStateException( "Cache is not initialized. Actual state: " + state );
}
}
private String getProperty( String key, String defaultValue )
{
String value = session.getUserProperties().getProperty( key );
if ( value == null )
{
value = session.getSystemProperties().getProperty( key );
if ( value == null )
{
value = defaultValue;
}
}
return value;
}
}