Skip to content

Commit

Permalink
Merge pull request #7 from evgeny-goldin/master
Browse files Browse the repository at this point in the history
Old properties removed, all <configuration>s are dumped with "-X", {A|B|C} expressions (no dollar needed + OR)
  • Loading branch information
Baruch Sadogursky committed Jul 1, 2013
2 parents 1a5427b + d96674e commit 1e2eb98
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 489 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.jfrog.build.extractor.maven.plugin

import static org.jfrog.build.extractor.maven.plugin.Utils.*
import org.apache.maven.AbstractMavenLifecycleParticipant
import org.apache.maven.execution.ExecutionEvent
import org.apache.maven.execution.MavenSession
Expand All @@ -14,23 +13,21 @@ import org.apache.maven.plugins.annotations.Mojo
import org.apache.maven.plugins.annotations.Parameter
import org.apache.maven.project.MavenProject
import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader
import org.gcontracts.annotations.Ensures
import org.codehaus.gmaven.mojo.GroovyMojo
import org.gcontracts.annotations.Requires
import org.jfrog.build.extractor.maven.BuildInfoRecorder
import org.jfrog.build.extractor.maven.BuildInfoRecorderLifecycleParticipant
import org.sonatype.aether.RepositorySystem
import org.sonatype.aether.impl.ArtifactDescriptorReader
import org.sonatype.aether.impl.internal.DefaultRepositorySystem
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.text.SimpleDateFormat


