blob: a3b8f3c087d0aa743b76884b694770493af9ffb5 [file] [log] [blame]
package org.apache.maven.plugins.invoker;
/*
* 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.
*/
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.apache.maven.shared.invoker.InvocationRequest.ReactorFailureBehavior;
import org.codehaus.plexus.util.StringUtils;
/**
* Provides a convenient facade around the <code>invoker.properties</code>.
*
* @author Benjamin Bentmann
*/
class InvokerProperties
{
private static final String SELECTOR_PREFIX = "selector.";
private enum InvocationProperty
{
PROJECT( "invoker.project" ),
GOALS( "invoker.goals" ),
PROFILES( "invoker.profiles" ),
MAVEN_OPTS( "invoker.mavenOpts" ),
FAILURE_BEHAVIOR( "invoker.failureBehavior" ),
NON_RECURSIVE( "invoker.nonRecursive" ),
OFFLINE( "invoker.offline" ),
SYSTEM_PROPERTIES_FILE( "invoker.systemPropertiesFile" ),
DEBUG( "invoker.debug" ),
SETTINGS_FILE ( "invoker.settingsFile" ),
TIMEOUT_IN_SECONDS ( "invoker.timeoutInSeconds" ),
ORDINAL ( "invoker.ordinal" );
private final String key;
InvocationProperty( final String s )
{
this.key = s;
}
@Override
public String toString()
{
return key;
}
}
private enum SelectorProperty
{
JAVA_VERSION( ".java.version" ),
MAVEN_VERSION( ".maven.version" ),
OS_FAMLY( ".os.family" );
private final String suffix;
SelectorProperty( String suffix )
{
this.suffix = suffix;
}
@Override
public String toString()
{
return suffix;
}
}
/**
* The invoker properties being wrapped.
*/
private final Properties properties;
/**
* Creates a new facade for the specified invoker properties. The properties will not be copied, so any changes to
* them will be reflected by the facade.
*
* @param properties The invoker properties to wrap, may be <code>null</code> if none.
*/
InvokerProperties( Properties properties )
{
this.properties = ( properties != null ) ? properties : new Properties();
}
/**
* Gets the invoker properties being wrapped.
*
* @return The invoker properties being wrapped, never <code>null</code>.
*/
public Properties getProperties()
{
return this.properties;
}
/**
* Gets the name of the corresponding build job.
*
* @return The name of the build job or an empty string if not set.
*/
public String getJobName()
{
return this.properties.getProperty( "invoker.name", "" );
}
/**
* Gets the description of the corresponding build job.
*
* @return The description of the build job or an empty string if not set.
*/
public String getJobDescription()
{
return this.properties.getProperty( "invoker.description", "" );
}
/**
* Get the corresponding ordinal value
* @return The ordinal value
*/
public int getOrdinal()
{
return Integer.parseInt( this.properties.getProperty( "invoker.ordinal", "0" ) );
}
/**
* Gets the specification of JRE versions on which this build job should be run.
*
* @return The specification of JRE versions or an empty string if not set.
*/
public String getJreVersion()
{
return this.properties.getProperty( "invoker.java.version", "" );
}
/**
* Gets the specification of JRE versions on which this build job should be run.
*
* @return The specification of JRE versions or an empty string if not set.
*/
public String getJreVersion( int index )
{
return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.JAVA_VERSION.suffix,
getJreVersion() );
}
/**
* Gets the specification of Maven versions on which this build job should be run.
*
* @return The specification of Maven versions on which this build job should be run.
* @since 1.5
*/
public String getMavenVersion()
{
return this.properties.getProperty( "invoker.maven.version", "" );
}
/**
*
* @param index the selector index
* @return The specification of Maven versions on which this build job should be run.
* @since 3.0.0
*/
public String getMavenVersion( int index )
{
return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.MAVEN_VERSION.suffix,
getMavenVersion() );
}
/**
* Gets the specification of OS families on which this build job should be run.
*
* @return The specification of OS families or an empty string if not set.
*/
public String getOsFamily()
{
return this.properties.getProperty( "invoker.os.family", "" );
}
/**
* Gets the specification of OS families on which this build job should be run.
*
* @param index the selector index
* @return The specification of OS families or an empty string if not set.
* @since 3.0.0
*/
public String getOsFamily( int index )
{
return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.OS_FAMLY.suffix,
getOsFamily() );
}
public Collection<InvokerToolchain> getToolchains()
{
return getToolchains( Pattern.compile( "invoker\\.toolchain\\.([^\\.]+)\\.(.+)" ) );
}
public Collection<InvokerToolchain> getToolchains( int index )
{
return getToolchains( Pattern.compile( "selector\\." + index + "\\.invoker\\.toolchain\\.([^\\.]+)\\.(.+)" ) );
}
private Collection<InvokerToolchain> getToolchains( Pattern p )
{
Map<String, InvokerToolchain> toolchains = new HashMap<>();
for ( Map.Entry<Object, Object> entry : this.properties.entrySet() )
{
Matcher m = p.matcher( entry.getKey().toString() );
if ( m.matches() )
{
String type = m.group( 1 );
String providesKey = m.group( 2 );
String providesValue = entry.getValue().toString();
InvokerToolchain tc = toolchains.get( type );
if ( tc == null )
{
tc = new InvokerToolchain( type );
toolchains.put( type, tc );
}
tc.addProvides( providesKey, providesValue );
}
}
return toolchains.values();
}
/**
* Determines whether these invoker properties contain a build definition for the specified invocation index.
*
* @param index The one-based index of the invocation to check for, must not be negative.
* @return <code>true</code> if the invocation with the specified index is defined, <code>false</code> otherwise.
*/
public boolean isInvocationDefined( int index )
{
for ( InvocationProperty prop : InvocationProperty.values() )
{
if ( properties.getProperty( prop.toString() + '.' + index ) != null )
{
return true;
}
}
return false;
}
/**
* Determines whether these invoker properties contain a build definition for the specified selector index.
*
* @param index the index
* @return <code>true</code> if the selector with the specified index is defined, <code>false</code> otherwise.
* @since 3.0.0
*/
public boolean isSelectorDefined( int index )
{
for ( SelectorProperty prop : SelectorProperty.values() )
{
if ( properties.getProperty( SELECTOR_PREFIX + index + prop.suffix ) != null )
{
return true;
}
}
return false;
}
/**
* Configures the specified invocation request from these invoker properties. Settings not present in the invoker
* properties will be left unchanged in the invocation request.
*
* @param request The invocation request to configure, must not be <code>null</code>.
* @param index The one-based index of the invocation to configure, must not be negative.
*/
public void configureInvocation( InvocationRequest request, int index )
{
String project = get( InvocationProperty.PROJECT, index );
if ( project != null )
{
File file = new File( request.getBaseDirectory(), project );
if ( file.isFile() )
{
request.setBaseDirectory( file.getParentFile() );
request.setPomFile( file );
}
else
{
request.setBaseDirectory( file );
request.setPomFile( null );
}
}
String goals = get( InvocationProperty.GOALS, index );
if ( goals != null )
{
request.setGoals( new ArrayList<>( Arrays.asList( StringUtils.split( goals, ", \t\n\r\f" ) ) ) );
}
String profiles = get( InvocationProperty.PROFILES, index );
if ( profiles != null )
{
// CHECKSTYLE_OFF: LineLength
request.setProfiles( new ArrayList<>( Arrays.asList( StringUtils.split( profiles,
", \t\n\r\f" ) ) ) );
// CHECKSTYLE_ON: LineLength
}
String mvnOpts = get( InvocationProperty.MAVEN_OPTS, index );
if ( mvnOpts != null )
{
request.setMavenOpts( mvnOpts );
}
String failureBehavior = get( InvocationProperty.FAILURE_BEHAVIOR, index );
if ( failureBehavior != null )
{
ReactorFailureBehavior valueOf =
InvocationRequest.ReactorFailureBehavior.valueOfByLongOption( failureBehavior );
request.setReactorFailureBehavior( valueOf );
}
String nonRecursive = get( InvocationProperty.NON_RECURSIVE, index );
if ( nonRecursive != null )
{
request.setRecursive( !Boolean.valueOf( nonRecursive ) );
}
String offline = get( InvocationProperty.OFFLINE, index );
if ( offline != null )
{
request.setOffline( Boolean.valueOf( offline ) );
}
String debug = get( InvocationProperty.DEBUG, index );
if ( debug != null )
{
request.setDebug( Boolean.valueOf( debug ) );
}
}
/**
* Checks whether the specified exit code matches the one expected for the given invocation.
*
* @param exitCode The exit code of the Maven invocation to check.
* @param index The index of the invocation for which to check the exit code, must not be negative.
* @return <code>true</code> if the exit code is zero and a success was expected or if the exit code is non-zero and
* a failue was expected, <code>false</code> otherwise.
*/
public boolean isExpectedResult( int exitCode, int index )
{
boolean nonZeroExit = "failure".equalsIgnoreCase( get( "invoker.buildResult", index ) );
return ( exitCode != 0 ) == nonZeroExit;
}
/**
* Gets the path to the properties file used to set the system properties for the specified invocation.
*
* @param index The index of the invocation, must not be negative.
* @return The path to the properties file or <code>null</code> if not set.
*/
public String getSystemPropertiesFile( int index )
{
return get( InvocationProperty.SYSTEM_PROPERTIES_FILE, index );
}
/**
* Gets the settings file used for the specified invocation.
*
* @param index The index of the invocation, must not be negative.
* @return the value for the settings file or <code>null</code> if not set.
*/
public String getSettingsFile( int index )
{
return get( InvocationProperty.SETTINGS_FILE, index );
}
/**
* Get timeout to execute the project
* @param index index The index of the invocation, must not be negative.
* @return the value for the timeout or -1
*/
public int getTimeoutInSeconds( int index )
{
String timeoutInSecondsStr = get( InvocationProperty.TIMEOUT_IN_SECONDS, index );
if ( StringUtils.isEmpty( timeoutInSecondsStr ) )
{
return -1;
}
// catch NumberFormatException? well we assume users knows what they do :-)
return Integer.parseInt( timeoutInSecondsStr );
}
/**
* Gets a value from the invoker properties. The invoker properties are intended to describe the invocation settings
* for multiple builds of the same project. For this reason, the properties are indexed. First, a property named
* <code>key.index</code> will be queried. If this property does not exist, the value of the property named
* <code>key</code> will finally be returned.
*
* @param key The (base) key for the invoker property to lookup, must not be <code>null</code>.
* @param index The index of the invocation for which to retrieve the value, must not be negative.
* @return The value for the requested invoker property or <code>null</code> if not defined.
*/
String get( String key, int index )
{
if ( index < 0 )
{
throw new IllegalArgumentException( "invalid invocation index: " + index );
}
String value = properties.getProperty( key + '.' + index );
if ( value == null )
{
value = properties.getProperty( key );
}
return value;
}
private String get( InvocationProperty prop, int index )
{
return get( prop.toString(), index );
}
}