Relocate projects to spring-boot-project
Move projects to better reflect the way that Spring Boot is released. The following projects are under `spring-boot-project`: - `spring-boot` - `spring-boot-autoconfigure` - `spring-boot-tools` - `spring-boot-starters` - `spring-boot-actuator` - `spring-boot-actuator-autoconfigure` - `spring-boot-test` - `spring-boot-test-autoconfigure` - `spring-boot-devtools` - `spring-boot-cli` - `spring-boot-docs` See gh-9316
This commit is contained in:
33
spring-boot-project/spring-boot-tools/pom.xml
Normal file
33
spring-boot-project/spring-boot-tools/pom.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-parent</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../spring-boot-parent</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-boot-tools</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Boot Tools</name>
|
||||
<description>Spring Boot Tools</description>
|
||||
<url>http://projects.spring.io/spring-boot/</url>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
</organization>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
</properties>
|
||||
<modules>
|
||||
<module>spring-boot-antlib</module>
|
||||
<module>spring-boot-autoconfigure-processor</module>
|
||||
<module>spring-boot-configuration-metadata</module>
|
||||
<module>spring-boot-configuration-processor</module>
|
||||
<module>spring-boot-gradle-plugin</module>
|
||||
<module>spring-boot-loader</module>
|
||||
<module>spring-boot-loader-tools</module>
|
||||
<module>spring-boot-maven-plugin</module>
|
||||
<module>spring-boot-test-support</module>
|
||||
</modules>
|
||||
</project>
|
||||
149
spring-boot-project/spring-boot-tools/spring-boot-antlib/pom.xml
Normal file
149
spring-boot-project/spring-boot-tools/spring-boot-antlib/pom.xml
Normal file
@@ -0,0 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-tools</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-boot-antlib</artifactId>
|
||||
<name>Spring Boot Antlib</name>
|
||||
<description>Spring Boot Antlib</description>
|
||||
<url>http://projects.spring.io/spring-boot/</url>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
</organization>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
<ant.version>1.9.3</ant.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-loader</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-loader-tools</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant</artifactId>
|
||||
<version>${ant.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
<include>org.springframework.boot:spring-boot-loader-tools</include>
|
||||
<include>org.springframework:spring-core</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade-runtime-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>antunit</id>
|
||||
<phase>integration-test</phase>
|
||||
<configuration>
|
||||
<target>
|
||||
<mkdir dir="${project.build.directory}/it" />
|
||||
<copy todir="${project.build.directory}/it">
|
||||
<fileset dir="${project.basedir}/src/it" />
|
||||
</copy>
|
||||
<path id="taskpath">
|
||||
<fileset dir="${project.build.directory}" includes="${project.build.finalName}.${project.packaging}" />
|
||||
</path>
|
||||
<pathconvert refid="taskpath" />
|
||||
<typedef resource="org/springframework/boot/ant/antlib.xml" classpathref="taskpath" uri="antlib:org.springframework.boot.ant" />
|
||||
<property name="build.compiler" value="${antBuildCompiler}" />
|
||||
<antunit xmlns="antlib:org.apache.ant.antunit">
|
||||
<propertyset>
|
||||
<propertyref name="build.compiler" />
|
||||
</propertyset>
|
||||
<plainlistener />
|
||||
<fileset xmlns="antlib:org.apache.tools.ant" dir="${project.build.directory}/it" includes="**/build.xml" />
|
||||
</antunit>
|
||||
</target>
|
||||
<skip>${skipTests}</skip>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant</artifactId>
|
||||
<version>${ant.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant-launcher</artifactId>
|
||||
<version>${ant.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant-antunit</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.ivy</groupId>
|
||||
<artifactId>ivy</artifactId>
|
||||
<version>2.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jdt.core.compiler</groupId>
|
||||
<artifactId>ecj</artifactId>
|
||||
<version>4.6.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>java-8</id>
|
||||
<activation>
|
||||
<jdk>[1.8,1.9)</jdk>
|
||||
</activation>
|
||||
<properties>
|
||||
<antBuildCompiler>org.eclipse.jdt.core.JDTCompilerAdapter</antBuildCompiler>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>java-9</id>
|
||||
<activation>
|
||||
<jdk>[1.9,)</jdk>
|
||||
</activation>
|
||||
<properties>
|
||||
<antBuildCompiler>modern</antBuildCompiler>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0"?>
|
||||
<project name="spring-boot-antlib-test" default="antunit" xmlns:au="antlib:org.apache.ant.antunit" xmlns:spring-boot="antlib:org.springframework.boot.ant" xmlns:ivy="antlib:org.apache.ivy.ant">
|
||||
|
||||
<property name="src.dir" location="src/main/java" />
|
||||
<property name="resource.dir" location="src/main/resources" />
|
||||
<property name="target.dir" location="target" />
|
||||
<property name="classes.dir" location="${target.dir}/classes" />
|
||||
|
||||
<target name="setUp">
|
||||
<ivy:cachepath inline="true" pathid="compile.classpath" organisation="joda-time" module="joda-time" type="jar" revision="2.8.1" />
|
||||
|
||||
<mkdir dir="${classes.dir}" />
|
||||
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="compile.classpath" />
|
||||
</target>
|
||||
|
||||
<target name="tearDown">
|
||||
<delete dir="${classes.dir}" />
|
||||
</target>
|
||||
|
||||
<target name="test-findmainclass-set-property" depends="setUp">
|
||||
<spring-boot:findmainclass classesroot="${classes.dir}" property="main-class" />
|
||||
<au:assertEquals expected="org.test.SampleApplication" actual="${main-class}" />
|
||||
</target>
|
||||
|
||||
<target name="test-findmainclass-log-message" depends="setUp">
|
||||
<spring-boot:findmainclass classesroot="${classes.dir}" />
|
||||
<au:assertLogContains text="org.test.SampleApplication" />
|
||||
</target>
|
||||
|
||||
<target name="test-findmainclass-override" depends="setUp">
|
||||
<spring-boot:findmainclass mainclass="OVERRIDDEN" property="main-class" />
|
||||
<au:assertEquals expected="OVERRIDDEN" actual="${main-class}" />
|
||||
</target>
|
||||
|
||||
<macrodef name="check-exejar">
|
||||
<attribute name="jar" />
|
||||
<sequential>
|
||||
<echo>Checking @{jar}</echo>
|
||||
<au:assertFileExists file="@{jar}" />
|
||||
|
||||
<javaresource id="foo" name="foo">
|
||||
<classpath>
|
||||
<pathelement location="@{jar}" />
|
||||
</classpath>
|
||||
</javaresource>
|
||||
<au:assertRefResourceExists refid="foo" />
|
||||
<au:assertRefResourceContains refid="foo" value="FOO" />
|
||||
|
||||
<java jar="@{jar}" fork="true" />
|
||||
<au:assertLogContains text="LocalDate" />
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<target name="test-exejar-explicit-start-class" depends="setUp">
|
||||
<local name="jar" />
|
||||
<property name="jar" location="${target.dir}/explicit.jar" />
|
||||
<spring-boot:exejar destfile="${jar}" classes="${classes.dir}" start-class="org.test.SampleApplication">
|
||||
<resources>
|
||||
<fileset dir="${resource.dir}" />
|
||||
</resources>
|
||||
<lib>
|
||||
<path refid="compile.classpath" />
|
||||
</lib>
|
||||
</spring-boot:exejar>
|
||||
<check-exejar jar="${jar}" />
|
||||
</target>
|
||||
|
||||
<target name="test-exejar-find-start-class" depends="setUp">
|
||||
<local name="jar" />
|
||||
<property name="jar" location="${target.dir}/found.jar" />
|
||||
<spring-boot:exejar destfile="${jar}" classes="${classes.dir}">
|
||||
<resources>
|
||||
<fileset dir="${resource.dir}" />
|
||||
</resources>
|
||||
<lib>
|
||||
<path refid="compile.classpath" />
|
||||
</lib>
|
||||
</spring-boot:exejar>
|
||||
<check-exejar jar="${jar}" />
|
||||
</target>
|
||||
|
||||
<target name="antunit">
|
||||
<au:antunit>
|
||||
<au:plainlistener />
|
||||
<file file="${ant.file}" />
|
||||
</au:antunit>
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="${target.dir}" />
|
||||
</target>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
<ivysettings>
|
||||
<settings defaultResolver="chain" />
|
||||
<resolvers>
|
||||
<chain name="chain">
|
||||
<!-- NOTE: You should declare only repositories that you need here -->
|
||||
<filesystem name="local" local="true" m2compatible="true">
|
||||
<artifact pattern="${user.home}/.m2/[organisation]/[module]/[revision]/[module]-[revision].[ext]" />
|
||||
<ivy pattern="${user.home}/.m2/[organisation]/[module]/[revision]/[module]-[revision].pom" />
|
||||
</filesystem>
|
||||
<ibiblio name="ibiblio" m2compatible="true" />
|
||||
<ibiblio name="spring-milestones" m2compatible="true" root="http://repo.spring.io/release" />
|
||||
<ibiblio name="spring-milestones" m2compatible="true" root="http://repo.spring.io/milestone" />
|
||||
<ibiblio name="spring-snapshots" m2compatible="true" root="http://repo.spring.io/snapshot" />
|
||||
</chain>
|
||||
</resolvers>
|
||||
</ivysettings>
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.test;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class SampleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(LocalDate.class.getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
FOO
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.ant;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
import org.springframework.boot.loader.tools.MainClassFinder;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Ant task to find a main class.
|
||||
*
|
||||
* @author Matt Benson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class FindMainClass extends Task {
|
||||
|
||||
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
|
||||
|
||||
private String mainClass;
|
||||
|
||||
private File classesRoot;
|
||||
|
||||
private String property;
|
||||
|
||||
public FindMainClass(Project project) {
|
||||
setProject(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws BuildException {
|
||||
String mainClass = this.mainClass;
|
||||
if (!StringUtils.hasText(mainClass)) {
|
||||
mainClass = findMainClass();
|
||||
if (!StringUtils.hasText(mainClass)) {
|
||||
throw new BuildException(
|
||||
"Could not determine main class given @classesRoot "
|
||||
+ this.classesRoot);
|
||||
}
|
||||
}
|
||||
handle(mainClass);
|
||||
}
|
||||
|
||||
private String findMainClass() {
|
||||
if (this.classesRoot == null) {
|
||||
throw new BuildException(
|
||||
"one of @mainClass or @classesRoot must be specified");
|
||||
}
|
||||
if (!this.classesRoot.exists()) {
|
||||
throw new BuildException(
|
||||
"@classesRoot " + this.classesRoot + " does not exist");
|
||||
}
|
||||
try {
|
||||
if (this.classesRoot.isDirectory()) {
|
||||
return MainClassFinder.findSingleMainClass(this.classesRoot,
|
||||
SPRING_BOOT_APPLICATION_CLASS_NAME);
|
||||
}
|
||||
return MainClassFinder.findSingleMainClass(new JarFile(this.classesRoot), "/",
|
||||
SPRING_BOOT_APPLICATION_CLASS_NAME);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new BuildException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(String mainClass) {
|
||||
if (StringUtils.hasText(this.property)) {
|
||||
getProject().setProperty(this.property, mainClass);
|
||||
}
|
||||
else {
|
||||
log("Found main class " + mainClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the main class, which will cause the search to be bypassed.
|
||||
* @param mainClass the main class name
|
||||
*/
|
||||
public void setMainClass(String mainClass) {
|
||||
this.mainClass = mainClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the root location of classes to be searched.
|
||||
* @param classesRoot the root location
|
||||
*/
|
||||
public void setClassesRoot(File classesRoot) {
|
||||
this.classesRoot = classesRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ANT property to set (if left unset, result will be printed to the log).
|
||||
* @param property the ANT property to set
|
||||
*/
|
||||
public void setProperty(String property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.ant;
|
||||
|
||||
import org.apache.tools.ant.BuildException;
|
||||
import org.apache.tools.ant.Project;
|
||||
import org.apache.tools.ant.Task;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Quiet task that establishes a reference to its loader.
|
||||
*
|
||||
* @author Matt Benson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class ShareAntlibLoader extends Task {
|
||||
|
||||
private String refid;
|
||||
|
||||
public ShareAntlibLoader(Project project) {
|
||||
super();
|
||||
setProject(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws BuildException {
|
||||
if (!StringUtils.hasText(this.refid)) {
|
||||
throw new BuildException("@refid has no text");
|
||||
}
|
||||
getProject().addReference(this.refid, getClass().getClassLoader());
|
||||
}
|
||||
|
||||
public void setRefid(String refid) {
|
||||
this.refid = refid;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0"?>
|
||||
<antlib>
|
||||
|
||||
<taskdef name="findmainclass" classname="org.springframework.boot.ant.FindMainClass" />
|
||||
|
||||
<!-- unadvertised taskdef to help pull antlib resources from within the antlib.xml file -->
|
||||
<taskdef name="spring-boot-antlib-share-antlib-loader" classname="org.springframework.boot.ant.ShareAntlibLoader" />
|
||||
|
||||
<macrodef name="exejar" description="Create a spring-boot executable jar">
|
||||
<attribute name="destfile" />
|
||||
<attribute name="classes" />
|
||||
<attribute name="start-class" default="" />
|
||||
|
||||
<element name="resources" optional="true"
|
||||
description="includes resource collections specifying additional Java resources" />
|
||||
|
||||
<element name="lib" optional="true"
|
||||
description="includes resource collections containing (jar) dependencies" />
|
||||
|
||||
<sequential>
|
||||
<local name="start-class" />
|
||||
|
||||
<findmainclass
|
||||
xmlns="antlib:org.springframework.boot.ant"
|
||||
property="start-class"
|
||||
mainclass="@{start-class}"
|
||||
classesroot="@{classes}" />
|
||||
|
||||
<echo>Using start class ${start-class}</echo>
|
||||
|
||||
<spring-boot-antlib-share-antlib-loader
|
||||
xmlns="antlib:org.springframework.boot.ant"
|
||||
refid="spring.boot.antlib.loader" />
|
||||
|
||||
<local name="spring-boot.version" />
|
||||
|
||||
<loadproperties prefix="spring-boot">
|
||||
<javaresource
|
||||
name="META-INF/maven/org.springframework.boot/spring-boot-antlib/pom.properties"
|
||||
loaderref="spring.boot.antlib.loader" />
|
||||
<filterchain>
|
||||
<linecontainsregexp>
|
||||
<regexp pattern="^version=" />
|
||||
</linecontainsregexp>
|
||||
</filterchain>
|
||||
</loadproperties>
|
||||
|
||||
<local name="destdir" />
|
||||
<dirname file="@{destfile}" property="destdir" />
|
||||
|
||||
<echo>Using destination directory ${destdir}</echo>
|
||||
<mkdir dir="${destdir}/dependency" />
|
||||
|
||||
<echo>Extracting spring-boot-loader to ${destdir}/dependency</echo>
|
||||
<copy todir="${destdir}/dependency">
|
||||
<javaresource name="META-INF/loader/spring-boot-loader.jar"
|
||||
loaderref="spring.boot.antlib.loader" />
|
||||
<flattenmapper />
|
||||
</copy>
|
||||
|
||||
<echo>Embedding spring-boot-loader v${spring-boot.version}...</echo>
|
||||
<jar destfile="@{destfile}" compress="false">
|
||||
<mappedresources>
|
||||
<fileset dir="@{classes}"/>
|
||||
<globmapper from="*" to="BOOT-INF/classes/*" />
|
||||
</mappedresources>
|
||||
<resources />
|
||||
<mappedresources>
|
||||
<lib />
|
||||
<globmapper from="*" to="BOOT-INF/lib/*" />
|
||||
</mappedresources>
|
||||
<zipfileset src="${destdir}/dependency/spring-boot-loader.jar" />
|
||||
<manifest>
|
||||
<attribute name="Main-Class"
|
||||
value="org.springframework.boot.loader.JarLauncher" />
|
||||
<attribute name="Start-Class" value="${start-class}" />
|
||||
<attribute name="Spring-Boot-Classes" value="BOOT-INF/classes/" />
|
||||
<attribute name="Spring-Boot-Lib" value="BOOT-INF/lib/" />
|
||||
</manifest>
|
||||
</jar>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</antlib>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-tools</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<name>Spring Boot Auto-Configure Annotation Processor</name>
|
||||
<description>Spring Auto-Configure Annotation Processor</description>
|
||||
<url>http://projects.spring.io/spring-boot/</url>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
</organization>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- Ensure own annotation processor doesn't kick in -->
|
||||
<proc>none</proc>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
/**
|
||||
* Annotation processor to store certain annotations from auto-configuration classes in a
|
||||
* property file.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SupportedAnnotationTypes({ "org.springframework.context.annotation.Configuration",
|
||||
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass",
|
||||
"org.springframework.boot.autoconfigure.AutoConfigureBefore",
|
||||
"org.springframework.boot.autoconfigure.AutoConfigureAfter",
|
||||
"org.springframework.boot.autoconfigure.AutoConfigureOrder" })
|
||||
public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
|
||||
|
||||
protected static final String PROPERTIES_PATH = "META-INF/"
|
||||
+ "spring-autoconfigure-metadata.properties";
|
||||
|
||||
private Map<String, String> annotations;
|
||||
|
||||
private final Properties properties = new Properties();
|
||||
|
||||
public AutoConfigureAnnotationProcessor() {
|
||||
Map<String, String> annotations = new LinkedHashMap<>();
|
||||
addAnnotations(annotations);
|
||||
this.annotations = Collections.unmodifiableMap(annotations);
|
||||
}
|
||||
|
||||
protected void addAnnotations(Map<String, String> annotations) {
|
||||
annotations.put("Configuration",
|
||||
"org.springframework.context.annotation.Configuration");
|
||||
annotations.put("ConditionalOnClass",
|
||||
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass");
|
||||
annotations.put("AutoConfigureBefore",
|
||||
"org.springframework.boot.autoconfigure.AutoConfigureBefore");
|
||||
annotations.put("AutoConfigureAfter",
|
||||
"org.springframework.boot.autoconfigure.AutoConfigureAfter");
|
||||
annotations.put("AutoConfigureOrder",
|
||||
"org.springframework.boot.autoconfigure.AutoConfigureOrder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations,
|
||||
RoundEnvironment roundEnv) {
|
||||
for (Map.Entry<String, String> entry : this.annotations.entrySet()) {
|
||||
process(roundEnv, entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (roundEnv.processingOver()) {
|
||||
try {
|
||||
writeProperties();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to write metadata", ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void process(RoundEnvironment roundEnv, String propertyKey,
|
||||
String annotationName) {
|
||||
TypeElement annotationType = this.processingEnv.getElementUtils()
|
||||
.getTypeElement(annotationName);
|
||||
if (annotationType != null) {
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) {
|
||||
Element enclosingElement = element.getEnclosingElement();
|
||||
if (enclosingElement != null
|
||||
&& enclosingElement.getKind() == ElementKind.PACKAGE) {
|
||||
processElement(element, propertyKey, annotationName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processElement(Element element, String propertyKey,
|
||||
String annotationName) {
|
||||
try {
|
||||
String qualifiedName = getQualifiedName(element);
|
||||
AnnotationMirror annotation = getAnnotation(element, annotationName);
|
||||
if (qualifiedName != null && annotation != null) {
|
||||
List<Object> values = getValues(annotation);
|
||||
this.properties.put(qualifiedName + "." + propertyKey,
|
||||
toCommaDelimitedString(values));
|
||||
this.properties.put(qualifiedName, "");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Error processing configuration meta-data on " + element, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationMirror getAnnotation(Element element, String type) {
|
||||
if (element != null) {
|
||||
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
|
||||
if (type.equals(annotation.getAnnotationType().toString())) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String toCommaDelimitedString(List<Object> list) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (Object item : list) {
|
||||
result.append(result.length() != 0 ? "," : "");
|
||||
result.append(item);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Object> getValues(AnnotationMirror annotation) {
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotation
|
||||
.getElementValues().entrySet()) {
|
||||
String attributeName = entry.getKey().getSimpleName().toString();
|
||||
if ("name".equals(attributeName) || "value".equals(attributeName)) {
|
||||
Object value = entry.getValue().getValue();
|
||||
if (value instanceof List) {
|
||||
for (AnnotationValue annotationValue : (List<AnnotationValue>) value) {
|
||||
result.add(annotationValue.getValue());
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getQualifiedName(Element element) {
|
||||
if (element != null) {
|
||||
TypeElement enclosingElement = getEnclosingTypeElement(element.asType());
|
||||
if (enclosingElement != null) {
|
||||
return getQualifiedName(enclosingElement) + "$"
|
||||
+ ((DeclaredType) element.asType()).asElement().getSimpleName()
|
||||
.toString();
|
||||
}
|
||||
if (element instanceof TypeElement) {
|
||||
return ((TypeElement) element).getQualifiedName().toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private TypeElement getEnclosingTypeElement(TypeMirror type) {
|
||||
if (type instanceof DeclaredType) {
|
||||
DeclaredType declaredType = (DeclaredType) type;
|
||||
Element enclosingElement = declaredType.asElement().getEnclosingElement();
|
||||
if (enclosingElement != null && enclosingElement instanceof TypeElement) {
|
||||
return (TypeElement) enclosingElement;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void writeProperties() throws IOException {
|
||||
if (!this.properties.isEmpty()) {
|
||||
FileObject file = this.processingEnv.getFiler()
|
||||
.createResource(StandardLocation.CLASS_OUTPUT, "", PROPERTIES_PATH);
|
||||
try (OutputStream outputStream = file.openOutputStream()) {
|
||||
this.properties.store(outputStream, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigureprocessor.AutoConfigureAnnotationProcessor
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import org.springframework.boot.testsupport.compiler.TestCompiler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AutoConfigureAnnotationProcessor}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class AutoConfigureAnnotationProcessorTests {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
private TestCompiler compiler;
|
||||
|
||||
@Before
|
||||
public void createCompiler() throws IOException {
|
||||
this.compiler = new TestCompiler(this.temporaryFolder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotatedClass() throws Exception {
|
||||
Properties properties = compile(TestClassConfiguration.class);
|
||||
System.out.println(properties);
|
||||
assertThat(properties).hasSize(3);
|
||||
assertThat(properties).containsEntry(
|
||||
"org.springframework.boot.autoconfigureprocessor."
|
||||
+ "TestClassConfiguration.ConditionalOnClass",
|
||||
"java.io.InputStream,java.io.OutputStream");
|
||||
assertThat(properties).containsKey(
|
||||
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration");
|
||||
assertThat(properties).containsKey(
|
||||
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.Configuration");
|
||||
assertThat(properties).doesNotContainKey(
|
||||
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration$Nested");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotatedMethod() throws Exception {
|
||||
Properties properties = compile(TestMethodConfiguration.class);
|
||||
List<String> matching = new ArrayList<>();
|
||||
for (Object key : properties.keySet()) {
|
||||
if (key.toString().startsWith(
|
||||
"org.springframework.boot.autoconfigureprocessor.TestMethodConfiguration")) {
|
||||
matching.add(key.toString());
|
||||
}
|
||||
}
|
||||
assertThat(matching).hasSize(2)
|
||||
.contains("org.springframework.boot.autoconfigureprocessor."
|
||||
+ "TestMethodConfiguration")
|
||||
.contains("org.springframework.boot.autoconfigureprocessor."
|
||||
+ "TestMethodConfiguration.Configuration");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void annotatedClassWithOrder() throws Exception {
|
||||
Properties properties = compile(TestOrderedClassConfiguration.class);
|
||||
assertThat(properties).containsEntry(
|
||||
"org.springframework.boot.autoconfigureprocessor."
|
||||
+ "TestOrderedClassConfiguration.ConditionalOnClass",
|
||||
"java.io.InputStream,java.io.OutputStream");
|
||||
assertThat(properties).containsEntry(
|
||||
"org.springframework.boot.autoconfigureprocessor."
|
||||
+ "TestOrderedClassConfiguration.AutoConfigureBefore",
|
||||
"test.before1,test.before2");
|
||||
assertThat(properties).containsEntry(
|
||||
"org.springframework.boot.autoconfigureprocessor."
|
||||
+ "TestOrderedClassConfiguration.AutoConfigureAfter",
|
||||
"java.io.ObjectInputStream");
|
||||
assertThat(properties)
|
||||
.containsEntry(
|
||||
"org.springframework.boot.autoconfigureprocessor."
|
||||
+ "TestOrderedClassConfiguration.AutoConfigureOrder",
|
||||
"123");
|
||||
}
|
||||
|
||||
private Properties compile(Class<?>... types) throws IOException {
|
||||
TestConditionMetadataAnnotationProcessor processor = new TestConditionMetadataAnnotationProcessor(
|
||||
this.compiler.getOutputLocation());
|
||||
this.compiler.getTask(types).call(processor);
|
||||
return processor.getWrittenProperties();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Alternative to Spring Boot's {@code @AutoConfigureAfter} for testing (removes the need
|
||||
* for a dependency on the real annotation).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE })
|
||||
public @interface TestAutoConfigureAfter {
|
||||
|
||||
Class<?>[] value() default {};
|
||||
|
||||
String[] name() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Alternative to Spring Boot's {@code @AutoConfigureBefore} for testing (removes the need
|
||||
* for a dependency on the real annotation).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE })
|
||||
public @interface TestAutoConfigureBefore {
|
||||
|
||||
Class<?>[] value() default {};
|
||||
|
||||
String[] name() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Alternative to Spring Boot's {@code @AutoConfigureOrder} for testing (removes the need
|
||||
* for a dependency on the real annotation).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
|
||||
public @interface TestAutoConfigureOrder {
|
||||
|
||||
int value() default Integer.MAX_VALUE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Test configuration with an annotated class.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@TestConfiguration
|
||||
@TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class)
|
||||
public class TestClassConfiguration {
|
||||
|
||||
@TestAutoConfigureOrder
|
||||
public static class Nested {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
|
||||
/**
|
||||
* Version of {@link AutoConfigureAnnotationProcessor} used for testing.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@SupportedAnnotationTypes({
|
||||
"org.springframework.boot.autoconfigureprocessor.TestConfiguration",
|
||||
"org.springframework.boot.autoconfigureprocessor.TestConditionalOnClass",
|
||||
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureBefore",
|
||||
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureAfter",
|
||||
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureOrder" })
|
||||
public class TestConditionMetadataAnnotationProcessor
|
||||
extends AutoConfigureAnnotationProcessor {
|
||||
|
||||
private final File outputLocation;
|
||||
|
||||
public TestConditionMetadataAnnotationProcessor(File outputLocation) {
|
||||
this.outputLocation = outputLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAnnotations(Map<String, String> annotations) {
|
||||
put(annotations, "Configuration", TestConfiguration.class);
|
||||
put(annotations, "ConditionalOnClass", TestConditionalOnClass.class);
|
||||
put(annotations, "AutoConfigureBefore", TestAutoConfigureBefore.class);
|
||||
put(annotations, "AutoConfigureAfter", TestAutoConfigureAfter.class);
|
||||
put(annotations, "AutoConfigureOrder", TestAutoConfigureOrder.class);
|
||||
}
|
||||
|
||||
private void put(Map<String, String> annotations, String key, Class<?> value) {
|
||||
annotations.put(key, value.getName());
|
||||
}
|
||||
|
||||
public Properties getWrittenProperties() throws IOException {
|
||||
File file = new File(this.outputLocation, PROPERTIES_PATH);
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
try (FileInputStream inputStream = new FileInputStream(file)) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(inputStream);
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Fake {@code @ConditionalOnClass} annotation used for testing.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface TestConditionalOnClass {
|
||||
|
||||
Class<?>[] value() default {};
|
||||
|
||||
String[] name() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Alternative to Spring's {@code @Configuration} for testing (removes the need for a
|
||||
* dependency on the real annotation).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface TestConfiguration {
|
||||
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Test configuration with an annotated method.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@TestConfiguration
|
||||
public class TestMethodConfiguration {
|
||||
|
||||
@TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class)
|
||||
public Object method() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.autoconfigureprocessor;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Test configuration with an annotated class.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@TestAutoConfigureBefore(name = { "test.before1", "test.before2" })
|
||||
@TestAutoConfigureAfter(ObjectInputStream.class)
|
||||
@TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class)
|
||||
@TestAutoConfigureOrder(123)
|
||||
public class TestOrderedClassConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-tools</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-boot-configuration-metadata</artifactId>
|
||||
<name>Spring Boot Configuration Metadata</name>
|
||||
<description>Spring Boot Configuration Metadata</description>
|
||||
<url>http://projects.spring.io/spring-boot/</url>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
</organization>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- Compile (should stick to the bare minimum) -->
|
||||
<dependency>
|
||||
<groupId>com.vaadin.external.google</groupId>
|
||||
<artifactId>android-json</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Gather a collection of {@link ConfigurationMetadataProperty properties} that are
|
||||
* sharing a {@link #getId() common prefix}. Provide access to all the
|
||||
* {@link ConfigurationMetadataSource sources} that have contributed properties to the
|
||||
* group.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ConfigurationMetadataGroup implements Serializable {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final Map<String, ConfigurationMetadataSource> sources = new HashMap<>();
|
||||
|
||||
private final Map<String, ConfigurationMetadataProperty> properties = new HashMap<>();
|
||||
|
||||
public ConfigurationMetadataGroup(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id of the group, used as a common prefix for all properties associated
|
||||
* to it.
|
||||
* @return the id of the group
|
||||
*/
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ConfigurationMetadataSource sources} defining the properties of
|
||||
* this group.
|
||||
* @return the sources of the group
|
||||
*/
|
||||
public Map<String, ConfigurationMetadataSource> getSources() {
|
||||
return this.sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link ConfigurationMetadataProperty properties} defined in this group.
|
||||
* <p>
|
||||
* A property may appear more than once for a given source, potentially with
|
||||
* conflicting type or documentation. This is a "merged" view of the properties of
|
||||
* this group.
|
||||
* @return the properties of the group
|
||||
* @see ConfigurationMetadataSource#getProperties()
|
||||
*/
|
||||
public Map<String, ConfigurationMetadataProperty> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A raw view of a hint used for parsing only.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
class ConfigurationMetadataHint {
|
||||
|
||||
private static final String KEY_SUFFIX = ".keys";
|
||||
|
||||
private static final String VALUE_SUFFIX = ".values";
|
||||
|
||||
private String id;
|
||||
|
||||
private final List<ValueHint> valueHints = new ArrayList<>();
|
||||
|
||||
private final List<ValueProvider> valueProviders = new ArrayList<>();
|
||||
|
||||
public boolean isMapKeyHints() {
|
||||
return (this.id != null && this.id.endsWith(KEY_SUFFIX));
|
||||
}
|
||||
|
||||
public boolean isMapValueHints() {
|
||||
return (this.id != null && this.id.endsWith(VALUE_SUFFIX));
|
||||
}
|
||||
|
||||
public String resolveId() {
|
||||
if (isMapKeyHints()) {
|
||||
return this.id.substring(0, this.id.length() - KEY_SUFFIX.length());
|
||||
}
|
||||
if (isMapValueHints()) {
|
||||
return this.id.substring(0, this.id.length() - VALUE_SUFFIX.length());
|
||||
}
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<ValueHint> getValueHints() {
|
||||
return this.valueHints;
|
||||
}
|
||||
|
||||
public List<ValueProvider> getValueProviders() {
|
||||
return this.valueProviders;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
/**
|
||||
* An extension of {@link ConfigurationMetadataProperty} that provides a reference to its
|
||||
* source.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
class ConfigurationMetadataItem extends ConfigurationMetadataProperty {
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private String sourceMethod;
|
||||
|
||||
/**
|
||||
* The class name of the source that contributed this property. For example, if the
|
||||
* property was from a class annotated with {@code @ConfigurationProperties} this
|
||||
* attribute would contain the fully qualified name of that class.
|
||||
* @return the source type
|
||||
*/
|
||||
public String getSourceType() {
|
||||
return this.sourceType;
|
||||
}
|
||||
|
||||
public void setSourceType(String sourceType) {
|
||||
this.sourceType = sourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The full name of the method (including parenthesis and argument types) that
|
||||
* contributed this property. For example, the name of a getter in a
|
||||
* {@code @ConfigurationProperties} annotated class.
|
||||
* @return the source method
|
||||
*/
|
||||
public String getSourceMethod() {
|
||||
return this.sourceMethod;
|
||||
}
|
||||
|
||||
public void setSourceMethod(String sourceMethod) {
|
||||
this.sourceMethod = sourceMethod;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Define a configuration property. Each property is fully identified by its
|
||||
* {@link #getId() id} which is composed of a namespace prefix (the
|
||||
* {@link ConfigurationMetadataGroup#getId() group id}), if any and the {@link #getName()
|
||||
* name} of the property.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ConfigurationMetadataProperty implements Serializable {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String type;
|
||||
|
||||
private String description;
|
||||
|
||||
private String shortDescription;
|
||||
|
||||
private Object defaultValue;
|
||||
|
||||
private final Hints hints = new Hints();
|
||||
|
||||
private Deprecation deprecation;
|
||||
|
||||
/**
|
||||
* The full identifier of the property, in lowercase dashed form (e.g.
|
||||
* my.group.simple-property)
|
||||
* @return the property id
|
||||
*/
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the property, in lowercase dashed form (e.g. simple-property). If this
|
||||
* item does not belong to any group, the id is returned.
|
||||
* @return the property name
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The class name of the data type of the property. For example,
|
||||
* {@code java.lang.String}.
|
||||
* <p>
|
||||
* For consistency, the type of a primitive is specified using its wrapper
|
||||
* counterpart, i.e. {@code boolean} becomes {@code java.lang.Boolean}. If the type
|
||||
* holds generic information, these are provided as well, i.e. a {@code HashMap} of
|
||||
* String to Integer would be defined as {@code java.util.HashMap
|
||||
* <java.lang.String,java.lang.Integer>}.
|
||||
* <p>
|
||||
* Note that this class may be a complex type that gets converted from a String as
|
||||
* values are bound.
|
||||
* @return the property type
|
||||
*/
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* A description of the property, if any. Can be multi-lines.
|
||||
* @return the property description
|
||||
* @see #getShortDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single-line, single-sentence description of this property, if any.
|
||||
* @return the property short description
|
||||
* @see #getDescription()
|
||||
*/
|
||||
public String getShortDescription() {
|
||||
return this.shortDescription;
|
||||
}
|
||||
|
||||
public void setShortDescription(String shortDescription) {
|
||||
this.shortDescription = shortDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default value, if any.
|
||||
* @return the default value
|
||||
*/
|
||||
public Object getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hints of this item.
|
||||
* @return the hints
|
||||
*/
|
||||
public Hints getHints() {
|
||||
return this.hints;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Deprecation} for this property, if any.
|
||||
* @return the deprecation
|
||||
* @see #isDeprecated()
|
||||
*/
|
||||
public Deprecation getDeprecation() {
|
||||
return this.deprecation;
|
||||
}
|
||||
|
||||
public void setDeprecation(Deprecation deprecation) {
|
||||
this.deprecation = deprecation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the property is deprecated.
|
||||
* @return if the property is deprecated
|
||||
* @see #getDeprecation()
|
||||
*/
|
||||
public boolean isDeprecated() {
|
||||
return this.deprecation != null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A repository of configuration metadata.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public interface ConfigurationMetadataRepository {
|
||||
|
||||
/**
|
||||
* Defines the name of the "root" group, that is the group that gathers all the
|
||||
* properties that aren't attached to a specific group.
|
||||
*/
|
||||
String ROOT_GROUP = "_ROOT_GROUP_";
|
||||
|
||||
/**
|
||||
* Return the groups, indexed by id.
|
||||
* @return all configuration meta-data groups
|
||||
*/
|
||||
Map<String, ConfigurationMetadataGroup> getAllGroups();
|
||||
|
||||
/**
|
||||
* Return the properties, indexed by id.
|
||||
* @return all configuration meta-data properties
|
||||
*/
|
||||
Map<String, ConfigurationMetadataProperty> getAllProperties();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Load a {@link ConfigurationMetadataRepository} from the content of arbitrary
|
||||
* resource(s).
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public final class ConfigurationMetadataRepositoryJsonBuilder {
|
||||
|
||||
/**
|
||||
* UTF-8 Charset.
|
||||
*/
|
||||
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
private Charset defaultCharset = UTF_8;
|
||||
|
||||
private final JsonReader reader = new JsonReader();
|
||||
|
||||
private final List<SimpleConfigurationMetadataRepository> repositories = new ArrayList<>();
|
||||
|
||||
private ConfigurationMetadataRepositoryJsonBuilder(Charset defaultCharset) {
|
||||
this.defaultCharset = defaultCharset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the content of a {@link ConfigurationMetadataRepository} defined by the
|
||||
* specified {@link InputStream} json document using the default charset. If this
|
||||
* metadata repository holds items that were loaded previously, these are ignored.
|
||||
* <p>
|
||||
* Leaves the stream open when done.
|
||||
* @param inputStream the source input stream
|
||||
* @return this builder
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
public ConfigurationMetadataRepositoryJsonBuilder withJsonResource(
|
||||
InputStream inputStream) throws IOException {
|
||||
return withJsonResource(inputStream, this.defaultCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the content of a {@link ConfigurationMetadataRepository} defined by the
|
||||
* specified {@link InputStream} json document using the specified {@link Charset}. If
|
||||
* this metadata repository holds items that were loaded previously, these are
|
||||
* ignored.
|
||||
* <p>
|
||||
* Leaves the stream open when done.
|
||||
* @param inputStream the source input stream
|
||||
* @param charset the charset of the input
|
||||
* @return this builder
|
||||
* @throws IOException in case of I/O errors
|
||||
*/
|
||||
public ConfigurationMetadataRepositoryJsonBuilder withJsonResource(
|
||||
InputStream inputStream, Charset charset) throws IOException {
|
||||
if (inputStream == null) {
|
||||
throw new IllegalArgumentException("InputStream must not be null.");
|
||||
}
|
||||
this.repositories.add(add(inputStream, charset));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link ConfigurationMetadataRepository} with the current state of this
|
||||
* builder.
|
||||
* @return this builder
|
||||
*/
|
||||
public ConfigurationMetadataRepository build() {
|
||||
SimpleConfigurationMetadataRepository result = new SimpleConfigurationMetadataRepository();
|
||||
for (SimpleConfigurationMetadataRepository repository : this.repositories) {
|
||||
result.include(repository);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SimpleConfigurationMetadataRepository add(InputStream in, Charset charset)
|
||||
throws IOException {
|
||||
try {
|
||||
RawConfigurationMetadata metadata = this.reader.read(in, charset);
|
||||
return create(metadata);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to read configuration metadata", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private SimpleConfigurationMetadataRepository create(
|
||||
RawConfigurationMetadata metadata) {
|
||||
SimpleConfigurationMetadataRepository repository = new SimpleConfigurationMetadataRepository();
|
||||
repository.add(metadata.getSources());
|
||||
for (ConfigurationMetadataItem item : metadata.getItems()) {
|
||||
ConfigurationMetadataSource source = getSource(metadata, item);
|
||||
repository.add(item, source);
|
||||
}
|
||||
Map<String, ConfigurationMetadataProperty> allProperties = repository
|
||||
.getAllProperties();
|
||||
for (ConfigurationMetadataHint hint : metadata.getHints()) {
|
||||
ConfigurationMetadataProperty property = allProperties.get(hint.getId());
|
||||
if (property != null) {
|
||||
addValueHints(property, hint);
|
||||
}
|
||||
else {
|
||||
String id = hint.resolveId();
|
||||
property = allProperties.get(id);
|
||||
if (property != null) {
|
||||
if (hint.isMapKeyHints()) {
|
||||
addMapHints(property, hint);
|
||||
}
|
||||
else {
|
||||
addValueHints(property, hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return repository;
|
||||
}
|
||||
|
||||
private void addValueHints(ConfigurationMetadataProperty property,
|
||||
ConfigurationMetadataHint hint) {
|
||||
property.getHints().getValueHints().addAll(hint.getValueHints());
|
||||
property.getHints().getValueProviders().addAll(hint.getValueProviders());
|
||||
}
|
||||
|
||||
private void addMapHints(ConfigurationMetadataProperty property,
|
||||
ConfigurationMetadataHint hint) {
|
||||
property.getHints().getKeyHints().addAll(hint.getValueHints());
|
||||
property.getHints().getKeyProviders().addAll(hint.getValueProviders());
|
||||
}
|
||||
|
||||
private ConfigurationMetadataSource getSource(RawConfigurationMetadata metadata,
|
||||
ConfigurationMetadataItem item) {
|
||||
if (item.getSourceType() != null) {
|
||||
return metadata.getSource(item.getSourceType());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new builder instance using {@link #UTF_8} as the default charset and the
|
||||
* specified json resource.
|
||||
* @param inputStreams the source input streams
|
||||
* @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance.
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public static ConfigurationMetadataRepositoryJsonBuilder create(
|
||||
InputStream... inputStreams) throws IOException {
|
||||
ConfigurationMetadataRepositoryJsonBuilder builder = create();
|
||||
for (InputStream inputStream : inputStreams) {
|
||||
builder = builder.withJsonResource(inputStream);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new builder instance using {@link #UTF_8} as the default charset.
|
||||
* @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance.
|
||||
*/
|
||||
public static ConfigurationMetadataRepositoryJsonBuilder create() {
|
||||
return create(UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new builder instance using the specified default {@link Charset}.
|
||||
* @param defaultCharset the default charset to use
|
||||
* @return a new {@link ConfigurationMetadataRepositoryJsonBuilder} instance.
|
||||
*/
|
||||
public static ConfigurationMetadataRepositoryJsonBuilder create(
|
||||
Charset defaultCharset) {
|
||||
return new ConfigurationMetadataRepositoryJsonBuilder(defaultCharset);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A source of configuration metadata. Also defines where the source is declared, for
|
||||
* instance if it is defined as a {@code @Bean}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ConfigurationMetadataSource implements Serializable {
|
||||
|
||||
private String groupId;
|
||||
|
||||
private String type;
|
||||
|
||||
private String description;
|
||||
|
||||
private String shortDescription;
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private String sourceMethod;
|
||||
|
||||
private final Map<String, ConfigurationMetadataProperty> properties = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The identifier of the group to which this source is associated.
|
||||
* @return the group id
|
||||
*/
|
||||
public String getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the source. Usually this is the fully qualified name of a class that
|
||||
* defines configuration items. This class may or may not be available at runtime.
|
||||
* @return the type
|
||||
*/
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* A description of this source, if any. Can be multi-lines.
|
||||
* @return the description
|
||||
* @see #getShortDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single-line, single-sentence description of this source, if any.
|
||||
* @return the short description
|
||||
* @see #getDescription()
|
||||
*/
|
||||
public String getShortDescription() {
|
||||
return this.shortDescription;
|
||||
}
|
||||
|
||||
public void setShortDescription(String shortDescription) {
|
||||
this.shortDescription = shortDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type where this source is defined. This can be identical to the
|
||||
* {@link #getType() type} if the source is self-defined.
|
||||
* @return the source type
|
||||
*/
|
||||
public String getSourceType() {
|
||||
return this.sourceType;
|
||||
}
|
||||
|
||||
void setSourceType(String sourceType) {
|
||||
this.sourceType = sourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method name that defines this source, if any.
|
||||
* @return the source method
|
||||
*/
|
||||
public String getSourceMethod() {
|
||||
return this.sourceMethod;
|
||||
}
|
||||
|
||||
void setSourceMethod(String sourceMethod) {
|
||||
this.sourceMethod = sourceMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the properties defined by this source.
|
||||
* @return the properties
|
||||
*/
|
||||
public Map<String, ConfigurationMetadataProperty> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Indicate that a property is deprecated. Provide additional information about the
|
||||
* deprecation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class Deprecation implements Serializable {
|
||||
|
||||
private Level level = Level.WARNING;
|
||||
|
||||
private String reason;
|
||||
|
||||
private String replacement;
|
||||
|
||||
/**
|
||||
* Define the {@link Level} of deprecation.
|
||||
* @return the deprecation level
|
||||
*/
|
||||
public Level getLevel() {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
public void setLevel(Level level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason why the related property is deprecated, if any. Can be multi-lines.
|
||||
* @return the deprecation reason
|
||||
*/
|
||||
public String getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* The full name of the property that replaces the related deprecated property, if
|
||||
* any.
|
||||
* @return the replacement property name
|
||||
*/
|
||||
public String getReplacement() {
|
||||
return this.replacement;
|
||||
}
|
||||
|
||||
public void setReplacement(String replacement) {
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Deprecation{" + "level='" + this.level + '\'' + ", reason='" + this.reason
|
||||
+ '\'' + ", replacement='" + this.replacement + '\'' + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the deprecation level.
|
||||
*/
|
||||
public enum Level {
|
||||
|
||||
/**
|
||||
* The property is still bound.
|
||||
*/
|
||||
WARNING,
|
||||
|
||||
/**
|
||||
* The property has been removed and is no longer bound.
|
||||
*/
|
||||
ERROR
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.text.BreakIterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utility to extract a description.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class DescriptionExtractor {
|
||||
|
||||
private static final String NEW_LINE = System.getProperty("line.separator");
|
||||
|
||||
public String getShortDescription(String description) {
|
||||
if (description == null) {
|
||||
return null;
|
||||
}
|
||||
int dot = description.indexOf(".");
|
||||
if (dot != -1) {
|
||||
BreakIterator breakIterator = BreakIterator.getSentenceInstance(Locale.US);
|
||||
breakIterator.setText(description);
|
||||
String text = description
|
||||
.substring(breakIterator.first(), breakIterator.next()).trim();
|
||||
return removeSpaceBetweenLine(text);
|
||||
}
|
||||
else {
|
||||
String[] lines = description.split(NEW_LINE);
|
||||
return lines[0].trim();
|
||||
}
|
||||
}
|
||||
|
||||
private String removeSpaceBetweenLine(String text) {
|
||||
String[] lines = text.split(NEW_LINE);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String line : lines) {
|
||||
sb.append(line.trim()).append(" ");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Hints of an item to provide the list of values and/or the name of the provider
|
||||
* responsible to identify suitable values. If the type of the related item is a
|
||||
* {@link java.util.Map} it can have both key and value hints.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class Hints {
|
||||
|
||||
private final List<ValueHint> keyHints = new ArrayList<>();
|
||||
|
||||
private final List<ValueProvider> keyProviders = new ArrayList<>();
|
||||
|
||||
private final List<ValueHint> valueHints = new ArrayList<>();
|
||||
|
||||
private final List<ValueProvider> valueProviders = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* The list of well-defined keys, if any. Only applicable if the type of the related
|
||||
* item is a {@link java.util.Map}. If no extra {@link ValueProvider provider} is
|
||||
* specified, these values are to be considered a closed-set of the available keys for
|
||||
* the map.
|
||||
* @return the key hints
|
||||
*/
|
||||
public List<ValueHint> getKeyHints() {
|
||||
return this.keyHints;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value providers that are applicable to the keys of this item. Only applicable
|
||||
* if the type of the related item is a {@link java.util.Map}. Only one
|
||||
* {@link ValueProvider} is enabled for a key: the first in the list that is supported
|
||||
* should be used.
|
||||
* @return the key providers
|
||||
*/
|
||||
public List<ValueProvider> getKeyProviders() {
|
||||
return this.keyProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of well-defined values, if any. If no extra {@link ValueProvider provider}
|
||||
* is specified, these values are to be considered a closed-set of the available
|
||||
* values for this item.
|
||||
* @return the value hints
|
||||
*/
|
||||
public List<ValueHint> getValueHints() {
|
||||
return this.valueHints;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value providers that are applicable to this item. Only one
|
||||
* {@link ValueProvider} is enabled for an item: the first in the list that is
|
||||
* supported should be used.
|
||||
* @return the value providers
|
||||
*/
|
||||
public List<ValueProvider> getValueProviders() {
|
||||
return this.valueProviders;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Read standard json metadata format as {@link ConfigurationMetadataRepository}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
class JsonReader {
|
||||
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
private final DescriptionExtractor descriptionExtractor = new DescriptionExtractor();
|
||||
|
||||
public RawConfigurationMetadata read(InputStream in, Charset charset)
|
||||
throws IOException {
|
||||
try {
|
||||
JSONObject json = readJson(in, charset);
|
||||
List<ConfigurationMetadataSource> groups = parseAllSources(json);
|
||||
List<ConfigurationMetadataItem> items = parseAllItems(json);
|
||||
List<ConfigurationMetadataHint> hints = parseAllHints(json);
|
||||
return new RawConfigurationMetadata(groups, items, hints);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (ex instanceof IOException) {
|
||||
throw (IOException) ex;
|
||||
}
|
||||
if (ex instanceof RuntimeException) {
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ConfigurationMetadataSource> parseAllSources(JSONObject root)
|
||||
throws Exception {
|
||||
List<ConfigurationMetadataSource> result = new ArrayList<>();
|
||||
if (!root.has("groups")) {
|
||||
return result;
|
||||
}
|
||||
JSONArray sources = root.getJSONArray("groups");
|
||||
for (int i = 0; i < sources.length(); i++) {
|
||||
JSONObject source = sources.getJSONObject(i);
|
||||
result.add(parseSource(source));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ConfigurationMetadataItem> parseAllItems(JSONObject root)
|
||||
throws Exception {
|
||||
List<ConfigurationMetadataItem> result = new ArrayList<>();
|
||||
if (!root.has("properties")) {
|
||||
return result;
|
||||
}
|
||||
JSONArray items = root.getJSONArray("properties");
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
JSONObject item = items.getJSONObject(i);
|
||||
result.add(parseItem(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ConfigurationMetadataHint> parseAllHints(JSONObject root)
|
||||
throws Exception {
|
||||
List<ConfigurationMetadataHint> result = new ArrayList<>();
|
||||
if (!root.has("hints")) {
|
||||
return result;
|
||||
}
|
||||
JSONArray items = root.getJSONArray("hints");
|
||||
for (int i = 0; i < items.length(); i++) {
|
||||
JSONObject item = items.getJSONObject(i);
|
||||
result.add(parseHint(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ConfigurationMetadataSource parseSource(JSONObject json) throws Exception {
|
||||
ConfigurationMetadataSource source = new ConfigurationMetadataSource();
|
||||
source.setGroupId(json.getString("name"));
|
||||
source.setType(json.optString("type", null));
|
||||
String description = json.optString("description", null);
|
||||
source.setDescription(description);
|
||||
source.setShortDescription(
|
||||
this.descriptionExtractor.getShortDescription(description));
|
||||
source.setSourceType(json.optString("sourceType", null));
|
||||
source.setSourceMethod(json.optString("sourceMethod", null));
|
||||
return source;
|
||||
}
|
||||
|
||||
private ConfigurationMetadataItem parseItem(JSONObject json) throws Exception {
|
||||
ConfigurationMetadataItem item = new ConfigurationMetadataItem();
|
||||
item.setId(json.getString("name"));
|
||||
item.setType(json.optString("type", null));
|
||||
String description = json.optString("description", null);
|
||||
item.setDescription(description);
|
||||
item.setShortDescription(
|
||||
this.descriptionExtractor.getShortDescription(description));
|
||||
item.setDefaultValue(readItemValue(json.opt("defaultValue")));
|
||||
item.setDeprecation(parseDeprecation(json));
|
||||
item.setSourceType(json.optString("sourceType", null));
|
||||
item.setSourceMethod(json.optString("sourceMethod", null));
|
||||
return item;
|
||||
}
|
||||
|
||||
private ConfigurationMetadataHint parseHint(JSONObject json) throws Exception {
|
||||
ConfigurationMetadataHint hint = new ConfigurationMetadataHint();
|
||||
hint.setId(json.getString("name"));
|
||||
if (json.has("values")) {
|
||||
JSONArray values = json.getJSONArray("values");
|
||||
for (int i = 0; i < values.length(); i++) {
|
||||
JSONObject value = values.getJSONObject(i);
|
||||
ValueHint valueHint = new ValueHint();
|
||||
valueHint.setValue(readItemValue(value.get("value")));
|
||||
String description = value.optString("description", null);
|
||||
valueHint.setDescription(description);
|
||||
valueHint.setShortDescription(
|
||||
this.descriptionExtractor.getShortDescription(description));
|
||||
hint.getValueHints().add(valueHint);
|
||||
}
|
||||
}
|
||||
if (json.has("providers")) {
|
||||
JSONArray providers = json.getJSONArray("providers");
|
||||
for (int i = 0; i < providers.length(); i++) {
|
||||
JSONObject provider = providers.getJSONObject(i);
|
||||
ValueProvider valueProvider = new ValueProvider();
|
||||
valueProvider.setName(provider.getString("name"));
|
||||
if (provider.has("parameters")) {
|
||||
JSONObject parameters = provider.getJSONObject("parameters");
|
||||
Iterator<?> keys = parameters.keys();
|
||||
while (keys.hasNext()) {
|
||||
String key = (String) keys.next();
|
||||
valueProvider.getParameters().put(key,
|
||||
readItemValue(parameters.get(key)));
|
||||
}
|
||||
}
|
||||
hint.getValueProviders().add(valueProvider);
|
||||
}
|
||||
}
|
||||
return hint;
|
||||
}
|
||||
|
||||
private Deprecation parseDeprecation(JSONObject object) throws Exception {
|
||||
if (object.has("deprecation")) {
|
||||
JSONObject deprecationJsonObject = object.getJSONObject("deprecation");
|
||||
Deprecation deprecation = new Deprecation();
|
||||
deprecation.setLevel(parseDeprecationLevel(
|
||||
deprecationJsonObject.optString("level", null)));
|
||||
deprecation.setReason(deprecationJsonObject.optString("reason", null));
|
||||
deprecation
|
||||
.setReplacement(deprecationJsonObject.optString("replacement", null));
|
||||
return deprecation;
|
||||
}
|
||||
return (object.optBoolean("deprecated") ? new Deprecation() : null);
|
||||
}
|
||||
|
||||
private Deprecation.Level parseDeprecationLevel(String value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return Deprecation.Level.valueOf(value.toUpperCase());
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// let's use the default
|
||||
}
|
||||
}
|
||||
return Deprecation.Level.WARNING;
|
||||
}
|
||||
|
||||
private Object readItemValue(Object value) throws Exception {
|
||||
if (value instanceof JSONArray) {
|
||||
JSONArray array = (JSONArray) value;
|
||||
Object[] content = new Object[array.length()];
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
content[i] = array.get(i);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private JSONObject readJson(InputStream in, Charset charset) throws Exception {
|
||||
try {
|
||||
StringBuilder out = new StringBuilder();
|
||||
InputStreamReader reader = new InputStreamReader(in, charset);
|
||||
char[] buffer = new char[BUFFER_SIZE];
|
||||
int bytesRead = -1;
|
||||
while ((bytesRead = reader.read(buffer)) != -1) {
|
||||
out.append(buffer, 0, bytesRead);
|
||||
}
|
||||
return new JSONObject(out.toString());
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A raw metadata structure. Used to initialize a {@link ConfigurationMetadataRepository}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
class RawConfigurationMetadata {
|
||||
|
||||
private final List<ConfigurationMetadataSource> sources;
|
||||
|
||||
private final List<ConfigurationMetadataItem> items;
|
||||
|
||||
private final List<ConfigurationMetadataHint> hints;
|
||||
|
||||
RawConfigurationMetadata(List<ConfigurationMetadataSource> sources,
|
||||
List<ConfigurationMetadataItem> items,
|
||||
List<ConfigurationMetadataHint> hints) {
|
||||
this.sources = new ArrayList<>(sources);
|
||||
this.items = new ArrayList<>(items);
|
||||
this.hints = new ArrayList<>(hints);
|
||||
for (ConfigurationMetadataItem item : this.items) {
|
||||
resolveName(item);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ConfigurationMetadataSource> getSources() {
|
||||
return this.sources;
|
||||
}
|
||||
|
||||
public ConfigurationMetadataSource getSource(String type) {
|
||||
for (ConfigurationMetadataSource source : this.sources) {
|
||||
if (type.equals(source.getType())) {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ConfigurationMetadataItem> getItems() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public List<ConfigurationMetadataHint> getHints() {
|
||||
return this.hints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the name of an item against this instance.
|
||||
* @param item the item to resolve
|
||||
* @see ConfigurationMetadataProperty#setName(String)
|
||||
*/
|
||||
private void resolveName(ConfigurationMetadataItem item) {
|
||||
item.setName(item.getId()); // fallback
|
||||
if (item.getSourceType() == null) {
|
||||
return;
|
||||
}
|
||||
ConfigurationMetadataSource source = getSource(item.getSourceType());
|
||||
if (source != null) {
|
||||
String groupId = source.getGroupId();
|
||||
String dottedPrefix = groupId + ".";
|
||||
String id = item.getId();
|
||||
if (hasLength(groupId) && id.startsWith(dottedPrefix)) {
|
||||
String name = id.substring(dottedPrefix.length());
|
||||
item.setName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasLength(String string) {
|
||||
return (string != null && !string.isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The default {@link ConfigurationMetadataRepository} implementation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SimpleConfigurationMetadataRepository
|
||||
implements ConfigurationMetadataRepository, Serializable {
|
||||
|
||||
private final Map<String, ConfigurationMetadataGroup> allGroups = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Map<String, ConfigurationMetadataGroup> getAllGroups() {
|
||||
return Collections.unmodifiableMap(this.allGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ConfigurationMetadataProperty> getAllProperties() {
|
||||
Map<String, ConfigurationMetadataProperty> properties = new HashMap<>();
|
||||
for (ConfigurationMetadataGroup group : this.allGroups.values()) {
|
||||
properties.putAll(group.getProperties());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified {@link ConfigurationMetadataSource sources}.
|
||||
* @param sources the sources to add
|
||||
*/
|
||||
public void add(Collection<ConfigurationMetadataSource> sources) {
|
||||
for (ConfigurationMetadataSource source : sources) {
|
||||
String groupId = source.getGroupId();
|
||||
ConfigurationMetadataGroup group = this.allGroups.get(groupId);
|
||||
if (group == null) {
|
||||
group = new ConfigurationMetadataGroup(groupId);
|
||||
this.allGroups.put(groupId, group);
|
||||
}
|
||||
String sourceType = source.getType();
|
||||
if (sourceType != null) {
|
||||
putIfAbsent(group.getSources(), sourceType, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link ConfigurationMetadataProperty} with the
|
||||
* {@link ConfigurationMetadataSource source} that defines it, if any.
|
||||
* @param property the property to add
|
||||
* @param source the source
|
||||
*/
|
||||
public void add(ConfigurationMetadataProperty property,
|
||||
ConfigurationMetadataSource source) {
|
||||
if (source != null) {
|
||||
putIfAbsent(source.getProperties(), property.getId(), property);
|
||||
}
|
||||
putIfAbsent(getGroup(source).getProperties(), property.getId(), property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the content of the specified repository to this repository.
|
||||
* @param repository the repository to include
|
||||
*/
|
||||
public void include(ConfigurationMetadataRepository repository) {
|
||||
for (ConfigurationMetadataGroup group : repository.getAllGroups().values()) {
|
||||
ConfigurationMetadataGroup existingGroup = this.allGroups.get(group.getId());
|
||||
if (existingGroup == null) {
|
||||
this.allGroups.put(group.getId(), group);
|
||||
}
|
||||
else {
|
||||
// Merge properties
|
||||
for (Map.Entry<String, ConfigurationMetadataProperty> entry : group
|
||||
.getProperties().entrySet()) {
|
||||
putIfAbsent(existingGroup.getProperties(), entry.getKey(),
|
||||
entry.getValue());
|
||||
}
|
||||
// Merge sources
|
||||
for (Map.Entry<String, ConfigurationMetadataSource> entry : group
|
||||
.getSources().entrySet()) {
|
||||
putIfAbsent(existingGroup.getSources(), entry.getKey(),
|
||||
entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ConfigurationMetadataGroup getGroup(ConfigurationMetadataSource source) {
|
||||
if (source == null) {
|
||||
ConfigurationMetadataGroup rootGroup = this.allGroups.get(ROOT_GROUP);
|
||||
if (rootGroup == null) {
|
||||
rootGroup = new ConfigurationMetadataGroup(ROOT_GROUP);
|
||||
this.allGroups.put(ROOT_GROUP, rootGroup);
|
||||
}
|
||||
return rootGroup;
|
||||
}
|
||||
return this.allGroups.get(source.getGroupId());
|
||||
}
|
||||
|
||||
private <V> void putIfAbsent(Map<String, V> map, String key, V value) {
|
||||
if (!map.containsKey(key)) {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Hint for a value a given property may have. Provide the value and an optional
|
||||
* description.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ValueHint implements Serializable {
|
||||
|
||||
private Object value;
|
||||
|
||||
private String description;
|
||||
|
||||
private String shortDescription;
|
||||
|
||||
/**
|
||||
* Return the hint value.
|
||||
* @return the value
|
||||
*/
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void setValue(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* A description of this value, if any. Can be multi-lines.
|
||||
* @return the description
|
||||
* @see #getShortDescription()
|
||||
*/
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single-line, single-sentence description of this hint, if any.
|
||||
* @return the short description
|
||||
* @see #getDescription()
|
||||
*/
|
||||
public String getShortDescription() {
|
||||
return this.shortDescription;
|
||||
}
|
||||
|
||||
public void setShortDescription(String shortDescription) {
|
||||
this.shortDescription = shortDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ValueHint{" + "value=" + this.value + ", description='" + this.description
|
||||
+ '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Define a component that is able to provide the values of a property.
|
||||
* <p>
|
||||
* Each provider is defined by a {@code name} and can have an arbitrary number of
|
||||
* {@code parameters}. The available providers are defined in the Spring Boot
|
||||
* documentation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ValueProvider implements Serializable {
|
||||
|
||||
private String name;
|
||||
|
||||
private final Map<String, Object> parameters = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Return the name of the provider.
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parameters.
|
||||
* @return the parameters
|
||||
*/
|
||||
public Map<String, Object> getParameters() {
|
||||
return this.parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ValueProvider{" + "name='" + this.name + ", parameters=" + this.parameters
|
||||
+ '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Spring Boot configuration meta-data parser.
|
||||
*/
|
||||
package org.springframework.boot.configurationmetadata;
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Base for configuration meta-data tests.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractConfigurationMetadataTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
protected void assertSource(ConfigurationMetadataSource actual, String groupId,
|
||||
String type, String sourceType) {
|
||||
assertThat(actual).isNotNull();
|
||||
assertThat(actual.getGroupId()).isEqualTo(groupId);
|
||||
assertThat(actual.getType()).isEqualTo(type);
|
||||
assertThat(actual.getSourceType()).isEqualTo(sourceType);
|
||||
}
|
||||
|
||||
protected void assertProperty(ConfigurationMetadataProperty actual, String id,
|
||||
String name, Class<?> type, Object defaultValue) {
|
||||
assertThat(actual).isNotNull();
|
||||
assertThat(actual.getId()).isEqualTo(id);
|
||||
assertThat(actual.getName()).isEqualTo(name);
|
||||
String typeName = type != null ? type.getName() : null;
|
||||
assertThat(actual.getType()).isEqualTo(typeName);
|
||||
assertThat(actual.getDefaultValue()).isEqualTo(defaultValue);
|
||||
}
|
||||
|
||||
protected void assertItem(ConfigurationMetadataItem actual, String sourceType) {
|
||||
assertThat(actual).isNotNull();
|
||||
assertThat(actual.getSourceType()).isEqualTo(sourceType);
|
||||
}
|
||||
|
||||
protected InputStream getInputStreamFor(String name) throws IOException {
|
||||
Resource r = new ClassPathResource(
|
||||
"metadata/configuration-metadata-" + name + ".json");
|
||||
return r.getInputStream();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigurationMetadataRepository}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ConfigurationMetadataRepositoryJsonBuilderTests
|
||||
extends AbstractConfigurationMetadataTests {
|
||||
|
||||
@Test
|
||||
public void nullResource() throws IOException {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
ConfigurationMetadataRepositoryJsonBuilder.create().withJsonResource(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleRepository() throws IOException {
|
||||
try (InputStream foo = getInputStreamFor("foo")) {
|
||||
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder
|
||||
.create(foo).build();
|
||||
validateFoo(repo);
|
||||
assertThat(repo.getAllGroups()).hasSize(1);
|
||||
contains(repo.getAllProperties(), "spring.foo.name", "spring.foo.description",
|
||||
"spring.foo.counter");
|
||||
assertThat(repo.getAllProperties()).hasSize(3);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hintsOnMaps() throws IOException {
|
||||
try (InputStream map = getInputStreamFor("map")) {
|
||||
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder
|
||||
.create(map).build();
|
||||
validateMap(repo);
|
||||
assertThat(repo.getAllGroups()).hasSize(1);
|
||||
contains(repo.getAllProperties(), "spring.map.first", "spring.map.second",
|
||||
"spring.map.keys", "spring.map.values");
|
||||
assertThat(repo.getAllProperties()).hasSize(4);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void severalRepositoriesNoConflict() throws IOException {
|
||||
try (InputStream foo = getInputStreamFor("foo");
|
||||
InputStream bar = getInputStreamFor("bar")) {
|
||||
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder
|
||||
.create(foo, bar).build();
|
||||
validateFoo(repo);
|
||||
validateBar(repo);
|
||||
assertThat(repo.getAllGroups()).hasSize(2);
|
||||
contains(repo.getAllProperties(), "spring.foo.name", "spring.foo.description",
|
||||
"spring.foo.counter", "spring.bar.name", "spring.bar.description",
|
||||
"spring.bar.counter");
|
||||
assertThat(repo.getAllProperties()).hasSize(6);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void repositoryWithRoot() throws IOException {
|
||||
try (InputStream foo = getInputStreamFor("foo");
|
||||
InputStream root = getInputStreamFor("root")) {
|
||||
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder
|
||||
.create(foo, root).build();
|
||||
validateFoo(repo);
|
||||
assertThat(repo.getAllGroups()).hasSize(2);
|
||||
|
||||
contains(repo.getAllProperties(), "spring.foo.name", "spring.foo.description",
|
||||
"spring.foo.counter", "spring.root.name", "spring.root2.name");
|
||||
assertThat(repo.getAllProperties()).hasSize(5);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void severalRepositoriesIdenticalGroups() throws IOException {
|
||||
try (InputStream foo = getInputStreamFor("foo");
|
||||
InputStream foo2 = getInputStreamFor("foo2")) {
|
||||
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder
|
||||
.create(foo, foo2).build();
|
||||
assertThat(repo.getAllGroups()).hasSize(1);
|
||||
ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo");
|
||||
contains(group.getSources(), "org.acme.Foo", "org.acme.Foo2",
|
||||
"org.springframework.boot.FooProperties");
|
||||
assertThat(group.getSources()).hasSize(3);
|
||||
contains(group.getProperties(), "spring.foo.name", "spring.foo.description",
|
||||
"spring.foo.counter", "spring.foo.enabled", "spring.foo.type");
|
||||
assertThat(group.getProperties()).hasSize(5);
|
||||
contains(repo.getAllProperties(), "spring.foo.name", "spring.foo.description",
|
||||
"spring.foo.counter", "spring.foo.enabled", "spring.foo.type");
|
||||
assertThat(repo.getAllProperties()).hasSize(5);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyGroups() throws IOException {
|
||||
try (InputStream in = getInputStreamFor("empty-groups")) {
|
||||
ConfigurationMetadataRepository repo = ConfigurationMetadataRepositoryJsonBuilder
|
||||
.create(in).build();
|
||||
validateEmptyGroup(repo);
|
||||
assertThat(repo.getAllGroups()).hasSize(1);
|
||||
contains(repo.getAllProperties(), "name", "title");
|
||||
assertThat(repo.getAllProperties()).hasSize(2);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builderInstancesAreIsolated() throws IOException {
|
||||
try (InputStream foo = getInputStreamFor("foo");
|
||||
InputStream bar = getInputStreamFor("bar")) {
|
||||
ConfigurationMetadataRepositoryJsonBuilder builder = ConfigurationMetadataRepositoryJsonBuilder
|
||||
.create();
|
||||
ConfigurationMetadataRepository firstRepo = builder.withJsonResource(foo)
|
||||
.build();
|
||||
validateFoo(firstRepo);
|
||||
ConfigurationMetadataRepository secondRepo = builder.withJsonResource(bar)
|
||||
.build();
|
||||
validateFoo(secondRepo);
|
||||
validateBar(secondRepo);
|
||||
// first repo not impacted by second build
|
||||
assertThat(secondRepo).isNotEqualTo(firstRepo);
|
||||
assertThat(firstRepo.getAllGroups()).hasSize(1);
|
||||
assertThat(firstRepo.getAllProperties()).hasSize(3);
|
||||
assertThat(secondRepo.getAllGroups()).hasSize(2);
|
||||
assertThat(secondRepo.getAllProperties()).hasSize(6);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateFoo(ConfigurationMetadataRepository repo) {
|
||||
ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.foo");
|
||||
contains(group.getSources(), "org.acme.Foo",
|
||||
"org.springframework.boot.FooProperties");
|
||||
ConfigurationMetadataSource source = group.getSources().get("org.acme.Foo");
|
||||
contains(source.getProperties(), "spring.foo.name", "spring.foo.description");
|
||||
assertThat(source.getProperties()).hasSize(2);
|
||||
ConfigurationMetadataSource source2 = group.getSources()
|
||||
.get("org.springframework.boot.FooProperties");
|
||||
contains(source2.getProperties(), "spring.foo.name", "spring.foo.counter");
|
||||
assertThat(source2.getProperties()).hasSize(2);
|
||||
validatePropertyHints(repo.getAllProperties().get("spring.foo.name"), 0, 0);
|
||||
validatePropertyHints(repo.getAllProperties().get("spring.foo.description"), 0,
|
||||
0);
|
||||
validatePropertyHints(repo.getAllProperties().get("spring.foo.counter"), 1, 1);
|
||||
}
|
||||
|
||||
private void validateBar(ConfigurationMetadataRepository repo) {
|
||||
ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.bar");
|
||||
contains(group.getSources(), "org.acme.Bar",
|
||||
"org.springframework.boot.BarProperties");
|
||||
ConfigurationMetadataSource source = group.getSources().get("org.acme.Bar");
|
||||
contains(source.getProperties(), "spring.bar.name", "spring.bar.description");
|
||||
assertThat(source.getProperties()).hasSize(2);
|
||||
ConfigurationMetadataSource source2 = group.getSources()
|
||||
.get("org.springframework.boot.BarProperties");
|
||||
contains(source2.getProperties(), "spring.bar.name", "spring.bar.counter");
|
||||
assertThat(source2.getProperties()).hasSize(2);
|
||||
validatePropertyHints(repo.getAllProperties().get("spring.bar.name"), 0, 0);
|
||||
validatePropertyHints(repo.getAllProperties().get("spring.bar.description"), 2,
|
||||
2);
|
||||
validatePropertyHints(repo.getAllProperties().get("spring.bar.counter"), 0, 0);
|
||||
}
|
||||
|
||||
private void validateMap(ConfigurationMetadataRepository repo) {
|
||||
ConfigurationMetadataGroup group = repo.getAllGroups().get("spring.map");
|
||||
ConfigurationMetadataSource source = group.getSources().get("org.acme.Map");
|
||||
contains(source.getProperties(), "spring.map.first", "spring.map.second",
|
||||
"spring.map.keys", "spring.map.values");
|
||||
assertThat(source.getProperties()).hasSize(4);
|
||||
ConfigurationMetadataProperty first = repo.getAllProperties()
|
||||
.get("spring.map.first");
|
||||
assertThat(first.getHints().getKeyHints()).hasSize(2);
|
||||
assertThat(first.getHints().getValueProviders()).hasSize(0);
|
||||
assertThat(first.getHints().getKeyHints().get(0).getValue()).isEqualTo("one");
|
||||
assertThat(first.getHints().getKeyHints().get(0).getDescription())
|
||||
.isEqualTo("First.");
|
||||
assertThat(first.getHints().getKeyHints().get(1).getValue()).isEqualTo("two");
|
||||
assertThat(first.getHints().getKeyHints().get(1).getDescription())
|
||||
.isEqualTo("Second.");
|
||||
ConfigurationMetadataProperty second = repo.getAllProperties()
|
||||
.get("spring.map.second");
|
||||
assertThat(second.getHints().getValueHints()).hasSize(2);
|
||||
assertThat(second.getHints().getValueProviders()).hasSize(0);
|
||||
assertThat(second.getHints().getValueHints().get(0).getValue()).isEqualTo("42");
|
||||
assertThat(second.getHints().getValueHints().get(0).getDescription())
|
||||
.isEqualTo("Choose me.");
|
||||
assertThat(second.getHints().getValueHints().get(1).getValue()).isEqualTo("24");
|
||||
assertThat(second.getHints().getValueHints().get(1).getDescription()).isNull();
|
||||
ConfigurationMetadataProperty keys = repo.getAllProperties()
|
||||
.get("spring.map.keys");
|
||||
assertThat(keys.getHints().getValueHints()).hasSize(0);
|
||||
assertThat(keys.getHints().getValueProviders()).hasSize(1);
|
||||
assertThat(keys.getHints().getValueProviders().get(0).getName()).isEqualTo("any");
|
||||
ConfigurationMetadataProperty values = repo.getAllProperties()
|
||||
.get("spring.map.values");
|
||||
assertThat(values.getHints().getValueHints()).hasSize(0);
|
||||
assertThat(values.getHints().getValueProviders()).hasSize(1);
|
||||
assertThat(values.getHints().getValueProviders().get(0).getName())
|
||||
.isEqualTo("handle-as");
|
||||
assertThat(values.getHints().getValueProviders().get(0).getParameters())
|
||||
.hasSize(1);
|
||||
assertThat(values.getHints().getValueProviders().get(0).getParameters()
|
||||
.get("target")).isEqualTo("java.lang.Integer");
|
||||
}
|
||||
|
||||
private void validateEmptyGroup(ConfigurationMetadataRepository repo) {
|
||||
ConfigurationMetadataGroup group = repo.getAllGroups().get("");
|
||||
contains(group.getSources(), "org.acme.Foo", "org.acme.Bar");
|
||||
ConfigurationMetadataSource source = group.getSources().get("org.acme.Foo");
|
||||
contains(source.getProperties(), "name");
|
||||
assertThat(source.getProperties()).hasSize(1);
|
||||
ConfigurationMetadataSource source2 = group.getSources().get("org.acme.Bar");
|
||||
contains(source2.getProperties(), "title");
|
||||
assertThat(source2.getProperties()).hasSize(1);
|
||||
validatePropertyHints(repo.getAllProperties().get("name"), 0, 0);
|
||||
validatePropertyHints(repo.getAllProperties().get("title"), 0, 0);
|
||||
}
|
||||
|
||||
private void validatePropertyHints(ConfigurationMetadataProperty property,
|
||||
int valueHints, int valueProviders) {
|
||||
assertThat(property.getHints().getValueHints().size()).isEqualTo(valueHints);
|
||||
assertThat(property.getHints().getValueProviders().size())
|
||||
.isEqualTo(valueProviders);
|
||||
}
|
||||
|
||||
private void contains(Map<String, ?> source, String... keys) {
|
||||
for (String key : keys) {
|
||||
assertThat(source).containsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DescriptionExtractor}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DescriptionExtractorTests {
|
||||
|
||||
private static final String NEW_LINE = System.getProperty("line.separator");
|
||||
|
||||
private DescriptionExtractor extractor = new DescriptionExtractor();
|
||||
|
||||
@Test
|
||||
public void extractShortDescription() {
|
||||
String description = this.extractor
|
||||
.getShortDescription("My short " + "description. More stuff.");
|
||||
assertThat(description).isEqualTo("My short description.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShortDescriptionNewLineBeforeDot() {
|
||||
String description = this.extractor.getShortDescription(
|
||||
"My short" + NEW_LINE + "description." + NEW_LINE + "More stuff.");
|
||||
assertThat(description).isEqualTo("My short description.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShortDescriptionNewLineBeforeDotWithSpaces() {
|
||||
String description = this.extractor.getShortDescription(
|
||||
"My short " + NEW_LINE + " description. " + NEW_LINE + "More stuff.");
|
||||
assertThat(description).isEqualTo("My short description.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShortDescriptionNoDot() {
|
||||
String description = this.extractor.getShortDescription("My short description");
|
||||
assertThat(description).isEqualTo("My short description");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShortDescriptionNoDotMultipleLines() {
|
||||
String description = this.extractor
|
||||
.getShortDescription("My short description " + NEW_LINE + " More stuff");
|
||||
assertThat(description).isEqualTo("My short description");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void extractShortDescriptionNull() {
|
||||
assertThat(this.extractor.getShortDescription(null)).isEqualTo(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationmetadata;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.json.JSONException;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JsonReader}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class JsonReaderTests extends AbstractConfigurationMetadataTests {
|
||||
|
||||
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
|
||||
|
||||
private final JsonReader reader = new JsonReader();
|
||||
|
||||
@Test
|
||||
public void emptyMetadata() throws IOException {
|
||||
RawConfigurationMetadata rawMetadata = readFor("empty");
|
||||
assertThat(rawMetadata.getSources()).isEmpty();
|
||||
assertThat(rawMetadata.getItems()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidMetadata() throws IOException {
|
||||
this.thrown.expectCause(CoreMatchers.<Throwable>instanceOf(JSONException.class));
|
||||
readFor("invalid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyGroupName() throws IOException {
|
||||
RawConfigurationMetadata rawMetadata = readFor("empty-groups");
|
||||
List<ConfigurationMetadataItem> items = rawMetadata.getItems();
|
||||
assertThat(items).hasSize(2);
|
||||
|
||||
ConfigurationMetadataItem name = items.get(0);
|
||||
assertProperty(name, "name", "name", String.class, null);
|
||||
ConfigurationMetadataItem dotTitle = items.get(1);
|
||||
assertProperty(dotTitle, "title", "title", String.class, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleMetadata() throws IOException {
|
||||
RawConfigurationMetadata rawMetadata = readFor("foo");
|
||||
List<ConfigurationMetadataSource> sources = rawMetadata.getSources();
|
||||
assertThat(sources).hasSize(2);
|
||||
List<ConfigurationMetadataItem> items = rawMetadata.getItems();
|
||||
assertThat(items).hasSize(4);
|
||||
List<ConfigurationMetadataHint> hints = rawMetadata.getHints();
|
||||
assertThat(hints).hasSize(1);
|
||||
|
||||
ConfigurationMetadataSource source = sources.get(0);
|
||||
assertSource(source, "spring.foo", "org.acme.Foo", "org.acme.config.FooApp");
|
||||
assertThat(source.getSourceMethod()).isEqualTo("foo()");
|
||||
assertThat(source.getDescription()).isEqualTo("This is Foo.");
|
||||
assertThat(source.getShortDescription()).isEqualTo("This is Foo.");
|
||||
|
||||
ConfigurationMetadataItem item = items.get(0);
|
||||
assertProperty(item, "spring.foo.name", "name", String.class, null);
|
||||
assertItem(item, "org.acme.Foo");
|
||||
ConfigurationMetadataItem item2 = items.get(1);
|
||||
assertProperty(item2, "spring.foo.description", "description", String.class,
|
||||
"FooBar");
|
||||
assertThat(item2.getDescription()).isEqualTo("Foo description.");
|
||||
assertThat(item2.getShortDescription()).isEqualTo("Foo description.");
|
||||
assertThat(item2.getSourceMethod()).isNull();
|
||||
assertItem(item2, "org.acme.Foo");
|
||||
|
||||
ConfigurationMetadataHint hint = hints.get(0);
|
||||
assertThat(hint.getId()).isEqualTo("spring.foo.counter");
|
||||
assertThat(hint.getValueHints()).hasSize(1);
|
||||
ValueHint valueHint = hint.getValueHints().get(0);
|
||||
assertThat(valueHint.getValue()).isEqualTo(42);
|
||||
assertThat(valueHint.getDescription()).isEqualTo(
|
||||
"Because that's the answer to any question, choose it. \nReally.");
|
||||
assertThat(valueHint.getShortDescription())
|
||||
.isEqualTo("Because that's the answer to any question, choose it.");
|
||||
assertThat(hint.getValueProviders()).hasSize(1);
|
||||
ValueProvider valueProvider = hint.getValueProviders().get(0);
|
||||
assertThat(valueProvider.getName()).isEqualTo("handle-as");
|
||||
assertThat(valueProvider.getParameters()).hasSize(1);
|
||||
assertThat(valueProvider.getParameters().get("target"))
|
||||
.isEqualTo(Integer.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metadataHints() throws IOException {
|
||||
RawConfigurationMetadata rawMetadata = readFor("bar");
|
||||
List<ConfigurationMetadataHint> hints = rawMetadata.getHints();
|
||||
assertThat(hints).hasSize(1);
|
||||
|
||||
ConfigurationMetadataHint hint = hints.get(0);
|
||||
assertThat(hint.getId()).isEqualTo("spring.bar.description");
|
||||
assertThat(hint.getValueHints()).hasSize(2);
|
||||
ValueHint valueHint = hint.getValueHints().get(0);
|
||||
assertThat(valueHint.getValue()).isEqualTo("one");
|
||||
assertThat(valueHint.getDescription()).isEqualTo("One.");
|
||||
ValueHint valueHint2 = hint.getValueHints().get(1);
|
||||
assertThat(valueHint2.getValue()).isEqualTo("two");
|
||||
assertThat(valueHint2.getDescription()).isEqualTo(null);
|
||||
|
||||
assertThat(hint.getValueProviders()).hasSize(2);
|
||||
ValueProvider valueProvider = hint.getValueProviders().get(0);
|
||||
assertThat(valueProvider.getName()).isEqualTo("handle-as");
|
||||
assertThat(valueProvider.getParameters()).hasSize(1);
|
||||
assertThat(valueProvider.getParameters().get("target"))
|
||||
.isEqualTo(String.class.getName());
|
||||
ValueProvider valueProvider2 = hint.getValueProviders().get(1);
|
||||
assertThat(valueProvider2.getName()).isEqualTo("any");
|
||||
assertThat(valueProvider2.getParameters()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rootMetadata() throws IOException {
|
||||
RawConfigurationMetadata rawMetadata = readFor("root");
|
||||
List<ConfigurationMetadataSource> sources = rawMetadata.getSources();
|
||||
assertThat(sources).isEmpty();
|
||||
List<ConfigurationMetadataItem> items = rawMetadata.getItems();
|
||||
assertThat(items).hasSize(2);
|
||||
ConfigurationMetadataItem item = items.get(0);
|
||||
assertProperty(item, "spring.root.name", "spring.root.name", String.class, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deprecatedMetadata() throws IOException {
|
||||
RawConfigurationMetadata rawMetadata = readFor("deprecated");
|
||||
List<ConfigurationMetadataItem> items = rawMetadata.getItems();
|
||||
assertThat(items).hasSize(5);
|
||||
|
||||
ConfigurationMetadataItem item = items.get(0);
|
||||
assertProperty(item, "server.port", "server.port", Integer.class, null);
|
||||
assertThat(item.isDeprecated()).isTrue();
|
||||
assertThat(item.getDeprecation().getReason())
|
||||
.isEqualTo("Server namespace has moved to spring.server");
|
||||
assertThat(item.getDeprecation().getReplacement())
|
||||
.isEqualTo("server.spring.port");
|
||||
assertThat(item.getDeprecation().getLevel()).isEqualTo(Deprecation.Level.WARNING);
|
||||
|
||||
ConfigurationMetadataItem item2 = items.get(1);
|
||||
assertProperty(item2, "server.cluster-name", "server.cluster-name", String.class,
|
||||
null);
|
||||
assertThat(item2.isDeprecated()).isTrue();
|
||||
assertThat(item2.getDeprecation().getReason()).isNull();
|
||||
assertThat(item2.getDeprecation().getReplacement()).isNull();
|
||||
assertThat(item.getDeprecation().getLevel()).isEqualTo(Deprecation.Level.WARNING);
|
||||
|
||||
ConfigurationMetadataItem item3 = items.get(2);
|
||||
assertProperty(item3, "spring.server.name", "spring.server.name", String.class,
|
||||
null);
|
||||
assertThat(item3.isDeprecated()).isFalse();
|
||||
assertThat(item3.getDeprecation()).isEqualTo(null);
|
||||
|
||||
ConfigurationMetadataItem item4 = items.get(3);
|
||||
assertProperty(item4, "spring.server-name", "spring.server-name", String.class,
|
||||
null);
|
||||
assertThat(item4.isDeprecated()).isTrue();
|
||||
assertThat(item4.getDeprecation().getReason()).isNull();
|
||||
assertThat(item4.getDeprecation().getReplacement())
|
||||
.isEqualTo("spring.server.name");
|
||||
assertThat(item4.getDeprecation().getLevel()).isEqualTo(Deprecation.Level.ERROR);
|
||||
|
||||
ConfigurationMetadataItem item5 = items.get(4);
|
||||
assertProperty(item5, "spring.server-name2", "spring.server-name2", String.class,
|
||||
null);
|
||||
assertThat(item5.isDeprecated()).isTrue();
|
||||
assertThat(item5.getDeprecation().getReason()).isNull();
|
||||
assertThat(item5.getDeprecation().getReplacement())
|
||||
.isEqualTo("spring.server.name");
|
||||
assertThat(item5.getDeprecation().getLevel())
|
||||
.isEqualTo(Deprecation.Level.WARNING);
|
||||
}
|
||||
|
||||
RawConfigurationMetadata readFor(String path) throws IOException {
|
||||
return this.reader.read(getInputStreamFor(path), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "spring.bar",
|
||||
"type": "org.acme.Bar",
|
||||
"sourceType": "org.acme.config.BarApp",
|
||||
"sourceMethod": "bar()",
|
||||
"description": "This is Bar."
|
||||
},
|
||||
{
|
||||
"name": "spring.bar",
|
||||
"type": "org.springframework.boot.BarProperties"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.bar.name",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Bar"
|
||||
},
|
||||
{
|
||||
"name": "spring.bar.description",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Bar",
|
||||
"description": "Bar description.",
|
||||
"defaultValue": "BarFoo"
|
||||
},
|
||||
{
|
||||
"name": "spring.bar.name",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.springframework.boot.BarProperties"
|
||||
},
|
||||
{
|
||||
"name": "spring.bar.counter",
|
||||
"type": "java.lang.Integer",
|
||||
"sourceType": "org.springframework.boot.BarProperties",
|
||||
"defaultValue": 0
|
||||
}
|
||||
],
|
||||
"hints": [
|
||||
{
|
||||
"name": "spring.bar.description",
|
||||
"values": [
|
||||
{
|
||||
"value": "one",
|
||||
"description": "One."
|
||||
},
|
||||
{
|
||||
"value": "two"
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
{
|
||||
"name": "handle-as",
|
||||
"parameters": {
|
||||
"target": "java.lang.String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "any"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "server.port",
|
||||
"type": "java.lang.Integer",
|
||||
"deprecation": {
|
||||
"reason": "Server namespace has moved to spring.server",
|
||||
"replacement": "server.spring.port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "server.cluster-name",
|
||||
"type": "java.lang.String",
|
||||
"deprecated": true
|
||||
},
|
||||
{
|
||||
"name": "spring.server.name",
|
||||
"type": "java.lang.String",
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "spring.server-name",
|
||||
"type": "java.lang.String",
|
||||
"deprecation": {
|
||||
"level": "error",
|
||||
"replacement": "spring.server.name"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "spring.server-name2",
|
||||
"type": "java.lang.String",
|
||||
"deprecation": {
|
||||
"level": "INVALID",
|
||||
"replacement": "spring.server.name"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "org.acme.Foo",
|
||||
"sourceType": "org.acme.config.FooApp",
|
||||
"sourceMethod": "foo()",
|
||||
"description": "This is Foo."
|
||||
},
|
||||
{
|
||||
"name": "",
|
||||
"type": "org.acme.Bar",
|
||||
"sourceType": "org.acme.config.FooApp",
|
||||
"sourceMethod": "bar()",
|
||||
"description": "This is Bar."
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "name",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Foo"
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"foo": "bar"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "spring.foo",
|
||||
"type": "org.acme.Foo",
|
||||
"sourceType": "org.acme.config.FooApp",
|
||||
"sourceMethod": "foo()",
|
||||
"description": "This is Foo."
|
||||
},
|
||||
{
|
||||
"name": "spring.foo",
|
||||
"type": "org.springframework.boot.FooProperties"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.foo.name",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Foo"
|
||||
},
|
||||
{
|
||||
"name": "spring.foo.description",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Foo",
|
||||
"description": "Foo description.",
|
||||
"defaultValue": "FooBar"
|
||||
},
|
||||
{
|
||||
"name": "spring.foo.name",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.springframework.boot.FooProperties"
|
||||
},
|
||||
{
|
||||
"name": "spring.foo.counter",
|
||||
"type": "java.lang.Integer",
|
||||
"sourceType": "org.springframework.boot.FooProperties",
|
||||
"defaultValue": 0
|
||||
}
|
||||
],
|
||||
"hints": [
|
||||
{
|
||||
"name": "spring.foo.counter",
|
||||
"values": [
|
||||
{
|
||||
"value": 42,
|
||||
"description": "Because that's the answer to any question, choose it. \nReally."
|
||||
}
|
||||
],
|
||||
"providers": [
|
||||
{
|
||||
"name": "handle-as",
|
||||
"parameters": {
|
||||
"target": "java.lang.Integer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "spring.foo",
|
||||
"type": "org.acme.Foo2",
|
||||
"sourceType": "org.acme.config.FooApp",
|
||||
"sourceMethod": "foo2()",
|
||||
"description": "This is Foo2."
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.foo.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"sourceType": "org.acme.Foo2"
|
||||
},
|
||||
{
|
||||
"name": "spring.foo.type",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Foo2"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Invalid"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "spring.map",
|
||||
"type": "org.acme.Map",
|
||||
"sourceType": "org.acme.config.MapApp",
|
||||
"sourceMethod": "map()",
|
||||
"description": "This is Map."
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.map.first",
|
||||
"type": "java.util.Map<String,String>",
|
||||
"sourceType": "org.acme.Map"
|
||||
},
|
||||
{
|
||||
"name": "spring.map.second",
|
||||
"type": "java.util.Map<String,String>",
|
||||
"sourceType": "org.acme.Map"
|
||||
},
|
||||
{
|
||||
"name": "spring.map.keys",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Map"
|
||||
},
|
||||
{
|
||||
"name": "spring.map.values",
|
||||
"type": "java.lang.String",
|
||||
"sourceType": "org.acme.Map"
|
||||
}
|
||||
],
|
||||
"hints": [
|
||||
{
|
||||
"name": "spring.map.first.keys",
|
||||
"values": [
|
||||
{
|
||||
"value": "one",
|
||||
"description": "First."
|
||||
},
|
||||
{
|
||||
"value": "two",
|
||||
"description": "Second."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spring.map.second.values",
|
||||
"values": [
|
||||
{
|
||||
"value": "42",
|
||||
"description": "Choose me."
|
||||
},
|
||||
{
|
||||
"value": "24"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spring.map.keys",
|
||||
"providers": [
|
||||
{
|
||||
"name": "any"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "spring.map.values",
|
||||
"providers": [
|
||||
{
|
||||
"name": "handle-as",
|
||||
"parameters": {
|
||||
"target": "java.lang.Integer"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "spring.root.name",
|
||||
"type": "java.lang.String"
|
||||
},
|
||||
{
|
||||
"name": "spring.root2.name"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-tools</artifactId>
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<name>Spring Boot Configuration Processor</name>
|
||||
<description>Spring Boot Configuration Processor</description>
|
||||
<url>http://projects.spring.io/spring-boot/</url>
|
||||
<organization>
|
||||
<name>Pivotal Software, Inc.</name>
|
||||
<url>http://www.spring.io</url>
|
||||
</organization>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- Compile (should stick to the bare minimum) -->
|
||||
<dependency>
|
||||
<groupId>com.vaadin.external.google</groupId>
|
||||
<artifactId>android-json</artifactId>
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test-support</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- Ensure own annotation processor doesn't kick in -->
|
||||
<proc>none</proc>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,579 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.tools.Diagnostic.Kind;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.FieldValuesParser;
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.javac.JavaCompilerFieldValuesParser;
|
||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||
import org.springframework.boot.configurationprocessor.metadata.InvalidConfigurationMetadataException;
|
||||
import org.springframework.boot.configurationprocessor.metadata.ItemDeprecation;
|
||||
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
|
||||
|
||||
/**
|
||||
* Annotation {@link Processor} that writes meta-data file for
|
||||
* {@code @ConfigurationProperties}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Kris De Volder
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@SupportedAnnotationTypes({ "*" })
|
||||
public class ConfigurationMetadataAnnotationProcessor extends AbstractProcessor {
|
||||
|
||||
static final String CONFIGURATION_PROPERTIES_ANNOTATION = "org.springframework.boot."
|
||||
+ "context.properties.ConfigurationProperties";
|
||||
|
||||
static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot."
|
||||
+ "context.properties.NestedConfigurationProperty";
|
||||
|
||||
static final String DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot."
|
||||
+ "context.properties.DeprecatedConfigurationProperty";
|
||||
|
||||
static final String ENDPOINT_ANNOTATION = "org.springframework.boot.actuate."
|
||||
+ "endpoint.annotation.Endpoint";
|
||||
|
||||
static final String LOMBOK_DATA_ANNOTATION = "lombok.Data";
|
||||
|
||||
static final String LOMBOK_GETTER_ANNOTATION = "lombok.Getter";
|
||||
|
||||
static final String LOMBOK_SETTER_ANNOTATION = "lombok.Setter";
|
||||
|
||||
private MetadataStore metadataStore;
|
||||
|
||||
private MetadataCollector metadataCollector;
|
||||
|
||||
private TypeUtils typeUtils;
|
||||
|
||||
private FieldValuesParser fieldValuesParser;
|
||||
|
||||
private TypeExcludeFilter typeExcludeFilter = new TypeExcludeFilter();
|
||||
|
||||
protected String configurationPropertiesAnnotation() {
|
||||
return CONFIGURATION_PROPERTIES_ANNOTATION;
|
||||
}
|
||||
|
||||
protected String nestedConfigurationPropertyAnnotation() {
|
||||
return NESTED_CONFIGURATION_PROPERTY_ANNOTATION;
|
||||
}
|
||||
|
||||
protected String deprecatedConfigurationPropertyAnnotation() {
|
||||
return DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION;
|
||||
}
|
||||
|
||||
protected String endpointAnnotation() {
|
||||
return ENDPOINT_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latestSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment env) {
|
||||
super.init(env);
|
||||
this.typeUtils = new TypeUtils(env);
|
||||
this.metadataStore = new MetadataStore(env);
|
||||
this.metadataCollector = new MetadataCollector(env,
|
||||
this.metadataStore.readMetadata());
|
||||
try {
|
||||
this.fieldValuesParser = new JavaCompilerFieldValuesParser(env);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
this.fieldValuesParser = FieldValuesParser.NONE;
|
||||
logWarning("Field value processing of @ConfigurationProperty meta-data is "
|
||||
+ "not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations,
|
||||
RoundEnvironment roundEnv) {
|
||||
this.metadataCollector.processing(roundEnv);
|
||||
Elements elementUtils = this.processingEnv.getElementUtils();
|
||||
TypeElement annotationType = elementUtils
|
||||
.getTypeElement(configurationPropertiesAnnotation());
|
||||
if (annotationType != null) { // Is @ConfigurationProperties available
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) {
|
||||
processElement(element);
|
||||
}
|
||||
}
|
||||
TypeElement endpointType = elementUtils.getTypeElement(endpointAnnotation());
|
||||
if (endpointType != null) { // Is @Endpoint available
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(endpointType)) {
|
||||
processEndpoint(element);
|
||||
}
|
||||
}
|
||||
if (roundEnv.processingOver()) {
|
||||
try {
|
||||
writeMetaData();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to write metadata", ex);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processElement(Element element) {
|
||||
try {
|
||||
AnnotationMirror annotation = getAnnotation(element,
|
||||
configurationPropertiesAnnotation());
|
||||
if (annotation != null) {
|
||||
String prefix = getPrefix(annotation);
|
||||
if (element instanceof TypeElement) {
|
||||
processAnnotatedTypeElement(prefix, (TypeElement) element);
|
||||
}
|
||||
else if (element instanceof ExecutableElement) {
|
||||
processExecutableElement(prefix, (ExecutableElement) element);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Error processing configuration meta-data on " + element, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void processAnnotatedTypeElement(String prefix, TypeElement element) {
|
||||
String type = this.typeUtils.getQualifiedName(element);
|
||||
this.metadataCollector.add(ItemMetadata.newGroup(prefix, type, type, null));
|
||||
processTypeElement(prefix, element, null);
|
||||
}
|
||||
|
||||
private void processExecutableElement(String prefix, ExecutableElement element) {
|
||||
if (element.getModifiers().contains(Modifier.PUBLIC)
|
||||
&& (TypeKind.VOID != element.getReturnType().getKind())) {
|
||||
Element returns = this.processingEnv.getTypeUtils()
|
||||
.asElement(element.getReturnType());
|
||||
if (returns instanceof TypeElement) {
|
||||
ItemMetadata group = ItemMetadata.newGroup(prefix,
|
||||
this.typeUtils.getQualifiedName(returns),
|
||||
this.typeUtils.getQualifiedName(element.getEnclosingElement()),
|
||||
element.toString());
|
||||
if (this.metadataCollector.hasSimilarGroup(group)) {
|
||||
this.processingEnv.getMessager().printMessage(Kind.ERROR,
|
||||
"Duplicate `@ConfigurationProperties` definition for prefix '"
|
||||
+ prefix + "'",
|
||||
element);
|
||||
}
|
||||
else {
|
||||
this.metadataCollector.add(group);
|
||||
processTypeElement(prefix, (TypeElement) returns, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processTypeElement(String prefix, TypeElement element,
|
||||
ExecutableElement source) {
|
||||
TypeElementMembers members = new TypeElementMembers(this.processingEnv,
|
||||
this.fieldValuesParser, element);
|
||||
Map<String, Object> fieldValues = members.getFieldValues();
|
||||
processSimpleTypes(prefix, element, source, members, fieldValues);
|
||||
processSimpleLombokTypes(prefix, element, source, members, fieldValues);
|
||||
processNestedTypes(prefix, element, source, members);
|
||||
processNestedLombokTypes(prefix, element, source, members);
|
||||
}
|
||||
|
||||
private void processSimpleTypes(String prefix, TypeElement element,
|
||||
ExecutableElement source, TypeElementMembers members,
|
||||
Map<String, Object> fieldValues) {
|
||||
for (Map.Entry<String, ExecutableElement> entry : members.getPublicGetters()
|
||||
.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
ExecutableElement getter = entry.getValue();
|
||||
TypeMirror returnType = getter.getReturnType();
|
||||
ExecutableElement setter = members.getPublicSetter(name, returnType);
|
||||
VariableElement field = members.getFields().get(name);
|
||||
Element returnTypeElement = this.processingEnv.getTypeUtils()
|
||||
.asElement(returnType);
|
||||
boolean isExcluded = this.typeExcludeFilter.isExcluded(returnType);
|
||||
boolean isNested = isNested(returnTypeElement, field, element);
|
||||
boolean isCollection = this.typeUtils.isCollectionOrMap(returnType);
|
||||
if (!isExcluded && !isNested && (setter != null || isCollection)) {
|
||||
String dataType = this.typeUtils.getType(returnType);
|
||||
String sourceType = this.typeUtils.getQualifiedName(element);
|
||||
String description = this.typeUtils.getJavaDoc(field);
|
||||
Object defaultValue = fieldValues.get(name);
|
||||
boolean deprecated = isDeprecated(getter) || isDeprecated(setter)
|
||||
|| isDeprecated(source);
|
||||
this.metadataCollector.add(ItemMetadata.newProperty(prefix, name,
|
||||
dataType, sourceType, null, description, defaultValue,
|
||||
(deprecated ? getItemDeprecation(getter) : null)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ItemDeprecation getItemDeprecation(ExecutableElement getter) {
|
||||
AnnotationMirror annotation = getAnnotation(getter,
|
||||
deprecatedConfigurationPropertyAnnotation());
|
||||
String reason = null;
|
||||
String replacement = null;
|
||||
if (annotation != null) {
|
||||
Map<String, Object> elementValues = getAnnotationElementValues(annotation);
|
||||
reason = (String) elementValues.get("reason");
|
||||
replacement = (String) elementValues.get("replacement");
|
||||
}
|
||||
return new ItemDeprecation(("".equals(reason) ? null : reason),
|
||||
("".equals(replacement) ? null : replacement));
|
||||
}
|
||||
|
||||
private void processSimpleLombokTypes(String prefix, TypeElement element,
|
||||
ExecutableElement source, TypeElementMembers members,
|
||||
Map<String, Object> fieldValues) {
|
||||
for (Map.Entry<String, VariableElement> entry : members.getFields().entrySet()) {
|
||||
String name = entry.getKey();
|
||||
VariableElement field = entry.getValue();
|
||||
if (!isLombokField(field, element)) {
|
||||
continue;
|
||||
}
|
||||
TypeMirror returnType = field.asType();
|
||||
Element returnTypeElement = this.processingEnv.getTypeUtils()
|
||||
.asElement(returnType);
|
||||
boolean isExcluded = this.typeExcludeFilter.isExcluded(returnType);
|
||||
boolean isNested = isNested(returnTypeElement, field, element);
|
||||
boolean isCollection = this.typeUtils.isCollectionOrMap(returnType);
|
||||
boolean hasSetter = hasLombokSetter(field, element);
|
||||
if (!isExcluded && !isNested && (hasSetter || isCollection)) {
|
||||
String dataType = this.typeUtils.getType(returnType);
|
||||
String sourceType = this.typeUtils.getQualifiedName(element);
|
||||
String description = this.typeUtils.getJavaDoc(field);
|
||||
Object defaultValue = fieldValues.get(name);
|
||||
boolean deprecated = isDeprecated(field) || isDeprecated(source);
|
||||
this.metadataCollector.add(ItemMetadata.newProperty(prefix, name,
|
||||
dataType, sourceType, null, description, defaultValue,
|
||||
(deprecated ? new ItemDeprecation() : null)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processNestedTypes(String prefix, TypeElement element,
|
||||
ExecutableElement source, TypeElementMembers members) {
|
||||
for (Map.Entry<String, ExecutableElement> entry : members.getPublicGetters()
|
||||
.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
ExecutableElement getter = entry.getValue();
|
||||
VariableElement field = members.getFields().get(name);
|
||||
processNestedType(prefix, element, source, name, getter, field,
|
||||
getter.getReturnType());
|
||||
}
|
||||
}
|
||||
|
||||
private void processNestedLombokTypes(String prefix, TypeElement element,
|
||||
ExecutableElement source, TypeElementMembers members) {
|
||||
for (Map.Entry<String, VariableElement> entry : members.getFields().entrySet()) {
|
||||
String name = entry.getKey();
|
||||
VariableElement field = entry.getValue();
|
||||
if (isLombokField(field, element)) {
|
||||
ExecutableElement getter = members.getPublicGetter(name, field.asType());
|
||||
processNestedType(prefix, element, source, name, getter, field,
|
||||
field.asType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLombokField(VariableElement field, TypeElement element) {
|
||||
return hasAnnotation(field, LOMBOK_GETTER_ANNOTATION)
|
||||
|| hasAnnotation(element, LOMBOK_GETTER_ANNOTATION)
|
||||
|| hasAnnotation(element, LOMBOK_DATA_ANNOTATION);
|
||||
}
|
||||
|
||||
private boolean hasLombokSetter(VariableElement field, TypeElement element) {
|
||||
return !field.getModifiers().contains(Modifier.FINAL)
|
||||
&& (hasAnnotation(field, LOMBOK_SETTER_ANNOTATION)
|
||||
|| hasAnnotation(element, LOMBOK_SETTER_ANNOTATION)
|
||||
|| hasAnnotation(element, LOMBOK_DATA_ANNOTATION));
|
||||
}
|
||||
|
||||
private void processNestedType(String prefix, TypeElement element,
|
||||
ExecutableElement source, String name, ExecutableElement getter,
|
||||
VariableElement field, TypeMirror returnType) {
|
||||
Element returnElement = this.processingEnv.getTypeUtils().asElement(returnType);
|
||||
boolean isNested = isNested(returnElement, field, element);
|
||||
AnnotationMirror annotation = getAnnotation(getter,
|
||||
configurationPropertiesAnnotation());
|
||||
if (returnElement instanceof TypeElement && annotation == null && isNested) {
|
||||
String nestedPrefix = ConfigurationMetadata.nestedPrefix(prefix, name);
|
||||
this.metadataCollector.add(ItemMetadata.newGroup(nestedPrefix,
|
||||
this.typeUtils.getQualifiedName(returnElement),
|
||||
this.typeUtils.getQualifiedName(element),
|
||||
(getter == null ? null : getter.toString())));
|
||||
processTypeElement(nestedPrefix, (TypeElement) returnElement, source);
|
||||
}
|
||||
}
|
||||
|
||||
private void processEndpoint(Element element) {
|
||||
try {
|
||||
AnnotationMirror annotation = getAnnotation(element, endpointAnnotation());
|
||||
if (element instanceof TypeElement) {
|
||||
processEndpoint(annotation, (TypeElement) element);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Error processing configuration meta-data on " + element, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void processEndpoint(AnnotationMirror annotation, TypeElement element) {
|
||||
Map<String, Object> elementValues = getAnnotationElementValues(annotation);
|
||||
String endpointId = (String) elementValues.get("id");
|
||||
if (endpointId == null || "".equals(endpointId)) {
|
||||
return; // Can't process that endpoint
|
||||
}
|
||||
Boolean enabledByDefault = determineEnabledByDefault(
|
||||
elementValues.get("defaultEnablement"));
|
||||
String type = this.typeUtils.getQualifiedName(element);
|
||||
this.metadataCollector
|
||||
.add(ItemMetadata.newGroup(endpointKey(endpointId), type, type, null));
|
||||
this.metadataCollector.add(ItemMetadata.newProperty(endpointKey(endpointId),
|
||||
"enabled", Boolean.class.getName(), type, null,
|
||||
String.format("Enable the %s endpoint.", endpointId), enabledByDefault,
|
||||
null));
|
||||
this.metadataCollector.add(ItemMetadata.newProperty(endpointKey(endpointId),
|
||||
"cache.time-to-live", Long.class.getName(), type, null,
|
||||
"Maximum time in milliseconds that a response can be cached.", 0, null));
|
||||
EndpointExposure endpointTypes = EndpointExposure
|
||||
.parse(elementValues.get("exposure"));
|
||||
if (endpointTypes.hasJmx()) {
|
||||
this.metadataCollector.add(ItemMetadata.newProperty(
|
||||
endpointKey(endpointId + ".jmx"), "enabled", Boolean.class.getName(),
|
||||
type, null,
|
||||
String.format("Expose the %s endpoint as a JMX MBean.", endpointId),
|
||||
enabledByDefault, null));
|
||||
}
|
||||
if (endpointTypes.hasWeb()) {
|
||||
this.metadataCollector.add(ItemMetadata.newProperty(
|
||||
endpointKey(endpointId + ".web"), "enabled", Boolean.class.getName(),
|
||||
type, null, String.format("Expose the %s endpoint as a Web endpoint.",
|
||||
endpointId),
|
||||
enabledByDefault, null));
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean determineEnabledByDefault(Object defaultEnablement) {
|
||||
if (defaultEnablement != null) {
|
||||
String value = String.valueOf(defaultEnablement);
|
||||
if ("ENABLED".equals(value)) {
|
||||
return true;
|
||||
}
|
||||
if ("DISABLED".equals(value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String endpointKey(String suffix) {
|
||||
return "endpoints." + suffix;
|
||||
}
|
||||
|
||||
private boolean isNested(Element returnType, VariableElement field,
|
||||
TypeElement element) {
|
||||
if (hasAnnotation(field, nestedConfigurationPropertyAnnotation())) {
|
||||
return true;
|
||||
}
|
||||
return (isParentTheSame(returnType, element))
|
||||
&& returnType.getKind() != ElementKind.ENUM;
|
||||
}
|
||||
|
||||
private boolean isParentTheSame(Element returnType, TypeElement element) {
|
||||
if (returnType == null || element == null) {
|
||||
return false;
|
||||
}
|
||||
return getTopLevelType(returnType).equals(getTopLevelType(element));
|
||||
}
|
||||
|
||||
private Element getTopLevelType(Element element) {
|
||||
if (!(element.getEnclosingElement() instanceof TypeElement)) {
|
||||
return element;
|
||||
}
|
||||
return getTopLevelType(element.getEnclosingElement());
|
||||
}
|
||||
|
||||
private boolean isDeprecated(Element element) {
|
||||
if (isElementDeprecated(element)) {
|
||||
return true;
|
||||
}
|
||||
if (element instanceof VariableElement || element instanceof ExecutableElement) {
|
||||
return isElementDeprecated(element.getEnclosingElement());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isElementDeprecated(Element element) {
|
||||
return hasAnnotation(element, "java.lang.Deprecated")
|
||||
|| hasAnnotation(element, deprecatedConfigurationPropertyAnnotation());
|
||||
}
|
||||
|
||||
private boolean hasAnnotation(Element element, String type) {
|
||||
return getAnnotation(element, type) != null;
|
||||
}
|
||||
|
||||
private AnnotationMirror getAnnotation(Element element, String type) {
|
||||
if (element != null) {
|
||||
for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
|
||||
if (type.equals(annotation.getAnnotationType().toString())) {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getPrefix(AnnotationMirror annotation) {
|
||||
Map<String, Object> elementValues = getAnnotationElementValues(annotation);
|
||||
Object prefix = elementValues.get("prefix");
|
||||
if (prefix != null && !"".equals(prefix)) {
|
||||
return (String) prefix;
|
||||
}
|
||||
Object value = elementValues.get("value");
|
||||
if (value != null && !"".equals(value)) {
|
||||
return (String) value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, Object> getAnnotationElementValues(AnnotationMirror annotation) {
|
||||
Map<String, Object> values = new LinkedHashMap<>();
|
||||
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotation
|
||||
.getElementValues().entrySet()) {
|
||||
values.put(entry.getKey().getSimpleName().toString(),
|
||||
entry.getValue().getValue());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
protected ConfigurationMetadata writeMetaData() throws Exception {
|
||||
ConfigurationMetadata metadata = this.metadataCollector.getMetadata();
|
||||
metadata = mergeAdditionalMetadata(metadata);
|
||||
if (!metadata.getItems().isEmpty()) {
|
||||
this.metadataStore.writeMetadata(metadata);
|
||||
return metadata;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConfigurationMetadata mergeAdditionalMetadata(
|
||||
ConfigurationMetadata metadata) {
|
||||
try {
|
||||
ConfigurationMetadata merged = new ConfigurationMetadata(metadata);
|
||||
merged.merge(this.metadataStore.readAdditionalMetadata());
|
||||
return merged;
|
||||
}
|
||||
catch (FileNotFoundException ex) {
|
||||
// No additional metadata
|
||||
}
|
||||
catch (InvalidConfigurationMetadataException ex) {
|
||||
log(ex.getKind(), ex.getMessage());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logWarning("Unable to merge additional metadata");
|
||||
logWarning(getStackTrace(ex));
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private String getStackTrace(Exception ex) {
|
||||
StringWriter writer = new StringWriter();
|
||||
ex.printStackTrace(new PrintWriter(writer, true));
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
private void logWarning(String msg) {
|
||||
log(Kind.WARNING, msg);
|
||||
}
|
||||
|
||||
private void log(Kind kind, String msg) {
|
||||
this.processingEnv.getMessager().printMessage(kind, msg);
|
||||
}
|
||||
|
||||
private static class EndpointExposure {
|
||||
|
||||
private static final List<String> ALL = Arrays.asList("JMX", "WEB");
|
||||
|
||||
private final List<String> types;
|
||||
|
||||
EndpointExposure(List<String> types) {
|
||||
this.types = types;
|
||||
}
|
||||
|
||||
static EndpointExposure parse(Object exposureAttribute) {
|
||||
List<AnnotationValue> values = asAnnotationValues(exposureAttribute);
|
||||
if (values.isEmpty()) {
|
||||
return new EndpointExposure(ALL);
|
||||
}
|
||||
return new EndpointExposure(
|
||||
values.stream().map(EndpointExposure::getValueAttribute)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<AnnotationValue> asAnnotationValues(Object typesAttribute) {
|
||||
if (!(typesAttribute instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return (List<AnnotationValue>) typesAttribute;
|
||||
}
|
||||
|
||||
private static String getValueAttribute(AnnotationValue value) {
|
||||
return ((VariableElement) value.getValue()).getSimpleName().toString();
|
||||
}
|
||||
|
||||
public boolean hasJmx() {
|
||||
return this.types.contains("JMX");
|
||||
}
|
||||
|
||||
public boolean hasWeb() {
|
||||
return this.types.contains("WEB");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
|
||||
|
||||
/**
|
||||
* Used by {@link ConfigurationMetadataAnnotationProcessor} to collect
|
||||
* {@link ConfigurationMetadata}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Kris De Volder
|
||||
* @since 1.2.2
|
||||
*/
|
||||
public class MetadataCollector {
|
||||
|
||||
private final Set<ItemMetadata> metadataItems = new LinkedHashSet<>();
|
||||
|
||||
private final ProcessingEnvironment processingEnvironment;
|
||||
|
||||
private final ConfigurationMetadata previousMetadata;
|
||||
|
||||
private final TypeUtils typeUtils;
|
||||
|
||||
private final Set<String> processedSourceTypes = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Creates a new {@code MetadataProcessor} instance.
|
||||
* @param processingEnvironment The processing environment of the build
|
||||
* @param previousMetadata Any previous metadata or {@code null}
|
||||
*/
|
||||
public MetadataCollector(ProcessingEnvironment processingEnvironment,
|
||||
ConfigurationMetadata previousMetadata) {
|
||||
this.processingEnvironment = processingEnvironment;
|
||||
this.previousMetadata = previousMetadata;
|
||||
this.typeUtils = new TypeUtils(processingEnvironment);
|
||||
}
|
||||
|
||||
public void processing(RoundEnvironment roundEnv) {
|
||||
for (Element element : roundEnv.getRootElements()) {
|
||||
markAsProcessed(element);
|
||||
}
|
||||
}
|
||||
|
||||
private void markAsProcessed(Element element) {
|
||||
if (element instanceof TypeElement) {
|
||||
this.processedSourceTypes.add(this.typeUtils.getQualifiedName(element));
|
||||
}
|
||||
}
|
||||
|
||||
public void add(ItemMetadata metadata) {
|
||||
this.metadataItems.add(metadata);
|
||||
}
|
||||
|
||||
public boolean hasSimilarGroup(ItemMetadata metadata) {
|
||||
if (!metadata.isOfItemType(ItemMetadata.ItemType.GROUP)) {
|
||||
throw new IllegalStateException("item " + metadata + " must be a group");
|
||||
}
|
||||
for (ItemMetadata existing : this.metadataItems) {
|
||||
if (existing.isOfItemType(ItemMetadata.ItemType.GROUP)
|
||||
&& existing.getName().equals(metadata.getName())
|
||||
&& existing.getType().equals(metadata.getType())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ConfigurationMetadata getMetadata() {
|
||||
ConfigurationMetadata metadata = new ConfigurationMetadata();
|
||||
for (ItemMetadata item : this.metadataItems) {
|
||||
metadata.add(item);
|
||||
}
|
||||
if (this.previousMetadata != null) {
|
||||
List<ItemMetadata> items = this.previousMetadata.getItems();
|
||||
for (ItemMetadata item : items) {
|
||||
if (shouldBeMerged(item)) {
|
||||
metadata.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private boolean shouldBeMerged(ItemMetadata itemMetadata) {
|
||||
String sourceType = itemMetadata.getSourceType();
|
||||
return (sourceType != null && !deletedInCurrentBuild(sourceType)
|
||||
&& !processedInCurrentBuild(sourceType));
|
||||
}
|
||||
|
||||
private boolean deletedInCurrentBuild(String sourceType) {
|
||||
return this.processingEnvironment.getElementUtils()
|
||||
.getTypeElement(sourceType) == null;
|
||||
}
|
||||
|
||||
private boolean processedInCurrentBuild(String sourceType) {
|
||||
return this.processedSourceTypes.contains(sourceType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||
import org.springframework.boot.configurationprocessor.metadata.InvalidConfigurationMetadataException;
|
||||
import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller;
|
||||
|
||||
/**
|
||||
* A {@code MetadataStore} is responsible for the storage of metadata on the filesystem.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.2.2
|
||||
*/
|
||||
public class MetadataStore {
|
||||
|
||||
static final String METADATA_PATH = "META-INF/spring-configuration-metadata.json";
|
||||
|
||||
private static final String ADDITIONAL_METADATA_PATH = "META-INF/additional-spring-configuration-metadata.json";
|
||||
|
||||
private static final String RESOURCES_FOLDER = "resources";
|
||||
|
||||
private static final String CLASSES_FOLDER = "classes";
|
||||
|
||||
private final ProcessingEnvironment environment;
|
||||
|
||||
public MetadataStore(ProcessingEnvironment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public ConfigurationMetadata readMetadata() {
|
||||
try {
|
||||
return readMetadata(getMetadataResource().openInputStream());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMetadata(ConfigurationMetadata metadata) throws IOException {
|
||||
if (!metadata.getItems().isEmpty()) {
|
||||
try (OutputStream outputStream = createMetadataResource()
|
||||
.openOutputStream()) {
|
||||
new JsonMarshaller().write(metadata, outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurationMetadata readAdditionalMetadata() throws IOException {
|
||||
return readMetadata(getAdditionalMetadataStream());
|
||||
}
|
||||
|
||||
private ConfigurationMetadata readMetadata(InputStream in) throws IOException {
|
||||
try {
|
||||
return new JsonMarshaller().read(in);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new InvalidConfigurationMetadataException(
|
||||
"Invalid additional meta-data in '" + METADATA_PATH + "': "
|
||||
+ ex.getMessage(),
|
||||
Diagnostic.Kind.ERROR);
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
private FileObject getMetadataResource() throws IOException {
|
||||
FileObject resource = this.environment.getFiler()
|
||||
.getResource(StandardLocation.CLASS_OUTPUT, "", METADATA_PATH);
|
||||
return resource;
|
||||
}
|
||||
|
||||
private FileObject createMetadataResource() throws IOException {
|
||||
FileObject resource = this.environment.getFiler()
|
||||
.createResource(StandardLocation.CLASS_OUTPUT, "", METADATA_PATH);
|
||||
return resource;
|
||||
}
|
||||
|
||||
private InputStream getAdditionalMetadataStream() throws IOException {
|
||||
// Most build systems will have copied the file to the class output location
|
||||
FileObject fileObject = this.environment.getFiler()
|
||||
.getResource(StandardLocation.CLASS_OUTPUT, "", ADDITIONAL_METADATA_PATH);
|
||||
File file = locateAdditionalMetadataFile(new File(fileObject.toUri()));
|
||||
return (file.exists() ? new FileInputStream(file)
|
||||
: fileObject.toUri().toURL().openStream());
|
||||
}
|
||||
|
||||
File locateAdditionalMetadataFile(File standardLocation) throws IOException {
|
||||
if (standardLocation.exists()) {
|
||||
return standardLocation;
|
||||
}
|
||||
return new File(locateGradleResourcesFolder(standardLocation),
|
||||
ADDITIONAL_METADATA_PATH);
|
||||
}
|
||||
|
||||
private File locateGradleResourcesFolder(File standardAdditionalMetadataLocation)
|
||||
throws FileNotFoundException {
|
||||
String path = standardAdditionalMetadataLocation.getPath();
|
||||
int index = path.lastIndexOf(CLASSES_FOLDER);
|
||||
if (index < 0) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
String buildFolderPath = path.substring(0, index);
|
||||
File classOutputLocation = standardAdditionalMetadataLocation.getParentFile()
|
||||
.getParentFile();
|
||||
return new File(buildFolderPath,
|
||||
RESOURCES_FOLDER + '/' + classOutputLocation.getName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.FieldValuesParser;
|
||||
|
||||
/**
|
||||
* Provides access to relevant {@link TypeElement} members.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class TypeElementMembers {
|
||||
|
||||
private static final String OBJECT_CLASS_NAME = Object.class.getName();
|
||||
|
||||
private final ProcessingEnvironment env;
|
||||
|
||||
private final TypeUtils typeUtils;
|
||||
|
||||
private final Map<String, VariableElement> fields = new LinkedHashMap<>();
|
||||
|
||||
private final Map<String, ExecutableElement> publicGetters = new LinkedHashMap<>();
|
||||
|
||||
private final Map<String, List<ExecutableElement>> publicSetters = new LinkedHashMap<>();
|
||||
|
||||
private final Map<String, Object> fieldValues = new LinkedHashMap<>();
|
||||
|
||||
private final FieldValuesParser fieldValuesParser;
|
||||
|
||||
TypeElementMembers(ProcessingEnvironment env, FieldValuesParser fieldValuesParser,
|
||||
TypeElement element) {
|
||||
this.env = env;
|
||||
this.typeUtils = new TypeUtils(this.env);
|
||||
this.fieldValuesParser = fieldValuesParser;
|
||||
process(element);
|
||||
}
|
||||
|
||||
private void process(TypeElement element) {
|
||||
for (ExecutableElement method : ElementFilter
|
||||
.methodsIn(element.getEnclosedElements())) {
|
||||
processMethod(method);
|
||||
}
|
||||
for (VariableElement field : ElementFilter
|
||||
.fieldsIn(element.getEnclosedElements())) {
|
||||
processField(field);
|
||||
}
|
||||
try {
|
||||
Map<String, Object> fieldValues = this.fieldValuesParser
|
||||
.getFieldValues(element);
|
||||
for (Map.Entry<String, Object> entry : fieldValues.entrySet()) {
|
||||
if (!this.fieldValues.containsKey(entry.getKey())) {
|
||||
this.fieldValues.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// continue
|
||||
}
|
||||
|
||||
Element superType = this.env.getTypeUtils().asElement(element.getSuperclass());
|
||||
if (superType != null && superType instanceof TypeElement
|
||||
&& !OBJECT_CLASS_NAME.equals(superType.toString())) {
|
||||
process((TypeElement) superType);
|
||||
}
|
||||
}
|
||||
|
||||
private void processMethod(ExecutableElement method) {
|
||||
if (method.getModifiers().contains(Modifier.PUBLIC)) {
|
||||
String name = method.getSimpleName().toString();
|
||||
if (isGetter(method) && !this.publicGetters.containsKey(name)) {
|
||||
this.publicGetters.put(getAccessorName(name), method);
|
||||
}
|
||||
else if (isSetter(method)) {
|
||||
String propertyName = getAccessorName(name);
|
||||
List<ExecutableElement> matchingSetters = this.publicSetters
|
||||
.get(propertyName);
|
||||
if (matchingSetters == null) {
|
||||
matchingSetters = new ArrayList<>();
|
||||
this.publicSetters.put(propertyName, matchingSetters);
|
||||
}
|
||||
TypeMirror paramType = method.getParameters().get(0).asType();
|
||||
if (getMatchingSetter(matchingSetters, paramType) == null) {
|
||||
matchingSetters.add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutableElement getMatchingSetter(List<ExecutableElement> candidates,
|
||||
TypeMirror type) {
|
||||
for (ExecutableElement candidate : candidates) {
|
||||
TypeMirror paramType = candidate.getParameters().get(0).asType();
|
||||
if (this.env.getTypeUtils().isSameType(paramType, type)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isGetter(ExecutableElement method) {
|
||||
String name = method.getSimpleName().toString();
|
||||
return ((name.startsWith("get") && name.length() > 3)
|
||||
|| (name.startsWith("is") && name.length() > 2))
|
||||
&& method.getParameters().isEmpty()
|
||||
&& (TypeKind.VOID != method.getReturnType().getKind());
|
||||
}
|
||||
|
||||
private boolean isSetter(ExecutableElement method) {
|
||||
final String name = method.getSimpleName().toString();
|
||||
return (name.startsWith("set") && name.length() > 3
|
||||
&& method.getParameters().size() == 1 && isSetterReturnType(method));
|
||||
}
|
||||
|
||||
private boolean isSetterReturnType(ExecutableElement method) {
|
||||
TypeMirror returnType = method.getReturnType();
|
||||
return (TypeKind.VOID == returnType.getKind() || this.env.getTypeUtils()
|
||||
.isSameType(method.getEnclosingElement().asType(), returnType));
|
||||
}
|
||||
|
||||
private String getAccessorName(String methodName) {
|
||||
String name = methodName.startsWith("is") ? methodName.substring(2)
|
||||
: methodName.substring(3);
|
||||
name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
|
||||
return name;
|
||||
}
|
||||
|
||||
private void processField(VariableElement field) {
|
||||
String name = field.getSimpleName().toString();
|
||||
if (!this.fields.containsKey(name)) {
|
||||
this.fields.put(name, field);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, VariableElement> getFields() {
|
||||
return Collections.unmodifiableMap(this.fields);
|
||||
}
|
||||
|
||||
public Map<String, ExecutableElement> getPublicGetters() {
|
||||
return Collections.unmodifiableMap(this.publicGetters);
|
||||
}
|
||||
|
||||
public ExecutableElement getPublicGetter(String name, TypeMirror type) {
|
||||
ExecutableElement candidate = this.publicGetters.get(name);
|
||||
if (candidate != null) {
|
||||
TypeMirror returnType = candidate.getReturnType();
|
||||
if (this.env.getTypeUtils().isSameType(returnType, type)) {
|
||||
return candidate;
|
||||
}
|
||||
TypeMirror alternative = this.typeUtils.getWrapperOrPrimitiveFor(type);
|
||||
if (alternative != null
|
||||
&& this.env.getTypeUtils().isSameType(returnType, alternative)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ExecutableElement getPublicSetter(String name, TypeMirror type) {
|
||||
List<ExecutableElement> candidates = this.publicSetters.get(name);
|
||||
if (candidates != null) {
|
||||
ExecutableElement matching = getMatchingSetter(candidates, type);
|
||||
if (matching != null) {
|
||||
return matching;
|
||||
}
|
||||
TypeMirror alternative = this.typeUtils.getWrapperOrPrimitiveFor(type);
|
||||
if (alternative != null) {
|
||||
return getMatchingSetter(candidates, alternative);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<String, Object> getFieldValues() {
|
||||
return Collections.unmodifiableMap(this.fieldValues);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
/**
|
||||
* Filter to exclude elements that don't make sense to process.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class TypeExcludeFilter {
|
||||
|
||||
private final Set<String> excludes = new HashSet<>();
|
||||
|
||||
TypeExcludeFilter() {
|
||||
add("com.zaxxer.hikari.IConnectionCustomizer");
|
||||
add("groovy.text.markup.MarkupTemplateEngine");
|
||||
add("java.io.Writer");
|
||||
add("java.io.PrintWriter");
|
||||
add("java.lang.ClassLoader");
|
||||
add("java.util.concurrent.ThreadFactory");
|
||||
add("javax.jms.XAConnectionFactory");
|
||||
add("javax.sql.DataSource");
|
||||
add("javax.sql.XADataSource");
|
||||
add("org.apache.tomcat.jdbc.pool.PoolConfiguration");
|
||||
add("org.apache.tomcat.jdbc.pool.Validator");
|
||||
add("org.flywaydb.core.api.callback.FlywayCallback");
|
||||
add("org.flywaydb.core.api.resolver.MigrationResolver");
|
||||
add("org.springframework.http.MediaType");
|
||||
}
|
||||
|
||||
private void add(String className) {
|
||||
this.excludes.add(className);
|
||||
}
|
||||
|
||||
public boolean isExcluded(TypeMirror type) {
|
||||
if (type == null) {
|
||||
return false;
|
||||
}
|
||||
String typeName = type.toString();
|
||||
if (typeName.endsWith("[]")) {
|
||||
typeName = typeName.substring(0, typeName.length() - 2);
|
||||
}
|
||||
return this.excludes.contains(typeName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
/**
|
||||
* Type Utilities.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class TypeUtils {
|
||||
|
||||
private static final Map<TypeKind, Class<?>> PRIMITIVE_WRAPPERS;
|
||||
|
||||
static {
|
||||
Map<TypeKind, Class<?>> wrappers = new HashMap<>();
|
||||
wrappers.put(TypeKind.BOOLEAN, Boolean.class);
|
||||
wrappers.put(TypeKind.BYTE, Byte.class);
|
||||
wrappers.put(TypeKind.CHAR, Character.class);
|
||||
wrappers.put(TypeKind.DOUBLE, Double.class);
|
||||
wrappers.put(TypeKind.FLOAT, Float.class);
|
||||
wrappers.put(TypeKind.INT, Integer.class);
|
||||
wrappers.put(TypeKind.LONG, Long.class);
|
||||
wrappers.put(TypeKind.SHORT, Short.class);
|
||||
PRIMITIVE_WRAPPERS = Collections.unmodifiableMap(wrappers);
|
||||
}
|
||||
|
||||
private static final Map<String, TypeKind> WRAPPER_TO_PRIMITIVE;
|
||||
|
||||
static {
|
||||
Map<String, TypeKind> primitives = new HashMap<>();
|
||||
for (Map.Entry<TypeKind, Class<?>> entry : PRIMITIVE_WRAPPERS.entrySet()) {
|
||||
primitives.put(entry.getValue().getName(), entry.getKey());
|
||||
}
|
||||
WRAPPER_TO_PRIMITIVE = primitives;
|
||||
}
|
||||
|
||||
private final ProcessingEnvironment env;
|
||||
|
||||
private final TypeMirror collectionType;
|
||||
|
||||
private final TypeMirror mapType;
|
||||
|
||||
TypeUtils(ProcessingEnvironment env) {
|
||||
this.env = env;
|
||||
Types types = env.getTypeUtils();
|
||||
this.collectionType = getDeclaredType(types, Collection.class, 1);
|
||||
this.mapType = getDeclaredType(types, Map.class, 2);
|
||||
}
|
||||
|
||||
private TypeMirror getDeclaredType(Types types, Class<?> typeClass,
|
||||
int numberOfTypeArgs) {
|
||||
TypeMirror[] typeArgs = new TypeMirror[numberOfTypeArgs];
|
||||
for (int i = 0; i < typeArgs.length; i++) {
|
||||
typeArgs[i] = types.getWildcardType(null, null);
|
||||
}
|
||||
TypeElement typeElement = this.env.getElementUtils()
|
||||
.getTypeElement(typeClass.getName());
|
||||
try {
|
||||
return types.getDeclaredType(typeElement, typeArgs);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
// Try again without generics for older Java versions
|
||||
return types.getDeclaredType(typeElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the qualified name of the specified element.
|
||||
* @param element the element to handle
|
||||
* @return the fully qualified name of the element, suitable for a call to
|
||||
* {@link Class#forName(String)}
|
||||
*/
|
||||
public String getQualifiedName(Element element) {
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
TypeElement enclosingElement = getEnclosingTypeElement(element.asType());
|
||||
if (enclosingElement != null) {
|
||||
return getQualifiedName(enclosingElement) + "$"
|
||||
+ ((DeclaredType) element.asType()).asElement().getSimpleName()
|
||||
.toString();
|
||||
}
|
||||
if (element instanceof TypeElement) {
|
||||
return ((TypeElement) element).getQualifiedName().toString();
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Could not extract qualified name from " + element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of the specified {@link TypeMirror} including all its generic
|
||||
* information.
|
||||
* @param type the type to handle
|
||||
* @return a representation of the type including all its generic information
|
||||
*/
|
||||
public String getType(TypeMirror type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?> wrapper = getWrapperFor(type);
|
||||
if (wrapper != null) {
|
||||
return wrapper.getName();
|
||||
}
|
||||
TypeElement enclosingElement = getEnclosingTypeElement(type);
|
||||
if (enclosingElement != null) {
|
||||
return getQualifiedName(enclosingElement) + "$"
|
||||
+ ((DeclaredType) type).asElement().getSimpleName().toString();
|
||||
}
|
||||
return type.toString();
|
||||
}
|
||||
|
||||
private TypeElement getEnclosingTypeElement(TypeMirror type) {
|
||||
if (type instanceof DeclaredType) {
|
||||
DeclaredType declaredType = (DeclaredType) type;
|
||||
Element enclosingElement = declaredType.asElement().getEnclosingElement();
|
||||
if (enclosingElement != null && enclosingElement instanceof TypeElement) {
|
||||
return (TypeElement) enclosingElement;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isCollectionOrMap(TypeMirror type) {
|
||||
return this.env.getTypeUtils().isAssignable(type, this.collectionType)
|
||||
|| this.env.getTypeUtils().isAssignable(type, this.mapType);
|
||||
}
|
||||
|
||||
public boolean isEnclosedIn(Element candidate, TypeElement element) {
|
||||
if (candidate == null || element == null) {
|
||||
return false;
|
||||
}
|
||||
if (candidate.equals(element)) {
|
||||
return true;
|
||||
}
|
||||
return isEnclosedIn(candidate.getEnclosingElement(), element);
|
||||
}
|
||||
|
||||
public String getJavaDoc(Element element) {
|
||||
String javadoc = (element == null ? null
|
||||
: this.env.getElementUtils().getDocComment(element));
|
||||
if (javadoc != null) {
|
||||
javadoc = javadoc.trim();
|
||||
}
|
||||
return ("".equals(javadoc) ? null : javadoc);
|
||||
}
|
||||
|
||||
public TypeMirror getWrapperOrPrimitiveFor(TypeMirror typeMirror) {
|
||||
Class<?> candidate = getWrapperFor(typeMirror);
|
||||
if (candidate != null) {
|
||||
return this.env.getElementUtils().getTypeElement(candidate.getName())
|
||||
.asType();
|
||||
}
|
||||
TypeKind primitiveKind = getPrimitiveFor(typeMirror);
|
||||
if (primitiveKind != null) {
|
||||
return this.env.getTypeUtils().getPrimitiveType(primitiveKind);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Class<?> getWrapperFor(TypeMirror type) {
|
||||
return PRIMITIVE_WRAPPERS.get(type.getKind());
|
||||
}
|
||||
|
||||
private TypeKind getPrimitiveFor(TypeMirror type) {
|
||||
return WRAPPER_TO_PRIMITIVE.get(type.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.javac.JavaCompilerFieldValuesParser;
|
||||
|
||||
/**
|
||||
* Parser which can be used to obtain the field values from an {@link TypeElement}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.1.2
|
||||
* @see JavaCompilerFieldValuesParser
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface FieldValuesParser {
|
||||
|
||||
/**
|
||||
* Implementation of {@link FieldValuesParser} that always returns an empty result.
|
||||
*/
|
||||
FieldValuesParser NONE = (element) -> Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* Return the field values for the given element.
|
||||
* @param element the element to inspect
|
||||
* @return a map of field names to values.
|
||||
* @throws Exception if the values cannot be extracted
|
||||
*/
|
||||
Map<String, Object> getFieldValues(TypeElement element) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues.javac;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reflection based access to {@code com.sun.source.tree.ExpressionTree}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class ExpressionTree extends ReflectionWrapper {
|
||||
|
||||
private final Class<?> literalTreeType = findClass("com.sun.source.tree.LiteralTree");
|
||||
|
||||
private final Method literalValueMethod = findMethod(this.literalTreeType,
|
||||
"getValue");
|
||||
|
||||
private final Class<?> methodInvocationTreeType = findClass(
|
||||
"com.sun.source.tree.MethodInvocationTree");
|
||||
|
||||
private final Method methodInvocationArgumentsMethod = findMethod(
|
||||
this.methodInvocationTreeType, "getArguments");
|
||||
|
||||
private final Class<?> newArrayTreeType = findClass(
|
||||
"com.sun.source.tree.NewArrayTree");
|
||||
|
||||
private final Method arrayValueMethod = findMethod(this.newArrayTreeType,
|
||||
"getInitializers");
|
||||
|
||||
ExpressionTree(Object instance) {
|
||||
super("com.sun.source.tree.ExpressionTree", instance);
|
||||
}
|
||||
|
||||
public String getKind() throws Exception {
|
||||
return findMethod("getKind").invoke(getInstance()).toString();
|
||||
}
|
||||
|
||||
public Object getLiteralValue() throws Exception {
|
||||
if (this.literalTreeType.isAssignableFrom(getInstance().getClass())) {
|
||||
return this.literalValueMethod.invoke(getInstance());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getFactoryValue() throws Exception {
|
||||
if (this.methodInvocationTreeType.isAssignableFrom(getInstance().getClass())) {
|
||||
List<?> arguments = (List<?>) this.methodInvocationArgumentsMethod
|
||||
.invoke(getInstance());
|
||||
if (arguments.size() == 1) {
|
||||
return new ExpressionTree(arguments.get(0)).getLiteralValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<? extends ExpressionTree> getArrayExpression() throws Exception {
|
||||
if (this.newArrayTreeType.isAssignableFrom(getInstance().getClass())) {
|
||||
List<?> elements = (List<?>) this.arrayValueMethod.invoke(getInstance());
|
||||
List<ExpressionTree> result = new ArrayList<>();
|
||||
if (elements == null) {
|
||||
return result;
|
||||
}
|
||||
for (Object element : elements) {
|
||||
result.add(new ExpressionTree(element));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues.javac;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.FieldValuesParser;
|
||||
|
||||
/**
|
||||
* {@link FieldValuesParser} implementation for the standard Java compiler.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class JavaCompilerFieldValuesParser implements FieldValuesParser {
|
||||
|
||||
private final Trees trees;
|
||||
|
||||
public JavaCompilerFieldValuesParser(ProcessingEnvironment env) throws Exception {
|
||||
this.trees = Trees.instance(env);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getFieldValues(TypeElement element) throws Exception {
|
||||
Tree tree = this.trees.getTree(element);
|
||||
if (tree != null) {
|
||||
FieldCollector fieldCollector = new FieldCollector();
|
||||
tree.accept(fieldCollector);
|
||||
return fieldCollector.getFieldValues();
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TreeVisitor} to collect fields.
|
||||
*/
|
||||
private static class FieldCollector implements TreeVisitor {
|
||||
|
||||
private static final Map<String, Class<?>> WRAPPER_TYPES;
|
||||
|
||||
static {
|
||||
Map<String, Class<?>> types = new HashMap<>();
|
||||
types.put("boolean", Boolean.class);
|
||||
types.put(Boolean.class.getName(), Boolean.class);
|
||||
types.put("byte", Byte.class);
|
||||
types.put(Byte.class.getName(), Byte.class);
|
||||
types.put("short", Short.class);
|
||||
types.put(Short.class.getName(), Short.class);
|
||||
types.put("int", Integer.class);
|
||||
types.put(Integer.class.getName(), Integer.class);
|
||||
types.put("long", Long.class);
|
||||
types.put(Long.class.getName(), Long.class);
|
||||
WRAPPER_TYPES = Collections.unmodifiableMap(types);
|
||||
}
|
||||
|
||||
private static final Map<Class<?>, Object> defaultTypeValues;
|
||||
|
||||
static {
|
||||
Map<Class<?>, Object> values = new HashMap<>();
|
||||
values.put(Boolean.class, false);
|
||||
values.put(Byte.class, (byte) 0);
|
||||
values.put(Short.class, (short) 0);
|
||||
values.put(Integer.class, 0);
|
||||
values.put(Long.class, (long) 0);
|
||||
defaultTypeValues = Collections.unmodifiableMap(values);
|
||||
}
|
||||
|
||||
private static final Map<String, Object> wellKnownStaticFinals;
|
||||
|
||||
static {
|
||||
Map<String, Object> values = new HashMap<>();
|
||||
values.put("Boolean.TRUE", true);
|
||||
values.put("Boolean.FALSE", false);
|
||||
wellKnownStaticFinals = Collections.unmodifiableMap(values);
|
||||
}
|
||||
|
||||
private final Map<String, Object> fieldValues = new HashMap<>();
|
||||
|
||||
private final Map<String, Object> staticFinals = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void visitVariable(VariableTree variable) throws Exception {
|
||||
Set<Modifier> flags = variable.getModifierFlags();
|
||||
if (flags.contains(Modifier.STATIC) && flags.contains(Modifier.FINAL)) {
|
||||
this.staticFinals.put(variable.getName(), getValue(variable));
|
||||
}
|
||||
if (!flags.contains(Modifier.FINAL)) {
|
||||
this.fieldValues.put(variable.getName(), getValue(variable));
|
||||
}
|
||||
}
|
||||
|
||||
private Object getValue(VariableTree variable) throws Exception {
|
||||
ExpressionTree initializer = variable.getInitializer();
|
||||
Class<?> wrapperType = WRAPPER_TYPES.get(variable.getType());
|
||||
Object defaultValue = defaultTypeValues.get(wrapperType);
|
||||
if (initializer != null) {
|
||||
return getValue(initializer, defaultValue);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
private Object getValue(ExpressionTree expression, Object defaultValue)
|
||||
throws Exception {
|
||||
Object literalValue = expression.getLiteralValue();
|
||||
if (literalValue != null) {
|
||||
return literalValue;
|
||||
}
|
||||
Object factoryValue = expression.getFactoryValue();
|
||||
if (factoryValue != null) {
|
||||
return factoryValue;
|
||||
}
|
||||
List<? extends ExpressionTree> arrayValues = expression.getArrayExpression();
|
||||
if (arrayValues != null) {
|
||||
Object[] result = new Object[arrayValues.size()];
|
||||
for (int i = 0; i < arrayValues.size(); i++) {
|
||||
Object value = getValue(arrayValues.get(i), null);
|
||||
if (value == null) { // One of the elements could not be resolved
|
||||
return defaultValue;
|
||||
}
|
||||
result[i] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (expression.getKind().equals("IDENTIFIER")) {
|
||||
return this.staticFinals.get(expression.toString());
|
||||
}
|
||||
if (expression.getKind().equals("MEMBER_SELECT")) {
|
||||
return wellKnownStaticFinals.get(expression.toString());
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public Map<String, Object> getFieldValues() {
|
||||
return this.fieldValues;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues.javac;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Base class for reflection based wrappers. Used to access internal Java classes without
|
||||
* needing tools.jar on the classpath.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class ReflectionWrapper {
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final Object instance;
|
||||
|
||||
ReflectionWrapper(String type, Object instance) {
|
||||
this.type = findClass(instance.getClass().getClassLoader(), type);
|
||||
this.instance = this.type.cast(instance);
|
||||
}
|
||||
|
||||
protected final Object getInstance() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.instance.toString();
|
||||
}
|
||||
|
||||
protected Class<?> findClass(String name) {
|
||||
return findClass(getInstance().getClass().getClassLoader(), name);
|
||||
}
|
||||
|
||||
protected Method findMethod(String name, Class<?>... parameterTypes) {
|
||||
return findMethod(this.type, name, parameterTypes);
|
||||
}
|
||||
|
||||
protected static Class<?> findClass(ClassLoader classLoader, String name) {
|
||||
try {
|
||||
return classLoader.loadClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected static Method findMethod(Class<?> type, String name,
|
||||
Class<?>... parameterTypes) {
|
||||
try {
|
||||
return type.getMethod(name, parameterTypes);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues.javac;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* Reflection based access to {@code com.sun.source.tree.Tree}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class Tree extends ReflectionWrapper {
|
||||
|
||||
private final Class<?> treeVisitorType = findClass("com.sun.source.tree.TreeVisitor");
|
||||
|
||||
private final Method acceptMethod = findMethod("accept", this.treeVisitorType,
|
||||
Object.class);
|
||||
|
||||
private final Method GET_CLASS_TREE_MEMBERS = findMethod(
|
||||
findClass("com.sun.source.tree.ClassTree"), "getMembers");
|
||||
|
||||
Tree(Object instance) {
|
||||
super("com.sun.source.tree.Tree", instance);
|
||||
}
|
||||
|
||||
public void accept(TreeVisitor visitor) throws Exception {
|
||||
this.acceptMethod.invoke(getInstance(),
|
||||
Proxy.newProxyInstance(getInstance().getClass().getClassLoader(),
|
||||
new Class<?>[] { this.treeVisitorType },
|
||||
new TreeVisitorInvocationHandler(visitor)),
|
||||
0);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link InvocationHandler} to call the {@link TreeVisitor}.
|
||||
*/
|
||||
private class TreeVisitorInvocationHandler implements InvocationHandler {
|
||||
|
||||
private TreeVisitor treeVisitor;
|
||||
|
||||
TreeVisitorInvocationHandler(TreeVisitor treeVisitor) {
|
||||
this.treeVisitor = treeVisitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
if (method.getName().equals("visitClass")) {
|
||||
if ((Integer) args[1] == 0) {
|
||||
Iterable members = (Iterable) Tree.this.GET_CLASS_TREE_MEMBERS
|
||||
.invoke(args[0]);
|
||||
for (Object member : members) {
|
||||
if (member != null) {
|
||||
Tree.this.acceptMethod.invoke(member, proxy,
|
||||
((Integer) args[1]) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (method.getName().equals("visitVariable")) {
|
||||
this.treeVisitor.visitVariable(new VariableTree(args[0]));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues.javac;
|
||||
|
||||
/**
|
||||
* Reflection base alternative for {@code com.sun.source.tree.TreeVisitor}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
interface TreeVisitor {
|
||||
|
||||
void visitVariable(VariableTree variable) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues.javac;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
/**
|
||||
* Reflection based access to {@code com.sun.source.util.Trees}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
final class Trees extends ReflectionWrapper {
|
||||
|
||||
private Trees(Object instance) {
|
||||
super("com.sun.source.util.Trees", instance);
|
||||
}
|
||||
|
||||
public Tree getTree(Element element) throws Exception {
|
||||
Object tree = findMethod("getTree", Element.class).invoke(getInstance(), element);
|
||||
return (tree == null ? null : new Tree(tree));
|
||||
}
|
||||
|
||||
public static Trees instance(ProcessingEnvironment env) throws Exception {
|
||||
ClassLoader classLoader = env.getClass().getClassLoader();
|
||||
Class<?> type = findClass(classLoader, "com.sun.source.util.Trees");
|
||||
Method method = findMethod(type, "instance", ProcessingEnvironment.class);
|
||||
return new Trees(method.invoke(null, env));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues.javac;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
|
||||
/**
|
||||
* Reflection based access to {@code com.sun.source.tree.VariableTree}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
class VariableTree extends ReflectionWrapper {
|
||||
|
||||
VariableTree(Object instance) {
|
||||
super("com.sun.source.tree.VariableTree", instance);
|
||||
}
|
||||
|
||||
public String getName() throws Exception {
|
||||
return findMethod("getName").invoke(getInstance()).toString();
|
||||
}
|
||||
|
||||
public String getType() throws Exception {
|
||||
return findMethod("getType").invoke(getInstance()).toString();
|
||||
}
|
||||
|
||||
public ExpressionTree getInitializer() throws Exception {
|
||||
Object instance = findMethod("getInitializer").invoke(getInstance());
|
||||
return (instance == null ? null : new ExpressionTree(instance));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<Modifier> getModifierFlags() throws Exception {
|
||||
Object modifiers = findMethod("getModifiers").invoke(getInstance());
|
||||
if (modifiers == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return (Set<Modifier>) findMethod(findClass("com.sun.source.tree.ModifiersTree"),
|
||||
"getFlags").invoke(modifiers);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Configuration meta-data.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
* @see ItemMetadata
|
||||
*/
|
||||
public class ConfigurationMetadata {
|
||||
|
||||
private static final Set<Character> SEPARATORS;
|
||||
|
||||
static {
|
||||
List<Character> chars = Arrays.asList('-', '_');
|
||||
SEPARATORS = Collections.unmodifiableSet(new HashSet<>(chars));
|
||||
}
|
||||
|
||||
private final Map<String, List<ItemMetadata>> items;
|
||||
|
||||
private final Map<String, List<ItemHint>> hints;
|
||||
|
||||
public ConfigurationMetadata() {
|
||||
this.items = new LinkedHashMap<>();
|
||||
this.hints = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public ConfigurationMetadata(ConfigurationMetadata metadata) {
|
||||
this.items = new LinkedHashMap<>(metadata.items);
|
||||
this.hints = new LinkedHashMap<>(metadata.hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item meta-data.
|
||||
* @param itemMetadata the meta-data to add
|
||||
*/
|
||||
public void add(ItemMetadata itemMetadata) {
|
||||
add(this.items, itemMetadata.getName(), itemMetadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item hint.
|
||||
* @param itemHint the item hint to add
|
||||
*/
|
||||
public void add(ItemHint itemHint) {
|
||||
add(this.hints, itemHint.getName(), itemHint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the content from another {@link ConfigurationMetadata}.
|
||||
* @param metadata the {@link ConfigurationMetadata} instance to merge
|
||||
*/
|
||||
public void merge(ConfigurationMetadata metadata) {
|
||||
for (ItemMetadata additionalItem : metadata.getItems()) {
|
||||
mergeItemMetadata(additionalItem);
|
||||
}
|
||||
for (ItemHint itemHint : metadata.getHints()) {
|
||||
add(itemHint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return item meta-data.
|
||||
* @return the items
|
||||
*/
|
||||
public List<ItemMetadata> getItems() {
|
||||
return flattenValues(this.items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return hint meta-data.
|
||||
* @return the hints
|
||||
*/
|
||||
public List<ItemHint> getHints() {
|
||||
return flattenValues(this.hints);
|
||||
}
|
||||
|
||||
protected void mergeItemMetadata(ItemMetadata metadata) {
|
||||
ItemMetadata matching = findMatchingItemMetadata(metadata);
|
||||
if (matching != null) {
|
||||
if (metadata.getDescription() != null) {
|
||||
matching.setDescription(metadata.getDescription());
|
||||
}
|
||||
if (metadata.getDefaultValue() != null) {
|
||||
matching.setDefaultValue(metadata.getDefaultValue());
|
||||
}
|
||||
ItemDeprecation deprecation = metadata.getDeprecation();
|
||||
ItemDeprecation matchingDeprecation = matching.getDeprecation();
|
||||
if (deprecation != null) {
|
||||
if (matchingDeprecation == null) {
|
||||
matching.setDeprecation(deprecation);
|
||||
}
|
||||
else {
|
||||
if (deprecation.getReason() != null) {
|
||||
matchingDeprecation.setReason(deprecation.getReason());
|
||||
}
|
||||
if (deprecation.getReplacement() != null) {
|
||||
matchingDeprecation.setReplacement(deprecation.getReplacement());
|
||||
}
|
||||
if (deprecation.getLevel() != null) {
|
||||
matchingDeprecation.setLevel(deprecation.getLevel());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
add(this.items, metadata.getName(), metadata);
|
||||
}
|
||||
}
|
||||
|
||||
private <K, V> void add(Map<K, List<V>> map, K key, V value) {
|
||||
List<V> values = map.get(key);
|
||||
if (values == null) {
|
||||
values = new ArrayList<>();
|
||||
map.put(key, values);
|
||||
}
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
private ItemMetadata findMatchingItemMetadata(ItemMetadata metadata) {
|
||||
List<ItemMetadata> candidates = this.items.get(metadata.getName());
|
||||
if (candidates == null || candidates.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
candidates.removeIf((itemMetadata) -> !itemMetadata.hasSameType(metadata));
|
||||
if (candidates.size() == 1) {
|
||||
return candidates.get(0);
|
||||
}
|
||||
for (ItemMetadata candidate : candidates) {
|
||||
if (nullSafeEquals(candidate.getSourceType(), metadata.getSourceType())) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean nullSafeEquals(Object o1, Object o2) {
|
||||
if (o1 == o2) {
|
||||
return true;
|
||||
}
|
||||
return o1 != null && o2 != null && o1.equals(o2);
|
||||
}
|
||||
|
||||
public static String nestedPrefix(String prefix, String name) {
|
||||
String nestedPrefix = (prefix == null ? "" : prefix);
|
||||
String dashedName = toDashedCase(name);
|
||||
nestedPrefix += ("".equals(nestedPrefix) ? dashedName : "." + dashedName);
|
||||
return nestedPrefix;
|
||||
}
|
||||
|
||||
static String toDashedCase(String name) {
|
||||
StringBuilder dashed = new StringBuilder();
|
||||
Character previous = null;
|
||||
for (char current : name.toCharArray()) {
|
||||
if (SEPARATORS.contains(current)) {
|
||||
dashed.append("-");
|
||||
}
|
||||
else if (Character.isUpperCase(current) && previous != null
|
||||
&& !SEPARATORS.contains(previous)) {
|
||||
dashed.append("-").append(current);
|
||||
}
|
||||
else {
|
||||
dashed.append(current);
|
||||
}
|
||||
previous = current;
|
||||
|
||||
}
|
||||
return dashed.toString().toLowerCase();
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> List<T> flattenValues(Map<?, List<T>> map) {
|
||||
List<T> content = new ArrayList<>();
|
||||
for (List<T> values : map.values()) {
|
||||
content.addAll(values);
|
||||
}
|
||||
Collections.sort(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(String.format("items: %n"));
|
||||
this.items.values().forEach((itemMetadata) -> result.append("\t")
|
||||
.append(String.format("%s%n", itemMetadata)));
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that some meta-data is invalid. Define the severity to determine
|
||||
* whether it has to fail the build.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class InvalidConfigurationMetadataException extends RuntimeException {
|
||||
|
||||
private final Diagnostic.Kind kind;
|
||||
|
||||
public InvalidConfigurationMetadataException(String message, Diagnostic.Kind kind) {
|
||||
super(message);
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
public Diagnostic.Kind getKind() {
|
||||
return this.kind;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
/**
|
||||
* Describe an item deprecation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class ItemDeprecation {
|
||||
|
||||
private String reason;
|
||||
|
||||
private String replacement;
|
||||
|
||||
private String level;
|
||||
|
||||
public ItemDeprecation() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public ItemDeprecation(String reason, String replacement) {
|
||||
this(reason, replacement, null);
|
||||
}
|
||||
|
||||
public ItemDeprecation(String reason, String replacement, String level) {
|
||||
this.reason = reason;
|
||||
this.replacement = replacement;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getReplacement() {
|
||||
return this.replacement;
|
||||
}
|
||||
|
||||
public void setReplacement(String replacement) {
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
public String getLevel() {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
public void setLevel(String level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ItemDeprecation{" + "reason='" + this.reason + '\'' + ", "
|
||||
+ "replacement='" + this.replacement + '\'' + ", " + "level='"
|
||||
+ this.level + '\'' + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ItemDeprecation other = (ItemDeprecation) o;
|
||||
return nullSafeEquals(this.reason, other.reason)
|
||||
&& nullSafeEquals(this.replacement, other.replacement)
|
||||
&& nullSafeEquals(this.level, other.level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = nullSafeHashCode(this.reason);
|
||||
result = 31 * result + nullSafeHashCode(this.replacement);
|
||||
result = 31 * result + nullSafeHashCode(this.level);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean nullSafeEquals(Object o1, Object o2) {
|
||||
if (o1 == o2) {
|
||||
return true;
|
||||
}
|
||||
if (o1 == null || o2 == null) {
|
||||
return false;
|
||||
}
|
||||
return o1.equals(o2);
|
||||
}
|
||||
|
||||
private int nullSafeHashCode(Object o) {
|
||||
return (o == null ? 0 : o.hashCode());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provide hints on an {@link ItemMetadata}. Defines the list of possible values for a
|
||||
* particular item as {@link ItemHint.ValueHint} instances.
|
||||
* <p>
|
||||
* The {@code name} of the hint is the name of the related property with one major
|
||||
* exception for map types as both the keys and values of the map can have hints. In such
|
||||
* a case, the hint should be suffixed by ".keys" or ".values" respectively. Creating a
|
||||
* hint for a map using its property name is therefore invalid.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public class ItemHint implements Comparable<ItemHint> {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final List<ValueHint> values;
|
||||
|
||||
private final List<ValueProvider> providers;
|
||||
|
||||
public ItemHint(String name, List<ValueHint> values, List<ValueProvider> providers) {
|
||||
this.name = toCanonicalName(name);
|
||||
this.values = (values != null ? new ArrayList<>(values) : new ArrayList<>());
|
||||
this.providers = (providers != null ? new ArrayList<>(providers)
|
||||
: new ArrayList<>());
|
||||
}
|
||||
|
||||
private String toCanonicalName(String name) {
|
||||
int dot = name.lastIndexOf('.');
|
||||
if (dot != -1) {
|
||||
String prefix = name.substring(0, dot);
|
||||
String originalName = name.substring(dot);
|
||||
return prefix + ConfigurationMetadata.toDashedCase(originalName);
|
||||
}
|
||||
return ConfigurationMetadata.toDashedCase(name);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public List<ValueHint> getValues() {
|
||||
return Collections.unmodifiableList(this.values);
|
||||
}
|
||||
|
||||
public List<ValueProvider> getProviders() {
|
||||
return Collections.unmodifiableList(this.providers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ItemHint other) {
|
||||
return getName().compareTo(other.getName());
|
||||
}
|
||||
|
||||
public static ItemHint newHint(String name, ValueHint... values) {
|
||||
return new ItemHint(name, Arrays.asList(values),
|
||||
Collections.<ValueProvider>emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ItemHint{" + "name='" + this.name + "', values=" + this.values
|
||||
+ ", providers=" + this.providers + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* A hint for a value.
|
||||
*/
|
||||
public static class ValueHint {
|
||||
|
||||
private final Object value;
|
||||
|
||||
private final String description;
|
||||
|
||||
public ValueHint(Object value, String description) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ValueHint{" + "value=" + this.value + ", description='"
|
||||
+ this.description + '\'' + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A value provider.
|
||||
*/
|
||||
public static class ValueProvider {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final Map<String, Object> parameters;
|
||||
|
||||
public ValueProvider(String name, Map<String, Object> parameters) {
|
||||
this.name = name;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Map<String, Object> getParameters() {
|
||||
return this.parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ValueProvider{" + "name='" + this.name + "', parameters="
|
||||
+ this.parameters + '}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
/**
|
||||
* A group or property meta-data item from some {@link ConfigurationMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
* @see ConfigurationMetadata
|
||||
*/
|
||||
public final class ItemMetadata implements Comparable<ItemMetadata> {
|
||||
|
||||
private ItemType itemType;
|
||||
|
||||
private String name;
|
||||
|
||||
private String type;
|
||||
|
||||
private String description;
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private String sourceMethod;
|
||||
|
||||
private Object defaultValue;
|
||||
|
||||
private ItemDeprecation deprecation;
|
||||
|
||||
ItemMetadata(ItemType itemType, String prefix, String name, String type,
|
||||
String sourceType, String sourceMethod, String description,
|
||||
Object defaultValue, ItemDeprecation deprecation) {
|
||||
super();
|
||||
this.itemType = itemType;
|
||||
this.name = buildName(prefix, name);
|
||||
this.type = type;
|
||||
this.sourceType = sourceType;
|
||||
this.sourceMethod = sourceMethod;
|
||||
this.description = description;
|
||||
this.defaultValue = defaultValue;
|
||||
this.deprecation = deprecation;
|
||||
}
|
||||
|
||||
private String buildName(String prefix, String name) {
|
||||
while (prefix != null && prefix.endsWith(".")) {
|
||||
prefix = prefix.substring(0, prefix.length() - 1);
|
||||
}
|
||||
StringBuilder fullName = new StringBuilder(prefix == null ? "" : prefix);
|
||||
if (fullName.length() > 0 && name != null) {
|
||||
fullName.append(".");
|
||||
}
|
||||
fullName.append(name == null ? "" : ConfigurationMetadata.toDashedCase(name));
|
||||
return fullName.toString();
|
||||
}
|
||||
|
||||
public boolean isOfItemType(ItemType itemType) {
|
||||
return this.itemType == itemType;
|
||||
}
|
||||
|
||||
public boolean hasSameType(ItemMetadata metadata) {
|
||||
return this.itemType == metadata.itemType;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getSourceType() {
|
||||
return this.sourceType;
|
||||
}
|
||||
|
||||
public void setSourceType(String sourceType) {
|
||||
this.sourceType = sourceType;
|
||||
}
|
||||
|
||||
public String getSourceMethod() {
|
||||
return this.sourceMethod;
|
||||
}
|
||||
|
||||
public void setSourceMethod(String sourceMethod) {
|
||||
this.sourceMethod = sourceMethod;
|
||||
}
|
||||
|
||||
public Object getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public ItemDeprecation getDeprecation() {
|
||||
return this.deprecation;
|
||||
}
|
||||
|
||||
public void setDeprecation(ItemDeprecation deprecation) {
|
||||
this.deprecation = deprecation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder string = new StringBuilder(this.name);
|
||||
buildToStringProperty(string, "type", this.type);
|
||||
buildToStringProperty(string, "sourceType", this.sourceType);
|
||||
buildToStringProperty(string, "description", this.description);
|
||||
buildToStringProperty(string, "defaultValue", this.defaultValue);
|
||||
buildToStringProperty(string, "deprecation", this.deprecation);
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
protected void buildToStringProperty(StringBuilder string, String property,
|
||||
Object value) {
|
||||
if (value != null) {
|
||||
string.append(" ").append(property).append(":").append(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ItemMetadata other = (ItemMetadata) o;
|
||||
boolean result = true;
|
||||
result = result && nullSafeEquals(this.itemType, other.itemType);
|
||||
result = result && nullSafeEquals(this.name, other.name);
|
||||
result = result && nullSafeEquals(this.type, other.type);
|
||||
result = result && nullSafeEquals(this.description, other.description);
|
||||
result = result && nullSafeEquals(this.sourceType, other.sourceType);
|
||||
result = result && nullSafeEquals(this.sourceMethod, other.sourceMethod);
|
||||
result = result && nullSafeEquals(this.defaultValue, other.defaultValue);
|
||||
result = result && nullSafeEquals(this.deprecation, other.deprecation);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = nullSafeHashCode(this.itemType);
|
||||
result = 31 * result + nullSafeHashCode(this.name);
|
||||
result = 31 * result + nullSafeHashCode(this.type);
|
||||
result = 31 * result + nullSafeHashCode(this.description);
|
||||
result = 31 * result + nullSafeHashCode(this.sourceType);
|
||||
result = 31 * result + nullSafeHashCode(this.sourceMethod);
|
||||
result = 31 * result + nullSafeHashCode(this.defaultValue);
|
||||
result = 31 * result + nullSafeHashCode(this.deprecation);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean nullSafeEquals(Object o1, Object o2) {
|
||||
if (o1 == o2) {
|
||||
return true;
|
||||
}
|
||||
if (o1 == null || o2 == null) {
|
||||
return false;
|
||||
}
|
||||
return o1.equals(o2);
|
||||
}
|
||||
|
||||
private int nullSafeHashCode(Object o) {
|
||||
return (o == null ? 0 : o.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ItemMetadata o) {
|
||||
return getName().compareTo(o.getName());
|
||||
}
|
||||
|
||||
public static ItemMetadata newGroup(String name, String type, String sourceType,
|
||||
String sourceMethod) {
|
||||
return new ItemMetadata(ItemType.GROUP, name, null, type, sourceType,
|
||||
sourceMethod, null, null, null);
|
||||
}
|
||||
|
||||
public static ItemMetadata newProperty(String prefix, String name, String type,
|
||||
String sourceType, String sourceMethod, String description,
|
||||
Object defaultValue, ItemDeprecation deprecation) {
|
||||
return new ItemMetadata(ItemType.PROPERTY, prefix, name, type, sourceType,
|
||||
sourceMethod, description, defaultValue, deprecation);
|
||||
}
|
||||
|
||||
/**
|
||||
* The item type.
|
||||
*/
|
||||
public enum ItemType {
|
||||
|
||||
/**
|
||||
* Group item type.
|
||||
*/
|
||||
GROUP,
|
||||
|
||||
/**
|
||||
* Property item type.
|
||||
*/
|
||||
PROPERTY
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Extension to {@link JSONObject} that remembers the order of inserts.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
class JSONOrderedObject extends JSONObject {
|
||||
|
||||
private Set<String> keys = new LinkedHashSet<>();
|
||||
|
||||
@Override
|
||||
public JSONObject put(String key, Object value) throws JSONException {
|
||||
this.keys.add(key);
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator keys() {
|
||||
return this.keys.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata.ItemType;
|
||||
|
||||
/**
|
||||
* Converter to change meta-data objects into JSON objects.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class JsonConverter {
|
||||
|
||||
public JSONArray toJsonArray(ConfigurationMetadata metadata, ItemType itemType)
|
||||
throws Exception {
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
for (ItemMetadata item : metadata.getItems()) {
|
||||
if (item.isOfItemType(itemType)) {
|
||||
jsonArray.put(toJsonObject(item));
|
||||
}
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
public JSONArray toJsonArray(Collection<ItemHint> hints) throws Exception {
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
for (ItemHint hint : hints) {
|
||||
jsonArray.put(toJsonObject(hint));
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
public JSONObject toJsonObject(ItemMetadata item) throws Exception {
|
||||
JSONObject jsonObject = new JSONOrderedObject();
|
||||
jsonObject.put("name", item.getName());
|
||||
putIfPresent(jsonObject, "type", item.getType());
|
||||
putIfPresent(jsonObject, "description", item.getDescription());
|
||||
putIfPresent(jsonObject, "sourceType", item.getSourceType());
|
||||
putIfPresent(jsonObject, "sourceMethod", item.getSourceMethod());
|
||||
Object defaultValue = item.getDefaultValue();
|
||||
if (defaultValue != null) {
|
||||
putDefaultValue(jsonObject, defaultValue);
|
||||
}
|
||||
ItemDeprecation deprecation = item.getDeprecation();
|
||||
if (deprecation != null) {
|
||||
jsonObject.put("deprecated", true); // backward compatibility
|
||||
JSONObject deprecationJsonObject = new JSONObject();
|
||||
if (deprecation.getLevel() != null) {
|
||||
deprecationJsonObject.put("level", deprecation.getLevel());
|
||||
}
|
||||
if (deprecation.getReason() != null) {
|
||||
deprecationJsonObject.put("reason", deprecation.getReason());
|
||||
}
|
||||
if (deprecation.getReplacement() != null) {
|
||||
deprecationJsonObject.put("replacement", deprecation.getReplacement());
|
||||
}
|
||||
jsonObject.put("deprecation", deprecationJsonObject);
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
private JSONObject toJsonObject(ItemHint hint) throws Exception {
|
||||
JSONObject jsonObject = new JSONOrderedObject();
|
||||
jsonObject.put("name", hint.getName());
|
||||
if (!hint.getValues().isEmpty()) {
|
||||
jsonObject.put("values", getItemHintValues(hint));
|
||||
}
|
||||
if (!hint.getProviders().isEmpty()) {
|
||||
jsonObject.put("providers", getItemHintProviders(hint));
|
||||
}
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
private JSONArray getItemHintValues(ItemHint hint) throws Exception {
|
||||
JSONArray values = new JSONArray();
|
||||
for (ItemHint.ValueHint value : hint.getValues()) {
|
||||
values.put(getItemHintValue(value));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private JSONObject getItemHintValue(ItemHint.ValueHint value) throws Exception {
|
||||
JSONObject result = new JSONOrderedObject();
|
||||
putHintValue(result, value.getValue());
|
||||
putIfPresent(result, "description", value.getDescription());
|
||||
return result;
|
||||
}
|
||||
|
||||
private JSONArray getItemHintProviders(ItemHint hint) throws Exception {
|
||||
JSONArray providers = new JSONArray();
|
||||
for (ItemHint.ValueProvider provider : hint.getProviders()) {
|
||||
providers.put(getItemHintProvider(provider));
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
private JSONObject getItemHintProvider(ItemHint.ValueProvider provider)
|
||||
throws Exception {
|
||||
JSONObject result = new JSONOrderedObject();
|
||||
result.put("name", provider.getName());
|
||||
if (provider.getParameters() != null && !provider.getParameters().isEmpty()) {
|
||||
JSONObject parameters = new JSONOrderedObject();
|
||||
for (Map.Entry<String, Object> entry : provider.getParameters().entrySet()) {
|
||||
parameters.put(entry.getKey(), extractItemValue(entry.getValue()));
|
||||
}
|
||||
result.put("parameters", parameters);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void putIfPresent(JSONObject jsonObject, String name, Object value)
|
||||
throws Exception {
|
||||
if (value != null) {
|
||||
jsonObject.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void putHintValue(JSONObject jsonObject, Object value) throws Exception {
|
||||
Object hintValue = extractItemValue(value);
|
||||
jsonObject.put("value", hintValue);
|
||||
}
|
||||
|
||||
private void putDefaultValue(JSONObject jsonObject, Object value) throws Exception {
|
||||
Object defaultValue = extractItemValue(value);
|
||||
jsonObject.put("defaultValue", defaultValue);
|
||||
}
|
||||
|
||||
private Object extractItemValue(Object value) {
|
||||
Object defaultValue = value;
|
||||
if (value.getClass().isArray()) {
|
||||
JSONArray array = new JSONArray();
|
||||
int length = Array.getLength(value);
|
||||
for (int i = 0; i < length; i++) {
|
||||
array.put(Array.get(value, i));
|
||||
}
|
||||
defaultValue = array;
|
||||
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata.ItemType;
|
||||
|
||||
/**
|
||||
* Marshaller to write {@link ConfigurationMetadata} as JSON.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public class JsonMarshaller {
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
private static final int BUFFER_SIZE = 4098;
|
||||
|
||||
public void write(ConfigurationMetadata metadata, OutputStream outputStream)
|
||||
throws IOException {
|
||||
try {
|
||||
JSONObject object = new JSONOrderedObject();
|
||||
JsonConverter converter = new JsonConverter();
|
||||
object.put("groups", converter.toJsonArray(metadata, ItemType.GROUP));
|
||||
object.put("properties", converter.toJsonArray(metadata, ItemType.PROPERTY));
|
||||
object.put("hints", converter.toJsonArray(metadata.getHints()));
|
||||
outputStream.write(object.toString(2).getBytes(UTF_8));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (ex instanceof IOException) {
|
||||
throw (IOException) ex;
|
||||
}
|
||||
if (ex instanceof RuntimeException) {
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurationMetadata read(InputStream inputStream) throws Exception {
|
||||
ConfigurationMetadata metadata = new ConfigurationMetadata();
|
||||
JSONObject object = new JSONObject(toString(inputStream));
|
||||
JSONArray groups = object.optJSONArray("groups");
|
||||
if (groups != null) {
|
||||
for (int i = 0; i < groups.length(); i++) {
|
||||
metadata.add(toItemMetadata((JSONObject) groups.get(i), ItemType.GROUP));
|
||||
}
|
||||
}
|
||||
JSONArray properties = object.optJSONArray("properties");
|
||||
if (properties != null) {
|
||||
for (int i = 0; i < properties.length(); i++) {
|
||||
metadata.add(toItemMetadata((JSONObject) properties.get(i),
|
||||
ItemType.PROPERTY));
|
||||
}
|
||||
}
|
||||
JSONArray hints = object.optJSONArray("hints");
|
||||
if (hints != null) {
|
||||
for (int i = 0; i < hints.length(); i++) {
|
||||
metadata.add(toItemHint((JSONObject) hints.get(i)));
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private ItemMetadata toItemMetadata(JSONObject object, ItemType itemType)
|
||||
throws Exception {
|
||||
String name = object.getString("name");
|
||||
String type = object.optString("type", null);
|
||||
String description = object.optString("description", null);
|
||||
String sourceType = object.optString("sourceType", null);
|
||||
String sourceMethod = object.optString("sourceMethod", null);
|
||||
Object defaultValue = readItemValue(object.opt("defaultValue"));
|
||||
ItemDeprecation deprecation = toItemDeprecation(object);
|
||||
return new ItemMetadata(itemType, name, null, type, sourceType, sourceMethod,
|
||||
description, defaultValue, deprecation);
|
||||
}
|
||||
|
||||
private ItemDeprecation toItemDeprecation(JSONObject object) throws Exception {
|
||||
if (object.has("deprecation")) {
|
||||
JSONObject deprecationJsonObject = object.getJSONObject("deprecation");
|
||||
ItemDeprecation deprecation = new ItemDeprecation();
|
||||
deprecation.setLevel(deprecationJsonObject.optString("level", null));
|
||||
deprecation.setReason(deprecationJsonObject.optString("reason", null));
|
||||
deprecation
|
||||
.setReplacement(deprecationJsonObject.optString("replacement", null));
|
||||
return deprecation;
|
||||
}
|
||||
return (object.optBoolean("deprecated") ? new ItemDeprecation() : null);
|
||||
}
|
||||
|
||||
private ItemHint toItemHint(JSONObject object) throws Exception {
|
||||
String name = object.getString("name");
|
||||
List<ItemHint.ValueHint> values = new ArrayList<>();
|
||||
if (object.has("values")) {
|
||||
JSONArray valuesArray = object.getJSONArray("values");
|
||||
for (int i = 0; i < valuesArray.length(); i++) {
|
||||
values.add(toValueHint((JSONObject) valuesArray.get(i)));
|
||||
}
|
||||
}
|
||||
List<ItemHint.ValueProvider> providers = new ArrayList<>();
|
||||
if (object.has("providers")) {
|
||||
JSONArray providersObject = object.getJSONArray("providers");
|
||||
for (int i = 0; i < providersObject.length(); i++) {
|
||||
providers.add(toValueProvider((JSONObject) providersObject.get(i)));
|
||||
}
|
||||
}
|
||||
return new ItemHint(name, values, providers);
|
||||
}
|
||||
|
||||
private ItemHint.ValueHint toValueHint(JSONObject object) throws Exception {
|
||||
Object value = readItemValue(object.get("value"));
|
||||
String description = object.optString("description", null);
|
||||
return new ItemHint.ValueHint(value, description);
|
||||
}
|
||||
|
||||
private ItemHint.ValueProvider toValueProvider(JSONObject object) throws Exception {
|
||||
String name = object.getString("name");
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
if (object.has("parameters")) {
|
||||
JSONObject parametersObject = object.getJSONObject("parameters");
|
||||
for (Iterator<?> iterator = parametersObject.keys(); iterator.hasNext();) {
|
||||
String key = (String) iterator.next();
|
||||
Object value = readItemValue(parametersObject.get(key));
|
||||
parameters.put(key, value);
|
||||
}
|
||||
}
|
||||
return new ItemHint.ValueProvider(name, parameters);
|
||||
}
|
||||
|
||||
private Object readItemValue(Object value) throws Exception {
|
||||
if (value instanceof JSONArray) {
|
||||
JSONArray array = (JSONArray) value;
|
||||
Object[] content = new Object[array.length()];
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
content[i] = array.get(i);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String toString(InputStream inputStream) throws IOException {
|
||||
StringBuilder out = new StringBuilder();
|
||||
InputStreamReader reader = new InputStreamReader(inputStream, UTF_8);
|
||||
char[] buffer = new char[BUFFER_SIZE];
|
||||
int bytesRead;
|
||||
while ((bytesRead = reader.read(buffer)) != -1) {
|
||||
out.append(buffer, 0, bytesRead);
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.configurationprocessor.ConfigurationMetadataAnnotationProcessor
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link MetadataStore}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class MetadataStoreTests {
|
||||
|
||||
@Rule
|
||||
public final TemporaryFolder temp = new TemporaryFolder();
|
||||
|
||||
private final MetadataStore metadataStore = new MetadataStore(
|
||||
mock(ProcessingEnvironment.class));
|
||||
|
||||
@Test
|
||||
public void additionalMetadataIsLocatedInMavenBuild() throws IOException {
|
||||
File app = this.temp.newFolder("app");
|
||||
File classesLocation = new File(app, "target/classes");
|
||||
File metaInf = new File(classesLocation, "META-INF");
|
||||
metaInf.mkdirs();
|
||||
File additionalMetadata = new File(metaInf,
|
||||
"additional-spring-configuration-metadata.json");
|
||||
additionalMetadata.createNewFile();
|
||||
assertThat(
|
||||
this.metadataStore.locateAdditionalMetadataFile(new File(classesLocation,
|
||||
"META-INF/additional-spring-configuration-metadata.json")))
|
||||
.isEqualTo(additionalMetadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalMetadataIsLocatedInGradle3Build() throws IOException {
|
||||
File app = this.temp.newFolder("app");
|
||||
File classesLocation = new File(app, "build/classes/main");
|
||||
File resourcesLocation = new File(app, "build/resources/main");
|
||||
File metaInf = new File(resourcesLocation, "META-INF");
|
||||
metaInf.mkdirs();
|
||||
File additionalMetadata = new File(metaInf,
|
||||
"additional-spring-configuration-metadata.json");
|
||||
additionalMetadata.createNewFile();
|
||||
assertThat(
|
||||
this.metadataStore.locateAdditionalMetadataFile(new File(classesLocation,
|
||||
"META-INF/additional-spring-configuration-metadata.json")))
|
||||
.isEqualTo(additionalMetadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalMetadataIsLocatedInGradle4Build() throws IOException {
|
||||
File app = this.temp.newFolder("app");
|
||||
File classesLocation = new File(app, "build/classes/java/main");
|
||||
File resourcesLocation = new File(app, "build/resources/main");
|
||||
File metaInf = new File(resourcesLocation, "META-INF");
|
||||
metaInf.mkdirs();
|
||||
File additionalMetadata = new File(metaInf,
|
||||
"additional-spring-configuration-metadata.json");
|
||||
additionalMetadata.createNewFile();
|
||||
assertThat(
|
||||
this.metadataStore.locateAdditionalMetadataFile(new File(classesLocation,
|
||||
"META-INF/additional-spring-configuration-metadata.json")))
|
||||
.isEqualTo(additionalMetadata);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||
import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller;
|
||||
|
||||
/**
|
||||
* Test {@link ConfigurationMetadataAnnotationProcessor}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
@SupportedAnnotationTypes({ "*" })
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_6)
|
||||
public class TestConfigurationMetadataAnnotationProcessor
|
||||
extends ConfigurationMetadataAnnotationProcessor {
|
||||
|
||||
static final String CONFIGURATION_PROPERTIES_ANNOTATION = "org.springframework.boot.configurationsample.ConfigurationProperties";
|
||||
|
||||
static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot.configurationsample.NestedConfigurationProperty";
|
||||
|
||||
static final String DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot.configurationsample.DeprecatedConfigurationProperty";
|
||||
|
||||
static final String ENDPOINT_ANNOTATION = "org.springframework.boot.configurationsample.Endpoint";
|
||||
|
||||
private ConfigurationMetadata metadata;
|
||||
|
||||
private final File outputLocation;
|
||||
|
||||
public TestConfigurationMetadataAnnotationProcessor(File outputLocation) {
|
||||
this.outputLocation = outputLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String configurationPropertiesAnnotation() {
|
||||
return CONFIGURATION_PROPERTIES_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String nestedConfigurationPropertyAnnotation() {
|
||||
return NESTED_CONFIGURATION_PROPERTY_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String deprecatedConfigurationPropertyAnnotation() {
|
||||
return DEPRECATED_CONFIGURATION_PROPERTY_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String endpointAnnotation() {
|
||||
return ENDPOINT_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurationMetadata writeMetaData() throws Exception {
|
||||
super.writeMetaData();
|
||||
try {
|
||||
File metadataFile = new File(this.outputLocation,
|
||||
"META-INF/spring-configuration-metadata.json");
|
||||
if (metadataFile.isFile()) {
|
||||
this.metadata = new JsonMarshaller()
|
||||
.read(new FileInputStream(metadataFile));
|
||||
}
|
||||
else {
|
||||
this.metadata = new ConfigurationMetadata();
|
||||
}
|
||||
return this.metadata;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Failed to read metadata from disk", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurationMetadata getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
import org.springframework.boot.configurationsample.NestedConfigurationProperty;
|
||||
import org.springframework.boot.testsupport.compiler.TestCompiler;
|
||||
import org.springframework.boot.testsupport.compiler.TestCompiler.TestCompilationTask;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
/**
|
||||
* A TestProject contains a copy of a subset of test sample code.
|
||||
* <p>
|
||||
* Why a copy? Because when doing incremental build testing, we need to make modifications
|
||||
* to the contents of the 'test project'. But we don't want to actually modify the
|
||||
* original content itself.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
public class TestProject {
|
||||
|
||||
private static final Class<?>[] ALWAYS_INCLUDE = { ConfigurationProperties.class,
|
||||
NestedConfigurationProperty.class };
|
||||
|
||||
/**
|
||||
* Contains copies of the original source so we can modify it safely to test
|
||||
* incremental builds.
|
||||
*/
|
||||
private File sourceFolder;
|
||||
|
||||
private TestCompiler compiler;
|
||||
|
||||
private Set<File> sourceFiles = new LinkedHashSet<>();
|
||||
|
||||
public TestProject(TemporaryFolder tempFolder, Class<?>... classes)
|
||||
throws IOException {
|
||||
this.sourceFolder = tempFolder.newFolder();
|
||||
this.compiler = new TestCompiler(tempFolder) {
|
||||
@Override
|
||||
protected File getSourceFolder() {
|
||||
return TestProject.this.sourceFolder;
|
||||
}
|
||||
};
|
||||
Set<Class<?>> contents = new HashSet<>(Arrays.asList(classes));
|
||||
contents.addAll(Arrays.asList(ALWAYS_INCLUDE));
|
||||
copySources(contents);
|
||||
}
|
||||
|
||||
private void copySources(Set<Class<?>> contents) throws IOException {
|
||||
for (Class<?> type : contents) {
|
||||
copySources(type);
|
||||
}
|
||||
}
|
||||
|
||||
private void copySources(Class<?> type) throws IOException {
|
||||
File original = getOriginalSourceFile(type);
|
||||
File target = getSourceFile(type);
|
||||
target.getParentFile().mkdirs();
|
||||
FileCopyUtils.copy(original, target);
|
||||
this.sourceFiles.add(target);
|
||||
}
|
||||
|
||||
public File getSourceFile(Class<?> type) {
|
||||
return new File(this.sourceFolder, TestCompiler.sourcePathFor(type));
|
||||
}
|
||||
|
||||
public ConfigurationMetadata fullBuild() {
|
||||
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(
|
||||
this.compiler.getOutputLocation());
|
||||
TestCompilationTask task = this.compiler.getTask(this.sourceFiles);
|
||||
deleteFolderContents(this.compiler.getOutputLocation());
|
||||
task.call(processor);
|
||||
return processor.getMetadata();
|
||||
}
|
||||
|
||||
public ConfigurationMetadata incrementalBuild(Class<?>... toRecompile) {
|
||||
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(
|
||||
this.compiler.getOutputLocation());
|
||||
TestCompilationTask task = this.compiler.getTask(toRecompile);
|
||||
task.call(processor);
|
||||
return processor.getMetadata();
|
||||
}
|
||||
|
||||
private void deleteFolderContents(File outputFolder) {
|
||||
FileSystemUtils.deleteRecursively(outputFolder);
|
||||
outputFolder.mkdirs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve File relative to project's output folder.
|
||||
* @param relativePath the relative path
|
||||
* @return the output file
|
||||
*/
|
||||
public File getOutputFile(String relativePath) {
|
||||
Assert.assertFalse(new File(relativePath).isAbsolute());
|
||||
return new File(this.compiler.getOutputLocation(), relativePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add source code at the end of file, just before last '}'
|
||||
* @param target the target
|
||||
* @param snippetStream the snippet stream
|
||||
* @throws Exception if the source cannot be added
|
||||
*/
|
||||
public void addSourceCode(Class<?> target, InputStream snippetStream)
|
||||
throws Exception {
|
||||
File targetFile = getSourceFile(target);
|
||||
String contents = getContents(targetFile);
|
||||
int insertAt = contents.lastIndexOf('}');
|
||||
String additionalSource = FileCopyUtils
|
||||
.copyToString(new InputStreamReader(snippetStream));
|
||||
contents = contents.substring(0, insertAt) + additionalSource
|
||||
+ contents.substring(insertAt);
|
||||
putContents(targetFile, contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete source file for given class from project.
|
||||
* @param type the class to delete
|
||||
*/
|
||||
public void delete(Class<?> type) {
|
||||
File target = getSourceFile(type);
|
||||
target.delete();
|
||||
this.sourceFiles.remove(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore source code of given class to its original contents.
|
||||
* @param type the class to revert
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
public void revert(Class<?> type) throws IOException {
|
||||
Assert.assertTrue(getSourceFile(type).exists());
|
||||
copySources(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add source code of given class to this project.
|
||||
* @param type the class to add
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
public void add(Class<?> type) throws IOException {
|
||||
Assert.assertFalse(getSourceFile(type).exists());
|
||||
copySources(type);
|
||||
}
|
||||
|
||||
public void replaceText(Class<?> type, String find, String replace) throws Exception {
|
||||
File target = getSourceFile(type);
|
||||
String contents = getContents(target);
|
||||
contents = contents.replace(find, replace);
|
||||
putContents(target, contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the 'original' source code for given test class. Clients or subclasses should
|
||||
* have no need to know about these. They should work only with the copied source
|
||||
* code.
|
||||
*/
|
||||
private File getOriginalSourceFile(Class<?> type) {
|
||||
return new File(TestCompiler.SOURCE_FOLDER, TestCompiler.sourcePathFor(type));
|
||||
}
|
||||
|
||||
private static void putContents(File targetFile, String contents)
|
||||
throws FileNotFoundException, IOException, UnsupportedEncodingException {
|
||||
FileCopyUtils.copy(new StringReader(contents), new FileWriter(targetFile));
|
||||
}
|
||||
|
||||
private static String getContents(File file) throws Exception {
|
||||
return FileCopyUtils.copyToString(new FileReader(file));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import org.springframework.boot.configurationsample.fieldvalues.FieldValues;
|
||||
import org.springframework.boot.testsupport.compiler.TestCompiler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link FieldValuesParser} tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractFieldValuesProcessorTests {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
protected abstract FieldValuesParser createProcessor(ProcessingEnvironment env);
|
||||
|
||||
@Test
|
||||
public void getFieldValues() throws Exception {
|
||||
TestProcessor processor = new TestProcessor();
|
||||
TestCompiler compiler = new TestCompiler(this.temporaryFolder);
|
||||
compiler.getTask(FieldValues.class).call(processor);
|
||||
Map<String, Object> values = processor.getValues();
|
||||
assertThat(values.get("string")).isEqualTo("1");
|
||||
assertThat(values.get("stringNone")).isNull();
|
||||
assertThat(values.get("stringConst")).isEqualTo("c");
|
||||
assertThat(values.get("bool")).isEqualTo(true);
|
||||
assertThat(values.get("boolNone")).isEqualTo(false);
|
||||
assertThat(values.get("boolConst")).isEqualTo(true);
|
||||
assertThat(values.get("boolObject")).isEqualTo(true);
|
||||
assertThat(values.get("boolObjectNone")).isNull();
|
||||
assertThat(values.get("boolObjectConst")).isEqualTo(true);
|
||||
assertThat(values.get("integer")).isEqualTo(1);
|
||||
assertThat(values.get("integerNone")).isEqualTo(0);
|
||||
assertThat(values.get("integerConst")).isEqualTo(2);
|
||||
assertThat(values.get("integerObject")).isEqualTo(3);
|
||||
assertThat(values.get("integerObjectNone")).isNull();
|
||||
assertThat(values.get("integerObjectConst")).isEqualTo(4);
|
||||
assertThat(values.get("charset")).isEqualTo("US-ASCII");
|
||||
assertThat(values.get("charsetConst")).isEqualTo("UTF-8");
|
||||
assertThat(values.get("mimeType")).isEqualTo("text/html");
|
||||
assertThat(values.get("mimeTypeConst")).isEqualTo("text/plain");
|
||||
assertThat(values.get("object")).isEqualTo(123);
|
||||
assertThat(values.get("objectNone")).isNull();
|
||||
assertThat(values.get("objectConst")).isEqualTo("c");
|
||||
assertThat(values.get("objectInstance")).isNull();
|
||||
assertThat(values.get("stringArray")).isEqualTo(new Object[] { "FOO", "BAR" });
|
||||
assertThat(values.get("stringArrayNone")).isNull();
|
||||
assertThat(values.get("stringEmptyArray")).isEqualTo(new Object[0]);
|
||||
assertThat(values.get("stringArrayConst")).isEqualTo(new Object[] { "OK", "KO" });
|
||||
assertThat(values.get("stringArrayConstElements"))
|
||||
.isEqualTo(new Object[] { "c" });
|
||||
assertThat(values.get("integerArray")).isEqualTo(new Object[] { 42, 24 });
|
||||
assertThat(values.get("unknownArray")).isNull();
|
||||
}
|
||||
|
||||
@SupportedAnnotationTypes({
|
||||
"org.springframework.boot.configurationsample.ConfigurationProperties" })
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_6)
|
||||
private class TestProcessor extends AbstractProcessor {
|
||||
|
||||
private FieldValuesParser processor;
|
||||
|
||||
private Map<String, Object> values = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment env) {
|
||||
this.processor = createProcessor(env);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations,
|
||||
RoundEnvironment roundEnv) {
|
||||
for (TypeElement annotation : annotations) {
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
|
||||
if (element instanceof TypeElement) {
|
||||
try {
|
||||
this.values.putAll(
|
||||
this.processor.getFieldValues((TypeElement) element));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<String, Object> getValues() {
|
||||
return this.values;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.fieldvalues.javac;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.AbstractFieldValuesProcessorTests;
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.FieldValuesParser;
|
||||
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link JavaCompilerFieldValuesParser}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class JavaCompilerFieldValuesProcessorTests
|
||||
extends AbstractFieldValuesProcessorTests {
|
||||
|
||||
@Override
|
||||
protected FieldValuesParser createProcessor(ProcessingEnvironment env) {
|
||||
try {
|
||||
return new JavaCompilerFieldValuesParser(env);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
assumeNoException(ex);
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigurationMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ConfigurationMetadataTests {
|
||||
|
||||
@Test
|
||||
public void toDashedCaseCamelCase() {
|
||||
assertThat(toDashedCase("simpleCamelCase")).isEqualTo("simple-camel-case");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseUpperCamelCaseSuffix() {
|
||||
assertThat(toDashedCase("myDLQ")).isEqualTo("my-d-l-q");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseUpperCamelCaseMiddle() {
|
||||
assertThat(toDashedCase("someDLQKey")).isEqualTo("some-d-l-q-key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseWordsUnderscore() {
|
||||
assertThat(toDashedCase("Word_With_underscore"))
|
||||
.isEqualTo("word-with-underscore");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseWordsSeveralUnderscores() {
|
||||
assertThat(toDashedCase("Word___With__underscore"))
|
||||
.isEqualTo("word---with--underscore");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseLowerCaseUnderscore() {
|
||||
assertThat(toDashedCase("lower_underscore")).isEqualTo("lower-underscore");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseUpperUnderscoreSuffix() {
|
||||
assertThat(toDashedCase("my_DLQ")).isEqualTo("my-d-l-q");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseUpperUnderscoreMiddle() {
|
||||
assertThat(toDashedCase("some_DLQ_key")).isEqualTo("some-d-l-q-key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseMultipleUnderscores() {
|
||||
assertThat(toDashedCase("super___crazy")).isEqualTo("super---crazy");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toDashedCaseLowercase() {
|
||||
assertThat(toDashedCase("lowercase")).isEqualTo("lowercase");
|
||||
}
|
||||
|
||||
private String toDashedCase(String name) {
|
||||
return ConfigurationMetadata.toDashedCase(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JsonMarshaller}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class JsonMarshallerTests {
|
||||
|
||||
@Test
|
||||
public void marshallAndUnmarshal() throws Exception {
|
||||
ConfigurationMetadata metadata = new ConfigurationMetadata();
|
||||
metadata.add(ItemMetadata.newProperty("a", "b", StringBuffer.class.getName(),
|
||||
InputStream.class.getName(), "sourceMethod", "desc", "x",
|
||||
new ItemDeprecation("Deprecation comment", "b.c.d")));
|
||||
metadata.add(ItemMetadata.newProperty("b.c.d", null, null, null, null, null, null,
|
||||
null));
|
||||
metadata.add(
|
||||
ItemMetadata.newProperty("c", null, null, null, null, null, 123, null));
|
||||
metadata.add(
|
||||
ItemMetadata.newProperty("d", null, null, null, null, null, true, null));
|
||||
metadata.add(ItemMetadata.newProperty("e", null, null, null, null, null,
|
||||
new String[] { "y", "n" }, null));
|
||||
metadata.add(ItemMetadata.newProperty("f", null, null, null, null, null,
|
||||
new Boolean[] { true, false }, null));
|
||||
metadata.add(ItemMetadata.newGroup("d", null, null, null));
|
||||
metadata.add(ItemHint.newHint("a.b"));
|
||||
metadata.add(ItemHint.newHint("c", new ItemHint.ValueHint(123, "hey"),
|
||||
new ItemHint.ValueHint(456, null)));
|
||||
metadata.add(new ItemHint("d", null,
|
||||
Arrays.asList(
|
||||
new ItemHint.ValueProvider("first",
|
||||
Collections.<String, Object>singletonMap("target",
|
||||
"foo")),
|
||||
new ItemHint.ValueProvider("second", null))));
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
JsonMarshaller marshaller = new JsonMarshaller();
|
||||
marshaller.write(metadata, outputStream);
|
||||
ConfigurationMetadata read = marshaller
|
||||
.read(new ByteArrayInputStream(outputStream.toByteArray()));
|
||||
assertThat(read).has(Metadata.withProperty("a.b", StringBuffer.class)
|
||||
.fromSource(InputStream.class).withDescription("desc")
|
||||
.withDefaultValue("x").withDeprecation("Deprecation comment", "b.c.d"));
|
||||
assertThat(read).has(Metadata.withProperty("b.c.d"));
|
||||
assertThat(read).has(Metadata.withProperty("c").withDefaultValue(123));
|
||||
assertThat(read).has(Metadata.withProperty("d").withDefaultValue(true));
|
||||
assertThat(read).has(
|
||||
Metadata.withProperty("e").withDefaultValue(new String[] { "y", "n" }));
|
||||
assertThat(read).has(Metadata.withProperty("f")
|
||||
.withDefaultValue(new Object[] { true, false }));
|
||||
assertThat(read).has(Metadata.withGroup("d"));
|
||||
assertThat(read).has(Metadata.withHint("a.b"));
|
||||
assertThat(read).has(
|
||||
Metadata.withHint("c").withValue(0, 123, "hey").withValue(1, 456, null));
|
||||
assertThat(read).has(Metadata.withHint("d").withProvider("first", "target", "foo")
|
||||
.withProvider("second"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.assertj.core.api.Condition;
|
||||
import org.hamcrest.collection.IsMapContaining;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata.ItemType;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* AssertJ {@link Condition} to help test {@link ConfigurationMetadata}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public final class Metadata {
|
||||
|
||||
private Metadata() {
|
||||
}
|
||||
|
||||
public static MetadataItemCondition withGroup(String name) {
|
||||
return new MetadataItemCondition(ItemType.GROUP, name);
|
||||
}
|
||||
|
||||
public static MetadataItemCondition withGroup(String name, Class<?> type) {
|
||||
return new MetadataItemCondition(ItemType.GROUP, name).ofType(type);
|
||||
}
|
||||
|
||||
public static MetadataItemCondition withGroup(String name, String type) {
|
||||
return new MetadataItemCondition(ItemType.GROUP, name).ofType(type);
|
||||
}
|
||||
|
||||
public static MetadataItemCondition withProperty(String name) {
|
||||
return new MetadataItemCondition(ItemType.PROPERTY, name);
|
||||
}
|
||||
|
||||
public static MetadataItemCondition withProperty(String name, Class<?> type) {
|
||||
return new MetadataItemCondition(ItemType.PROPERTY, name).ofType(type);
|
||||
}
|
||||
|
||||
public static MetadataItemCondition withProperty(String name, String type) {
|
||||
return new MetadataItemCondition(ItemType.PROPERTY, name).ofType(type);
|
||||
}
|
||||
|
||||
public static Metadata.MetadataItemCondition withEnabledFlag(String key) {
|
||||
return withProperty(key).ofType(Boolean.class);
|
||||
}
|
||||
|
||||
public static MetadataHintCondition withHint(String name) {
|
||||
return new MetadataHintCondition(name);
|
||||
}
|
||||
|
||||
public static class MetadataItemCondition extends Condition<ConfigurationMetadata> {
|
||||
|
||||
private final ItemType itemType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String type;
|
||||
|
||||
private final Class<?> sourceType;
|
||||
|
||||
private final String sourceMethod;
|
||||
|
||||
private final String description;
|
||||
|
||||
private final Object defaultValue;
|
||||
|
||||
private final ItemDeprecation deprecation;
|
||||
|
||||
public MetadataItemCondition(ItemType itemType, String name) {
|
||||
this(itemType, name, null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public MetadataItemCondition(ItemType itemType, String name, String type,
|
||||
Class<?> sourceType, String sourceMethod, String description,
|
||||
Object defaultValue, ItemDeprecation deprecation) {
|
||||
this.itemType = itemType;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.sourceType = sourceType;
|
||||
this.sourceMethod = sourceMethod;
|
||||
this.description = description;
|
||||
this.defaultValue = defaultValue;
|
||||
this.deprecation = deprecation;
|
||||
describedAs(createDescription());
|
||||
}
|
||||
|
||||
private String createDescription() {
|
||||
StringBuilder description = new StringBuilder();
|
||||
description.append("an item named '" + this.name + "'");
|
||||
if (this.type != null) {
|
||||
description.append(" with dataType:").append(this.type);
|
||||
}
|
||||
if (this.sourceType != null) {
|
||||
description.append(" with sourceType:").append(this.sourceType);
|
||||
}
|
||||
if (this.sourceMethod != null) {
|
||||
description.append(" with sourceMethod:").append(this.sourceMethod);
|
||||
}
|
||||
if (this.defaultValue != null) {
|
||||
description.append(" with defaultValue:").append(this.defaultValue);
|
||||
}
|
||||
if (this.description != null) {
|
||||
description.append(" with description:").append(this.description);
|
||||
}
|
||||
if (this.deprecation != null) {
|
||||
description.append(" with deprecation:").append(this.deprecation);
|
||||
}
|
||||
return description.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ConfigurationMetadata value) {
|
||||
ItemMetadata itemMetadata = getFirstItemWithName(value, this.name);
|
||||
if (itemMetadata == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.type != null && !this.type.equals(itemMetadata.getType())) {
|
||||
return false;
|
||||
}
|
||||
if (this.sourceType != null
|
||||
&& !this.sourceType.getName().equals(itemMetadata.getSourceType())) {
|
||||
return false;
|
||||
}
|
||||
if (this.sourceMethod != null
|
||||
&& !this.sourceMethod.equals(itemMetadata.getSourceMethod())) {
|
||||
return false;
|
||||
}
|
||||
if (this.defaultValue != null && !ObjectUtils
|
||||
.nullSafeEquals(this.defaultValue, itemMetadata.getDefaultValue())) {
|
||||
return false;
|
||||
}
|
||||
if (this.defaultValue == null && itemMetadata.getDefaultValue() != null) {
|
||||
return false;
|
||||
}
|
||||
if (this.description != null
|
||||
&& !this.description.equals(itemMetadata.getDescription())) {
|
||||
return false;
|
||||
}
|
||||
if (this.deprecation == null && itemMetadata.getDeprecation() != null) {
|
||||
return false;
|
||||
}
|
||||
if (this.deprecation != null
|
||||
&& !this.deprecation.equals(itemMetadata.getDeprecation())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public MetadataItemCondition ofType(Class<?> dataType) {
|
||||
return new MetadataItemCondition(this.itemType, this.name, dataType.getName(),
|
||||
this.sourceType, this.sourceMethod, this.description,
|
||||
this.defaultValue, this.deprecation);
|
||||
}
|
||||
|
||||
public MetadataItemCondition ofType(String dataType) {
|
||||
return new MetadataItemCondition(this.itemType, this.name, dataType,
|
||||
this.sourceType, this.sourceMethod, this.description,
|
||||
this.defaultValue, this.deprecation);
|
||||
}
|
||||
|
||||
public MetadataItemCondition fromSource(Class<?> sourceType) {
|
||||
return new MetadataItemCondition(this.itemType, this.name, this.type,
|
||||
sourceType, this.sourceMethod, this.description, this.defaultValue,
|
||||
this.deprecation);
|
||||
}
|
||||
|
||||
public MetadataItemCondition fromSourceMethod(String sourceMethod) {
|
||||
return new MetadataItemCondition(this.itemType, this.name, this.type,
|
||||
this.sourceType, sourceMethod, this.description, this.defaultValue,
|
||||
this.deprecation);
|
||||
}
|
||||
|
||||
public MetadataItemCondition withDescription(String description) {
|
||||
return new MetadataItemCondition(this.itemType, this.name, this.type,
|
||||
this.sourceType, this.sourceMethod, description, this.defaultValue,
|
||||
this.deprecation);
|
||||
}
|
||||
|
||||
public MetadataItemCondition withDefaultValue(Object defaultValue) {
|
||||
return new MetadataItemCondition(this.itemType, this.name, this.type,
|
||||
this.sourceType, this.sourceMethod, this.description, defaultValue,
|
||||
this.deprecation);
|
||||
}
|
||||
|
||||
public MetadataItemCondition withDeprecation(String reason, String replacement) {
|
||||
return withDeprecation(reason, replacement, null);
|
||||
}
|
||||
|
||||
public MetadataItemCondition withDeprecation(String reason, String replacement,
|
||||
String level) {
|
||||
return new MetadataItemCondition(this.itemType, this.name, this.type,
|
||||
this.sourceType, this.sourceMethod, this.description,
|
||||
this.defaultValue, new ItemDeprecation(reason, replacement, level));
|
||||
}
|
||||
|
||||
public MetadataItemCondition withNoDeprecation() {
|
||||
return new MetadataItemCondition(this.itemType, this.name, this.type,
|
||||
this.sourceType, this.sourceMethod, this.description,
|
||||
this.defaultValue, null);
|
||||
}
|
||||
|
||||
private ItemMetadata getFirstItemWithName(ConfigurationMetadata metadata,
|
||||
String name) {
|
||||
for (ItemMetadata item : metadata.getItems()) {
|
||||
if (item.isOfItemType(this.itemType) && name.equals(item.getName())) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class MetadataHintCondition extends Condition<ConfigurationMetadata> {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final List<ItemHintValueCondition> valueConditions;
|
||||
|
||||
private final List<ItemHintProviderCondition> providerConditions;
|
||||
|
||||
public MetadataHintCondition(String name) {
|
||||
this.name = name;
|
||||
this.valueConditions = Collections.emptyList();
|
||||
this.providerConditions = Collections.emptyList();
|
||||
}
|
||||
|
||||
public MetadataHintCondition(String name,
|
||||
List<ItemHintValueCondition> valueConditions,
|
||||
List<ItemHintProviderCondition> providerConditions) {
|
||||
this.name = name;
|
||||
this.valueConditions = valueConditions;
|
||||
this.providerConditions = providerConditions;
|
||||
describedAs(createDescription());
|
||||
}
|
||||
|
||||
private String createDescription() {
|
||||
StringBuilder description = new StringBuilder();
|
||||
description.append("a hints name '" + this.name + "'");
|
||||
if (!this.valueConditions.isEmpty()) {
|
||||
description.append(" with values:").append(this.valueConditions);
|
||||
}
|
||||
if (!this.providerConditions.isEmpty()) {
|
||||
description.append(" with providers:").append(this.providerConditions);
|
||||
}
|
||||
return description.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ConfigurationMetadata metadata) {
|
||||
ItemHint itemHint = getFirstHintWithName(metadata, this.name);
|
||||
if (itemHint == null) {
|
||||
return false;
|
||||
}
|
||||
return matches(itemHint, this.valueConditions)
|
||||
&& matches(itemHint, this.providerConditions);
|
||||
}
|
||||
|
||||
private boolean matches(ItemHint itemHint,
|
||||
List<? extends Condition<ItemHint>> conditions) {
|
||||
for (Condition<ItemHint> condition : conditions) {
|
||||
if (!condition.matches(itemHint)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ItemHint getFirstHintWithName(ConfigurationMetadata metadata,
|
||||
String name) {
|
||||
for (ItemHint hint : metadata.getHints()) {
|
||||
if (name.equals(hint.getName())) {
|
||||
return hint;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public MetadataHintCondition withValue(int index, Object value,
|
||||
String description) {
|
||||
return new MetadataHintCondition(this.name,
|
||||
add(this.valueConditions,
|
||||
new ItemHintValueCondition(index, value, description)),
|
||||
this.providerConditions);
|
||||
}
|
||||
|
||||
public MetadataHintCondition withProvider(String provider) {
|
||||
return withProvider(this.providerConditions.size(), provider, null);
|
||||
}
|
||||
|
||||
public MetadataHintCondition withProvider(String provider, String key,
|
||||
Object value) {
|
||||
return withProvider(this.providerConditions.size(), provider,
|
||||
Collections.singletonMap(key, value));
|
||||
}
|
||||
|
||||
public MetadataHintCondition withProvider(int index, String provider,
|
||||
Map<String, Object> parameters) {
|
||||
return new MetadataHintCondition(this.name, this.valueConditions,
|
||||
add(this.providerConditions,
|
||||
new ItemHintProviderCondition(index, provider, parameters)));
|
||||
}
|
||||
|
||||
private <T> List<T> add(List<T> items, T item) {
|
||||
List<T> result = new ArrayList<>(items);
|
||||
result.add(item);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ItemHintValueCondition extends Condition<ItemHint> {
|
||||
|
||||
private final int index;
|
||||
|
||||
private final Object value;
|
||||
|
||||
private final String description;
|
||||
|
||||
ItemHintValueCondition(int index, Object value, String description) {
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
describedAs(createDescription());
|
||||
}
|
||||
|
||||
private String createDescription() {
|
||||
StringBuilder description = new StringBuilder();
|
||||
description.append("value hint at index '" + this.index + "'");
|
||||
if (this.value != null) {
|
||||
description.append(" with value:").append(this.value);
|
||||
}
|
||||
if (this.description != null) {
|
||||
description.append(" with description:").append(this.description);
|
||||
}
|
||||
return description.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ItemHint value) {
|
||||
if (this.index + 1 > value.getValues().size()) {
|
||||
return false;
|
||||
}
|
||||
ItemHint.ValueHint valueHint = value.getValues().get(this.index);
|
||||
if (this.value != null && !this.value.equals(valueHint.getValue())) {
|
||||
return false;
|
||||
}
|
||||
if (this.description != null
|
||||
&& !this.description.equals(valueHint.getDescription())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ItemHintProviderCondition extends Condition<ItemHint> {
|
||||
|
||||
private final int index;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final Map<String, Object> parameters;
|
||||
|
||||
ItemHintProviderCondition(int index, String name,
|
||||
Map<String, Object> parameters) {
|
||||
this.index = index;
|
||||
this.name = name;
|
||||
this.parameters = parameters;
|
||||
describedAs(createDescription());
|
||||
}
|
||||
|
||||
public String createDescription() {
|
||||
StringBuilder description = new StringBuilder();
|
||||
description.append("value provider");
|
||||
if (this.name != null) {
|
||||
description.append(" with name:").append(this.name);
|
||||
}
|
||||
if (this.parameters != null) {
|
||||
description.append(" with parameters:").append(this.parameters);
|
||||
}
|
||||
return description.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ItemHint hint) {
|
||||
if (this.index + 1 > hint.getProviders().size()) {
|
||||
return false;
|
||||
}
|
||||
ItemHint.ValueProvider valueProvider = hint.getProviders().get(this.index);
|
||||
if (this.name != null && !this.name.equals(valueProvider.getName())) {
|
||||
return false;
|
||||
}
|
||||
if (this.parameters != null) {
|
||||
for (Map.Entry<String, Object> entry : this.parameters.entrySet()) {
|
||||
if (!IsMapContaining.hasEntry(entry.getKey(), entry.getValue())
|
||||
.matches(valueProvider.getParameters())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationprocessor.metadata;
|
||||
|
||||
/**
|
||||
* {@link JsonConverter} for use in tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class TestJsonConverter extends JsonConverter {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Alternative to Spring Boot's {@code @ConfigurationProperties} for testing (removes the
|
||||
* need for a dependency on the real annotation).
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ConfigurationProperties {
|
||||
|
||||
String value() default "";
|
||||
|
||||
String prefix() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample;
|
||||
|
||||
public enum DefaultEnablement {
|
||||
|
||||
ENABLED, DISABLED, NEUTRAL
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a getter in a {@link ConfigurationProperties} object is deprecated. This
|
||||
* annotation has no bearing on the actual binding processes, but it is used by the
|
||||
* {@code spring-boot-configuration-processor} to add deprecation meta-data.
|
||||
* <p>
|
||||
* This annotation <strong>must</strong> be used on the getter of the deprecated element.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface DeprecatedConfigurationProperty {
|
||||
|
||||
/**
|
||||
* The reason for the deprecation.
|
||||
* @return the deprecation reason
|
||||
*/
|
||||
String reason() default "";
|
||||
|
||||
/**
|
||||
* The field that should be used instead (if any).
|
||||
* @return the replacement field
|
||||
*/
|
||||
String replacement() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Alternative to Spring Boot's {@code @Endpoint} for testing (removes the need for a
|
||||
* dependency on the real annotation).
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Endpoint {
|
||||
|
||||
String id();
|
||||
|
||||
DefaultEnablement defaultEnablement() default DefaultEnablement.NEUTRAL;
|
||||
|
||||
EndpointExposure[] exposure() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample;
|
||||
|
||||
public enum EndpointExposure {
|
||||
|
||||
JMX,
|
||||
|
||||
WEB
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2014 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Alternative to Spring Boot's {@code @NestedConfigurationProperty} for testing (removes
|
||||
* the need for a dependency on the real annotation).
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface NestedConfigurationProperty {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample.endpoint;
|
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
|
||||
/**
|
||||
* An endpoint with additional custom properties.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Endpoint(id = "customprops")
|
||||
@ConfigurationProperties("endpoints.customprops")
|
||||
public class CustomPropertiesEndpoint {
|
||||
|
||||
private String name = "test";
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample.endpoint;
|
||||
|
||||
import org.springframework.boot.configurationsample.DefaultEnablement;
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
|
||||
/**
|
||||
* An endpoint that is disabled unless configured explicitly.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Endpoint(id = "disabled", defaultEnablement = DefaultEnablement.DISABLED)
|
||||
public class DisabledEndpoint {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample.endpoint;
|
||||
|
||||
import org.springframework.boot.configurationsample.DefaultEnablement;
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
|
||||
/**
|
||||
* An endpoint that is enabled unless configured explicitly..
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Endpoint(id = "enabled", defaultEnablement = DefaultEnablement.ENABLED)
|
||||
public class EnabledEndpoint {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample.endpoint;
|
||||
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
import org.springframework.boot.configurationsample.EndpointExposure;
|
||||
|
||||
/**
|
||||
* An endpoint that only exposes a JMX MBean.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Endpoint(id = "jmx", exposure = EndpointExposure.JMX)
|
||||
public class OnlyJmxEndpoint {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample.endpoint;
|
||||
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
import org.springframework.boot.configurationsample.EndpointExposure;
|
||||
|
||||
/**
|
||||
* An endpoints that only exposes a web endpoint.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Endpoint(id = "web", exposure = EndpointExposure.WEB)
|
||||
public class OnlyWebEndpoint {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample.endpoint;
|
||||
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
|
||||
/**
|
||||
* A simple endpoint with no default override.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Endpoint(id = "simple")
|
||||
public class SimpleEndpoint {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample.endpoint.incremental;
|
||||
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
|
||||
/**
|
||||
* An endpoint that is enabled by default.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Endpoint(id = "incremental")
|
||||
public class IncrementalEndpoint {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.boot.configurationsample.endpoint.incremental;
|
||||
|
||||
import org.springframework.boot.configurationsample.Endpoint;
|
||||
import org.springframework.boot.configurationsample.EndpointExposure;
|
||||
|
||||
/**
|
||||
* An endpoint that only exposes a JMX MBean.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Endpoint(id = "incremental", exposure = EndpointExposure.JMX)
|
||||
public class IncrementalJmxEndpoint {
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user