/**
* Artifactory plugin creating and deploying JSON build data together with build artifacts.
*/
@Mojo ( name = 'extract-build-info', defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true )
class ExtractorMojo extends ExtractorMojoProperties
class ExtractorMojo extends GroovyMojo
{
/**
* ---------------------------
Expand Down Expand Up @@ -77,6 +74,9 @@ class ExtractorMojo extends ExtractorMojoProperties
@Parameter
boolean pomPropertiesPriority = false

@Parameter
String deployProperties

/**
* ----------------
* Mojo parameters - property handlers
Expand Down Expand Up @@ -105,73 +105,29 @@ class ExtractorMojo extends ExtractorMojoProperties
Config.BlackDuck blackDuck = new Config.BlackDuck()


/**
* Mapping of mojo parameters of type {@link Config.DelegatesToPrefixPropertyHandler}:
* Key - parameter (field) name
* Value - parameter (field) value
*/
final Map<String, Config.DelegatesToPrefixPropertyHandler> prefixPropertyHandlers =
this.class.declaredFields.
findAll{ Field f -> Config.DelegatesToPrefixPropertyHandler.isAssignableFrom( f.type ) }.
inject( [:] ) { Map m, Field f -> m[ f.name ] = this."${ f.name }"; m }


@SuppressWarnings([ 'GroovyAccessibility' ])
@Requires({ descriptorReader && repoSystem && session })
@Requires({ descriptorReader && repoSystem && session && log })
@Override
void execute () throws MojoExecutionException , MojoFailureException
void execute ()
throws MojoExecutionException , MojoFailureException
{
boolean invokedAlready = (( descriptorReader.artifactResolver instanceof RepositoryResolver ) ||
( repoSystem.artifactResolver instanceof RepositoryResolver ) ||
( session.request.executionListener instanceof BuildInfoRecorder ))

final helper = new ExtractorMojoHelper( this )

if ( invokedAlready ){ return }
if ( log.debugEnabled ){ printHandlerSettings() }
if ( log.debugEnabled ){ helper.printConfigurations() }

skipDefaultDeploy()
updateConfiguration()
overrideResolutionRepository( new PropertiesHelper( this ).mergeProperties())
updateBuildInfo()
helper.createPropertiesFile()
overrideResolutionRepository()
recordBuildInfo()
}


/**
* Prints out all possible settings for each handler container.
*/
@Requires({ log.debugEnabled && prefixPropertyHandlers.values() })
private void printHandlerSettings ()
{
log.debug( "<configuration> handlers:\n" + ( [ artifactory : artifactory ] + prefixPropertyHandlers ).collect {
String handlerName, Object handler ->
"""
|<$handlerName>
| ${ handlerSettings( handler ).join( '\n ' )}
|</$handlerName>""".stripMargin().trim()
}.join( '\n' ))
}


/**
* Retrieves a list of all handler's settings.
*/
@Requires({ handler })
@Ensures ({ result })
private List<String> handlerSettings ( Object handler )
{
handler.class.methods.findAll { Method m -> ( m.parameterTypes.length == 1 ) &&
[ String, Boolean, boolean, Number ].any { it.isAssignableFrom( m.parameterTypes.first()) }}.
findAll { Method m -> m.name.startsWith( 'set' ) && ( m.name.length() > 3 ) }.
collect { Method m ->
final tagName = "${ m.name.charAt( 3 ).toLowerCase()}${ m.name.substring( 4 )}"
final tagContent = [ Boolean, boolean ].any{ it.isAssignableFrom( m.parameterTypes.first()) } ? 'true/false' :
Number.isAssignableFrom ( m.parameterTypes.first()) ? 'N' :
' .. '
"<$tagName>${ tagContent }</$tagName>"
}.
sort()
}


/**
* Cancels default "deploy" activity.
*/
Expand All @@ -182,29 +138,26 @@ class ExtractorMojo extends ExtractorMojoProperties


/**
* Updates various <configuration> values.
* Updates {@link #buildInfo} fields.
*/
@Requires({ project })
private void updateConfiguration ()
@Requires({ buildInfo && session && project })
private void updateBuildInfo ()
{
buildInfoBuildTimestamp = session.startTime.time.toString()
buildInfoBuildStarted = new SimpleDateFormat( 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZ' ).format( session.startTime ) // 2013-06-23T18\:38\:37.597+0200
artifactoryDeployBuildTimestamp = buildInfoBuildTimestamp
buildInfoBuildName = buildInfoBuildName ?: project.artifactId
artifactoryDeployBuildName = artifactoryDeployBuildName ?: buildInfoBuildName
buildInfo.buildTimestamp = session.startTime.time.toString()
buildInfo.buildStarted = new SimpleDateFormat( 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZ' ).format( session.startTime ) // 2013-06-23T18\:38\:37.597+0200
buildInfo.buildName = buildInfo.buildName ?: project.artifactId
}



/**
* Overrides resolution repository if a corresponding property is set.
*/
@SuppressWarnings([ 'GroovyAccessibility' ])
@Requires({ ( properties != null ) && descriptorReader.artifactResolver && repoSystem.artifactResolver })
private void overrideResolutionRepository ( Properties properties )
@Requires({ publisher && descriptorReader.artifactResolver && repoSystem.artifactResolver })
private void overrideResolutionRepository ()
{
final String artifactoryUrl = properties[ propertyName( 'artifactoryPublishContextUrl' ) ]
final String resolutionRepo = properties[ propertyName( 'artifactoryResolutionRepoKey' ) ]
final String artifactoryUrl = publisher.contextUrl
final String resolutionRepo = resolver.repoKey

if ( artifactoryUrl && resolutionRepo )
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package org.jfrog.build.extractor.maven.plugin

import org.jfrog.build.api.BuildInfoFields
import org.gcontracts.annotations.Ensures
import org.gcontracts.annotations.Requires
import org.jfrog.build.api.BuildInfoConfigProperties
import org.jfrog.build.client.ClientProperties
import java.lang.reflect.Field
import java.lang.reflect.Method


/**
* Helper merging all mojo properties.
*/
class ExtractorMojoHelper
{
@Delegate
private final ExtractorMojo mojo

private final Properties systemProperties

/**
* Mapping of mojo parameters of type {@link Config.DelegatesToPrefixPropertyHandler}: name => value.
*/
private final Map<String, Config.DelegatesToPrefixPropertyHandler> prefixPropertyHandlers

/**
* Mapping of types printed by {@link #printConfigurations()}: class => description.
*/
private final static Map<Class<?>, String> TYPE_DESCRIPTORS = [ ( Boolean ) : 'true/false',
( boolean ) : 'true/false',
( Number ) : 'N',
( File ) : 'path/to/file',
( String ) : ' .. ' ].asImmutable()


@SuppressWarnings([ 'GrFinalVariableAccess' ])
@Requires({ mojo })
@Ensures ({ this.mojo && ( this.systemProperties != null ) && prefixPropertyHandlers })
ExtractorMojoHelper ( ExtractorMojo mojo )
{
this.mojo = mojo
final systemProperty = System.getProperty( BuildInfoConfigProperties.PROP_PROPS_FILE )
this.systemProperties = readProperties( systemProperty ? new File( systemProperty ) : '' )
prefixPropertyHandlers = (( Map ) mojo.class.declaredFields.
findAll{ Field f -> Config.DelegatesToPrefixPropertyHandler.isAssignableFrom( f.type ) }.
inject( [:] ) { Map m, Field f -> m[ f.name ] = mojo."${ f.name }"; m }).
asImmutable()
}


/**
* Prints out all possible Mojo <configuration> settings.
*/
@Requires({ log.debugEnabled && artifactory && prefixPropertyHandlers })
void printConfigurations ()
{
final Map<String, Object> objectsMap = [ '' : this, artifactory : artifactory ] + prefixPropertyHandlers
final List<String> lines = [ 'Possible <configuration> values:' ] + objectsMap.collect {
String objectName, Object object ->
objectName ? [ "<$objectName>", objectConfigurations( object ).collect { " $it" }, "</$objectName>" ] :
objectConfigurations( object )
}.flatten()

log.debug( lines.join( '\n' ))
}


/**
* Retrieves a list of all object settings.
*/
@Requires({ object != null })
@Ensures ({ result })
private List<String> objectConfigurations ( Object object )
{
object.class.methods.findAll { Method m -> ( m.name.length() > 3 ) &&
( m.name.startsWith( 'set' )) &&
( m.parameterTypes.length == 1 ) &&
TYPE_DESCRIPTORS.keySet().any { it.isAssignableFrom( m.parameterTypes.first()) }}.
collect { Method m ->
final tag = "${ m.name.charAt( 3 ).toLowerCase()}${ m.name.substring( 4 )}"
"<$tag>${ TYPE_DESCRIPTORS[ m.parameterTypes.first()] }</$tag>"
}.
sort()
}


/**
* Merges *.properties files with <configuration> values and writes a new *.properties file to be picked up later
* by the original Maven listener.
*/
@Ensures ({ result })
Properties createPropertiesFile ()
{
final mergedProperties = mergeProperties()
assert mergedProperties

final propertiesFile = writeProperties ? new File( mojo.project.basedir, 'buildInfo.properties' ) :
File.createTempFile( 'buildInfo', '.properties' )

mergedProperties[ BuildInfoConfigProperties.PROP_PROPS_FILE ] = propertiesFile.canonicalPath
propertiesFile.withWriter { Writer w -> mergedProperties.store( w, 'Build Info Properties' )}
if ( ! writeProperties ){ propertiesFile.deleteOnExit() }

log.info( "Merged properties file:${ propertiesFile.canonicalPath } created" )
System.setProperty( BuildInfoConfigProperties.PROP_PROPS_FILE, propertiesFile.canonicalPath )

mergedProperties
}


/**
* Merges system-provided properties with POM properties and class fields.
*/
@SuppressWarnings([ 'GroovyAccessibility' ])
@Requires({ prefixPropertyHandlers && artifactory.delegate.root.props && buildInfo.buildTimestamp && buildInfo.buildName })
@Ensures ({ result != null })
private Properties mergeProperties ()
{
assert prefixPropertyHandlers.values().each { assert it.delegate.props.is( artifactory.delegate.root.props ) }

final mergedProperties = new Properties()
final deployPropertiesPrefix = ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX
final deployProperties = (( Properties ) readProperties( deployProperties ).collectEntries {
String key, String value -> [ "${ deployPropertiesPrefix }${ key }", value ]
}) + [ ( "${ deployPropertiesPrefix }${ BuildInfoFields.BUILD_TIMESTAMP }".toString()) : buildInfo.buildTimestamp,
( "${ deployPropertiesPrefix }${ BuildInfoFields.BUILD_NAME }".toString()) : buildInfo.buildName ]

addProperties(( Map<String, String> ) readProperties( mojo.propertiesFile ) + readProperties( mojo.properties ),
mergedProperties )

addProperties( artifactory.delegate.root.props,
mergedProperties )

addProperties(( Map<String, String> ) deployProperties,
mergedProperties )

( Properties ) mergedProperties.collectEntries { String key, String value -> [ key, updateValue( value ) ]}
}


/**
* Adds all {@code propertiesFrom} to {@code propertiesTo} considering {@link #systemProperties}.
*/
@SuppressWarnings([ 'GroovyAssignmentToMethodParameter' ])
@Requires({ ( propertiesFrom != null ) && ( propertiesTo != null ) && ( systemProperties != null ) })
private void addProperties ( Map<String, String> propertiesFrom, Properties propertiesTo )
{
propertiesFrom.each {
String propertyName, String propertyValue ->
propertyName = propertyName.toString() // Possible GString => String
propertiesTo[ propertyName ] = ( systemProperties[ propertyName ] && ( ! pomPropertiesPriority )) ?
systemProperties[ propertyName ] :
propertyValue
}
}


/**
* Reads {@link Properties} from the {@link File} specified.
*/
@Ensures ({ result != null })
private Properties readProperties ( File propertiesFile )
{
assert (( ! propertiesFile ) || propertiesFile.file ), "Properties file [$propertiesFile.canonicalPath] is not available"
readProperties( propertiesFile?.file ? propertiesFile.getText( 'UTF-8' ) : '' )
}


/**
* Reads {@link Properties} from the {@link String} specified.
*/
@Ensures ({ result != null })
private Properties readProperties ( String propertiesContent )
{
final p = new Properties()
if ( propertiesContent ){ p.load( new StringReader( propertiesContent )) }
p
}


/**
* Updates all "${var}" entries in the value specified to their corresponding environment variables or system properties.
*/
@Requires({ value })
@Ensures ({ result })
private String updateValue( String value )
{
value?.replaceAll( /(\$?\{)([^}]+)(\})/ ){
final expressionValue = (( String ) it[ 2 ] ).tokenize( '|' ).collect { System.getenv( it ) ?: System.getProperty( it )}.grep()[ 0 ]
expressionValue ?: "${ it[ 1 ] }${ it[ 2 ] }${ it[ 3 ] }"
}
}
}
Loading

0 comments on commit 1e2eb98

Please sign in to comment.