@ -17,29 +17,50 @@
@@ -17,29 +17,50 @@
package org.springframework.boot.build.autoconfigure ;
import java.io.File ;
import java.io.IOException ;
import java.nio.file.Files ;
import java.nio.file.Path ;
import java.util.Collections ;
import java.util.List ;
import java.util.concurrent.Callable ;
import com.tngtech.archunit.core.domain.JavaClass ;
import com.tngtech.archunit.lang.ArchCondition ;
import com.tngtech.archunit.lang.ArchRule ;
import com.tngtech.archunit.lang.ConditionEvents ;
import com.tngtech.archunit.lang.SimpleConditionEvent ;
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition ;
import org.gradle.api.Plugin ;
import org.gradle.api.Project ;
import org.gradle.api.artifacts.Configuration ;
import org.gradle.api.plugins.JavaPlugin ;
import org.gradle.api.plugins.JavaPluginExtension ;
import org.gradle.api.provider.Provider ;
import org.gradle.api.tasks.PathSensitivity ;
import org.gradle.api.tasks.SourceSet ;
import org.springframework.boot.build.DeployedPlugin ;
import org.springframework.boot.build.architecture.ArchitectureCheck ;
import org.springframework.boot.build.architecture.ArchitecturePlugin ;
import org.springframework.boot.build.context.properties.ConfigurationPropertiesPlugin ;
/ * *
* { @link Plugin } for projects that define auto - configuration . When applied , the plugin
* applies the { @link DeployedPlugin } . Additionally , it reacts to the presence of the
* { @link JavaPlugin } by :
* applies the { @link DeployedPlugin } . Additionally , when the { @link JavaPlugin } is
* applied it :
*
* < ul >
* < li > Applying the { @link ConfigurationPropertiesPlugin } .
* < li > Adding a dependency on the auto - configuration annotation processor .
* < li > Defining a task that produces metadata describing the auto - configuration . The
* metadata is made available as an artifact in the
* < li > Applies the { @link ConfigurationPropertiesPlugin } .
* < li > Adds a dependency on the auto - configuration annotation processor .
* < li > Defines a task that produces metadata describing the auto - configuration . The
* metadata is made available as an artifact in the { @code autoConfigurationMetadata }
* configuration .
* < li > Reacts to the { @link ArchitecturePlugin } being applied and :
* < ul >
* < li > Adds a rule to the { @code checkArchitectureMain } task to verify that all
* { @code AutoConfiguration } classes are listed in the { @code AutoConfiguration . imports }
* file .
* < / ul >
* < / ul >
*
* @author Andy Wilkinson
@ -52,6 +73,8 @@ public class AutoConfigurationPlugin implements Plugin<Project> {
@@ -52,6 +73,8 @@ public class AutoConfigurationPlugin implements Plugin<Project> {
* /
public static final String AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME = "autoConfigurationMetadata" ;
private static final String AUTO_CONFIGURATION_IMPORTS_PATH = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports" ;
@Override
public void apply ( Project project ) {
project . getPlugins ( ) . apply ( DeployedPlugin . class ) ;
@ -80,7 +103,71 @@ public class AutoConfigurationPlugin implements Plugin<Project> {
@@ -80,7 +103,71 @@ public class AutoConfigurationPlugin implements Plugin<Project> {
project . provider ( ( Callable < File > ) task : : getOutputFile ) ,
( artifact ) - > artifact . builtBy ( task ) ) ;
} ) ;
project . getPlugins ( ) . withType ( ArchitecturePlugin . class , ( architecturePlugin ) - > {
project . getTasks ( ) . named ( "checkArchitectureMain" , ArchitectureCheck . class ) . configure ( ( task ) - > {
SourceSet main = project . getExtensions ( )
. getByType ( JavaPluginExtension . class )
. getSourceSets ( )
. getByName ( SourceSet . MAIN_SOURCE_SET_NAME ) ;
File resourcesDirectory = main . getOutput ( ) . getResourcesDir ( ) ;
task . dependsOn ( main . getProcessResourcesTaskName ( ) ) ;
task . getInputs ( ) . files ( resourcesDirectory ) . optional ( ) . withPathSensitivity ( PathSensitivity . RELATIVE ) ;
task . getRules ( )
. add ( allClassesAnnotatedWithAutoConfigurationShouldBeListedInAutoConfigurationImports (
autoConfigurationImports ( project , resourcesDirectory ) ) ) ;
} ) ;
} ) ;
} ) ;
}
private ArchRule allClassesAnnotatedWithAutoConfigurationShouldBeListedInAutoConfigurationImports (
Provider < AutoConfigurationImports > imports ) {
return ArchRuleDefinition . classes ( )
. that ( )
. areAnnotatedWith ( "org.springframework.boot.autoconfigure.AutoConfiguration" )
. should ( beListedInAutoConfigurationImports ( imports ) )
. allowEmptyShould ( true ) ;
}
private ArchCondition < JavaClass > beListedInAutoConfigurationImports ( Provider < AutoConfigurationImports > imports ) {
return new ArchCondition < JavaClass > ( "be listed in " + AUTO_CONFIGURATION_IMPORTS_PATH ) {
@Override
public void check ( JavaClass item , ConditionEvents events ) {
AutoConfigurationImports autoConfigurationImports = imports . get ( ) ;
if ( ! autoConfigurationImports . imports . contains ( item . getName ( ) ) ) {
events . add ( SimpleConditionEvent . violated ( item ,
item . getName ( ) + " was not listed in " + autoConfigurationImports . importsFile ) ) ;
}
}
} ;
}
private Provider < AutoConfigurationImports > autoConfigurationImports ( Project project , File resourcesDirectory ) {
Path importsFile = new File ( resourcesDirectory , AUTO_CONFIGURATION_IMPORTS_PATH ) . toPath ( ) ;
return project . provider ( ( ) - > {
try {
return new AutoConfigurationImports ( project . getProjectDir ( ) . toPath ( ) . relativize ( importsFile ) ,
Files . readAllLines ( importsFile ) ) ;
}
catch ( IOException ex ) {
throw new RuntimeException ( "Failed to read AutoConfiguration.imports" , ex ) ;
}
} ) ;
}
private static final class AutoConfigurationImports {
private final Path importsFile ;
private final List < String > imports ;
private AutoConfigurationImports ( Path importsFile , List < String > imports ) {
this . importsFile = importsFile ;
this . imports = imports ;
}
}
}