Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
2dfd916c
Commit
2dfd916c
authored
Apr 11, 2019
by
Phillip Webb
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Polish
parent
dabe75a2
Changes
33
Show whitespace changes
Inline
Side-by-side
Showing
33 changed files
with
512 additions
and
363 deletions
+512
-363
ReactiveCloudFoundryActuatorAutoConfiguration.java
...active/ReactiveCloudFoundryActuatorAutoConfiguration.java
+5
-7
CloudFoundryActuatorAutoConfiguration.java
...oundry/servlet/CloudFoundryActuatorAutoConfiguration.java
+5
-7
PrometheusMetricsExportAutoConfiguration.java
.../prometheus/PrometheusMetricsExportAutoConfiguration.java
+2
-1
ManagementContextAutoConfiguration.java
...figure/web/server/ManagementContextAutoConfiguration.java
+2
-2
ManagementContextAutoConfigurationTests.java
...e/web/server/ManagementContextAutoConfigurationTests.java
+0
-1
HttpTraceWebFilter.java
...k/boot/actuate/web/trace/reactive/HttpTraceWebFilter.java
+1
-1
FlywayMigrationScriptMissingFailureAnalyzer.java
...e/flyway/FlywayMigrationScriptMissingFailureAnalyzer.java
+6
-9
MongoReactiveAutoConfiguration.java
...t/autoconfigure/mongo/MongoReactiveAutoConfiguration.java
+29
-26
RSocketNettyServerCustomizer.java
...t/autoconfigure/rsocket/RSocketNettyServerCustomizer.java
+1
-1
RSocketProperties.java
...amework/boot/autoconfigure/rsocket/RSocketProperties.java
+1
-1
SpringBootContextLoader.java
...gframework/boot/test/context/SpringBootContextLoader.java
+3
-1
AsciidocBuilder.java
...ringframework/boot/configurationdocs/AsciidocBuilder.java
+52
-0
CompoundConfigurationTableEntry.java
...ot/configurationdocs/CompoundConfigurationTableEntry.java
+9
-8
ConfigurationMetadataDocumentWriter.java
...onfigurationdocs/ConfigurationMetadataDocumentWriter.java
+74
-61
ConfigurationTable.java
...gframework/boot/configurationdocs/ConfigurationTable.java
+10
-11
ConfigurationTableEntry.java
...ework/boot/configurationdocs/ConfigurationTableEntry.java
+9
-14
SingleConfigurationTableEntry.java
...boot/configurationdocs/SingleConfigurationTableEntry.java
+85
-0
CompoundConfigurationTableEntryTests.java
...nfigurationdocs/CompoundConfigurationTableEntryTests.java
+7
-9
ConfigurationTableTests.java
...ework/boot/configurationdocs/ConfigurationTableTests.java
+4
-6
SingleConfigurationTableEntryTests.java
...configurationdocs/SingleConfigurationTableEntryTests.java
+21
-31
ConstructorParameterPropertyDescriptor.java
...tionprocessor/ConstructorParameterPropertyDescriptor.java
+25
-18
MetadataGenerationEnvironment.java
...configurationprocessor/MetadataGenerationEnvironment.java
+20
-21
TypeUtils.java
...pringframework/boot/configurationprocessor/TypeUtils.java
+2
-3
CloudPlatform.java
...in/java/org/springframework/boot/cloud/CloudPlatform.java
+30
-15
ConfigFileApplicationListener.java
...rk/boot/context/config/ConfigFileApplicationListener.java
+4
-14
ConfigurationPropertiesBeanDefinitionRegistrar.java
...rties/ConfigurationPropertiesBeanDefinitionRegistrar.java
+3
-5
ConfigurationPropertiesScanRegistrar.java
...text/properties/ConfigurationPropertiesScanRegistrar.java
+18
-12
EnableConfigurationPropertiesImportSelector.java
...operties/EnableConfigurationPropertiesImportSelector.java
+2
-3
ConstructorParametersBinder.java
.../context/properties/bind/ConstructorParametersBinder.java
+62
-62
DefaultLogbackConfiguration.java
...ork/boot/logging/logback/DefaultLogbackConfiguration.java
+8
-10
Log4J2LoggingSystemTests.java
...amework/boot/logging/log4j2/Log4J2LoggingSystemTests.java
+2
-2
SampleController.java
...cat/src/main/java/sample/tomcat/web/SampleController.java
+8
-1
application.properties
...t-sample-tomcat/src/main/resources/application.properties
+2
-0
No files found.
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/reactive/ReactiveCloudFoundryActuatorAutoConfiguration.java
View file @
2dfd916c
...
...
@@ -103,13 +103,11 @@ public class ReactiveCloudFoundryActuatorAutoConfiguration {
public
CloudFoundryInfoEndpointWebExtension
cloudFoundryInfoEndpointWebExtension
(
GitProperties
properties
,
ObjectProvider
<
InfoContributor
>
infoContributors
)
{
List
<
InfoContributor
>
contributors
=
infoContributors
.
orderedStream
()
.
map
((
infoContributor
)
->
{
if
(
infoContributor
instanceof
GitInfoContributor
)
{
return
new
GitInfoContributor
(
properties
,
InfoPropertiesInfoContributor
.
Mode
.
FULL
);
}
return
infoContributor
;
}).
collect
(
Collectors
.
toList
());
.
map
((
infoContributor
)
->
(
infoContributor
instanceof
GitInfoContributor
)
?
new
GitInfoContributor
(
properties
,
InfoPropertiesInfoContributor
.
Mode
.
FULL
)
:
infoContributor
)
.
collect
(
Collectors
.
toList
());
return
new
CloudFoundryInfoEndpointWebExtension
(
new
InfoEndpoint
(
contributors
));
}
...
...
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.java
View file @
2dfd916c
...
...
@@ -104,13 +104,11 @@ public class CloudFoundryActuatorAutoConfiguration {
public
CloudFoundryInfoEndpointWebExtension
cloudFoundryInfoEndpointWebExtension
(
GitProperties
properties
,
ObjectProvider
<
InfoContributor
>
infoContributors
)
{
List
<
InfoContributor
>
contributors
=
infoContributors
.
orderedStream
()
.
map
((
infoContributor
)
->
{
if
(
infoContributor
instanceof
GitInfoContributor
)
{
return
new
GitInfoContributor
(
properties
,
InfoPropertiesInfoContributor
.
Mode
.
FULL
);
}
return
infoContributor
;
}).
collect
(
Collectors
.
toList
());
.
map
((
infoContributor
)
->
(
infoContributor
instanceof
GitInfoContributor
)
?
new
GitInfoContributor
(
properties
,
InfoPropertiesInfoContributor
.
Mode
.
FULL
)
:
infoContributor
)
.
collect
(
Collectors
.
toList
());
return
new
CloudFoundryInfoEndpointWebExtension
(
new
InfoEndpoint
(
contributors
));
}
...
...
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusMetricsExportAutoConfiguration.java
View file @
2dfd916c
...
...
@@ -48,6 +48,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.core.env.Environment
;
import
org.springframework.core.log.LogMessage
;
/**
* {@link EnableAutoConfiguration Auto-configuration} for exporting metrics to Prometheus.
...
...
@@ -142,7 +143,7 @@ public class PrometheusMetricsExportAutoConfiguration {
return
new
PushGateway
(
new
URL
(
url
));
}
catch
(
MalformedURLException
ex
)
{
logger
.
warn
(
String
.
format
(
logger
.
warn
(
LogMessage
.
format
(
"Invalid PushGateway base url '%s': update your configuration to a valid URL"
,
url
));
return
new
PushGateway
(
url
);
...
...
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.java
View file @
2dfd916c
...
...
@@ -157,8 +157,8 @@ public class ManagementContextAutoConfiguration {
AbstractApplicationContext
context
=
(
AbstractApplicationContext
)
this
.
applicationContext
;
List
<
BeanFactoryPostProcessor
>
postProcessors
=
context
.
getBeanFactoryPostProcessors
();
return
postProcessors
.
stream
().
anyMatch
(
(
postProcessor
)
->
postProcessor
instanceof
LazyInitializationBeanFactoryPostProcessor
);
return
postProcessors
.
stream
().
anyMatch
(
LazyInitializationBeanFactoryPostProcessor
.
class
::
isInstance
);
}
private
void
setClassLoaderIfPossible
(
ConfigurableApplicationContext
child
)
{
...
...
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfigurationTests.java
View file @
2dfd916c
...
...
@@ -56,7 +56,6 @@ public class ManagementContextAutoConfigurationTests {
contextRunner
.
withPropertyValues
(
"server.port=0"
,
"management.server.port=0"
).
run
(
(
context
)
->
assertThat
(
tomcatStartedOccurencesIn
(
this
.
output
.
toString
()))
.
isEqualTo
(
2
));
}
private
int
tomcatStartedOccurencesIn
(
String
output
)
{
...
...
spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/web/trace/reactive/HttpTraceWebFilter.java
View file @
2dfd916c
...
...
@@ -91,7 +91,7 @@ public class HttpTraceWebFilter implements WebFilter, Ordered {
Principal
principal
,
WebSession
session
)
{
ServerWebExchangeTraceableRequest
request
=
new
ServerWebExchangeTraceableRequest
(
exchange
);
final
HttpTrace
trace
=
this
.
tracer
.
receivedRequest
(
request
);
HttpTrace
trace
=
this
.
tracer
.
receivedRequest
(
request
);
exchange
.
getResponse
().
beforeCommit
(()
->
{
TraceableServerHttpResponse
response
=
new
TraceableServerHttpResponse
(
exchange
.
getResponse
());
...
...
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayMigrationScriptMissingFailureAnalyzer.java
View file @
2dfd916c
...
...
@@ -38,15 +38,12 @@ class FlywayMigrationScriptMissingFailureAnalyzer
.
append
(
"no migration scripts location is configured"
).
toString
(),
"Check your Flyway configuration"
,
cause
);
}
else
{
description
.
append
(
String
.
format
(
"none of the following migration scripts locations could be found:%n%n"
));
cause
.
getLocations
().
forEach
((
location
)
->
description
.
append
(
String
.
format
(
"\t- %s%n"
,
location
)));
cause
.
getLocations
().
forEach
(
(
location
)
->
description
.
append
(
String
.
format
(
"\t- %s%n"
,
location
)));
return
new
FailureAnalysis
(
description
.
toString
(),
"Review the locations above or check your Flyway configuration"
,
cause
);
}
"Review the locations above or check your Flyway configuration"
,
cause
);
}
}
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoReactiveAutoConfiguration.java
View file @
2dfd916c
...
...
@@ -74,6 +74,11 @@ public class MongoReactiveAutoConfiguration {
return
new
NettyDriverMongoClientSettingsBuilderCustomizer
(
settings
);
}
}
/**
* {@link MongoClientSettingsBuilderCustomizer} to apply Mongo client settings.
*/
private
static
final
class
NettyDriverMongoClientSettingsBuilderCustomizer
implements
MongoClientSettingsBuilderCustomizer
,
DisposableBean
{
...
...
@@ -111,6 +116,4 @@ public class MongoReactiveAutoConfiguration {
}
}
}
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketNettyServerCustomizer.java
View file @
2dfd916c
...
...
@@ -43,7 +43,7 @@ class RSocketNettyServerCustomizer implements NettyServerCustomizer {
@Override
public
HttpServer
apply
(
HttpServer
httpServer
)
{
final
ServerTransport
.
ConnectionAcceptor
acceptor
=
RSocketFactory
.
receive
()
ServerTransport
.
ConnectionAcceptor
acceptor
=
RSocketFactory
.
receive
()
.
acceptor
(
this
.
messageHandlerAcceptor
).
toConnectionAcceptor
();
return
httpServer
.
route
((
routes
)
->
routes
.
ws
(
this
.
mappingPath
,
WebsocketRouteTransport
.
newHandler
(
acceptor
)));
...
...
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/rsocket/RSocketProperties.java
View file @
2dfd916c
...
...
@@ -29,7 +29,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties
(
"spring.rsocket"
)
public
class
RSocketProperties
{
private
Server
server
=
new
Server
();
private
final
Server
server
=
new
Server
();
public
Server
getServer
()
{
return
this
.
server
;
...
...
spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java
View file @
2dfd916c
...
...
@@ -77,6 +77,8 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
*/
public
class
SpringBootContextLoader
extends
AbstractContextLoader
{
private
static
final
String
[]
NO_ARGS
=
new
String
[
0
];
@Override
public
ApplicationContext
loadContext
(
MergedContextConfiguration
config
)
throws
Exception
{
...
...
@@ -155,7 +157,7 @@ public class SpringBootContextLoader extends AbstractContextLoader {
protected
String
[]
getArgs
(
MergedContextConfiguration
config
)
{
SpringBootTest
annotation
=
AnnotatedElementUtils
.
findMergedAnnotation
(
config
.
getTestClass
(),
SpringBootTest
.
class
);
return
(
annotation
!=
null
)
?
annotation
.
args
()
:
new
String
[
0
]
;
return
(
annotation
!=
null
)
?
annotation
.
args
()
:
NO_ARGS
;
}
private
void
setActiveProfiles
(
ConfigurableEnvironment
environment
,
...
...
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/AsciidocBuilder.java
0 → 100644
View file @
2dfd916c
/*
* Copyright 2012-2019 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
*
* https://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
.
configurationdocs
;
/**
* Simple builder to help construct Asciidoc markup.
*
* @author Phillip Webb
*/
class
AsciidocBuilder
{
private
static
final
String
NEWLINE
=
System
.
lineSeparator
();
private
final
StringBuilder
content
;
AsciidocBuilder
()
{
this
.
content
=
new
StringBuilder
();
}
public
AsciidocBuilder
appendln
(
Object
...
items
)
{
append
(
items
);
append
(
NEWLINE
);
return
this
;
}
public
AsciidocBuilder
append
(
Object
...
items
)
{
for
(
Object
item
:
items
)
{
this
.
content
.
append
(
item
);
}
return
this
;
}
@Override
public
String
toString
()
{
return
this
.
content
.
toString
();
}
}
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/Compound
Key
Entry.java
→
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/Compound
ConfigurationTable
Entry.java
View file @
2dfd916c
...
...
@@ -27,29 +27,30 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataPrope
*
* @author Brian Clozel
*/
class
Compound
KeyEntry
extends
AbstractConfiguration
Entry
{
class
Compound
ConfigurationTableEntry
extends
ConfigurationTable
Entry
{
private
Set
<
String
>
configurationKeys
;
private
String
description
;
Compound
Key
Entry
(
String
key
,
String
description
)
{
Compound
ConfigurationTable
Entry
(
String
key
,
String
description
)
{
this
.
key
=
key
;
this
.
description
=
description
;
this
.
configurationKeys
=
new
TreeSet
<>();
}
void
addConfigurationKeys
(
ConfigurationMetadataProperty
...
properties
)
{
Stream
.
of
(
properties
)
.
forEach
(
(
property
)
->
this
.
configurationKeys
.
add
(
property
.
getId
())
);
Stream
.
of
(
properties
)
.
map
(
ConfigurationMetadataProperty:
:
getId
)
.
forEach
(
this
.
configurationKeys
::
add
);
}
@Override
public
void
write
Asciidoc
(
String
Builder
builder
)
{
public
void
write
(
Asciidoc
Builder
builder
)
{
builder
.
append
(
"|`+++"
);
this
.
configurationKeys
.
forEach
((
key
)
->
builder
.
append
(
key
).
append
(
NEWLINE
));
builder
.
append
(
"+++`"
).
append
(
NEWLINE
).
append
(
"|"
).
append
(
NEWLINE
).
append
(
"|+++"
)
.
append
(
this
.
description
).
append
(
"+++"
).
append
(
NEWLINE
);
this
.
configurationKeys
.
forEach
(
builder:
:
appendln
);
builder
.
appendln
(
"+++`"
);
builder
.
appendln
(
"|"
);
builder
.
appendln
(
"|+++"
,
this
.
description
,
"+++"
);
}
}
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationMetadataDocumentWriter.java
View file @
2dfd916c
...
...
@@ -29,7 +29,6 @@ import java.util.Map;
import
java.util.stream.Collectors
;
import
org.springframework.boot.configurationmetadata.ConfigurationMetadataProperty
;
import
org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository
;
import
org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder
;
/**
...
...
@@ -39,8 +38,21 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepos
*/
public
class
ConfigurationMetadataDocumentWriter
{
public
void
writeDocument
(
Path
outputDirPath
,
DocumentOptions
options
,
InputStream
...
metadataInput
)
throws
IOException
{
public
void
writeDocument
(
Path
outputDirectory
,
DocumentOptions
options
,
InputStream
...
metadata
)
throws
IOException
{
assertValidOutputDirectory
(
outputDirectory
);
if
(!
Files
.
exists
(
outputDirectory
))
{
Files
.
createDirectory
(
outputDirectory
);
}
assertMetadata
(
metadata
);
List
<
ConfigurationTable
>
tables
=
createConfigTables
(
getMetadataProperties
(
metadata
),
options
);
for
(
ConfigurationTable
table
:
tables
)
{
writeConfigurationTable
(
table
,
outputDirectory
);
}
}
private
void
assertValidOutputDirectory
(
Path
outputDirPath
)
{
if
(
outputDirPath
==
null
)
{
throw
new
IllegalArgumentException
(
"output path should not be null"
);
}
...
...
@@ -48,64 +60,33 @@ public class ConfigurationMetadataDocumentWriter {
throw
new
IllegalArgumentException
(
"output path already exists and is not a directory"
);
}
else
if
(!
Files
.
exists
(
outputDirPath
))
{
Files
.
createDirectory
(
outputDirPath
);
}
if
(
metadataInput
==
null
||
metadataInput
.
length
<
1
)
{
throw
new
IllegalArgumentException
(
"missing input metadata"
);
}
ConfigurationMetadataRepository
configRepository
=
ConfigurationMetadataRepositoryJsonBuilder
.
create
(
metadataInput
).
build
();
Map
<
String
,
ConfigurationMetadataProperty
>
allProperties
=
configRepository
.
getAllProperties
();
List
<
ConfigurationTable
>
tables
=
createConfigTables
(
allProperties
,
options
);
for
(
ConfigurationTable
table
:
tables
)
{
Path
outputFilePath
=
outputDirPath
.
resolve
(
table
.
getId
()
+
".adoc"
);
Files
.
deleteIfExists
(
outputFilePath
);
Files
.
createFile
(
outputFilePath
);
try
(
OutputStream
outputStream
=
Files
.
newOutputStream
(
outputFilePath
))
{
outputStream
.
write
(
table
.
toAsciidocTable
().
getBytes
(
StandardCharsets
.
UTF_8
));
private
void
assertMetadata
(
InputStream
...
metadata
)
{
if
(
metadata
==
null
||
metadata
.
length
<
1
)
{
throw
new
IllegalArgumentException
(
"missing input metadata"
);
}
}
private
Map
<
String
,
ConfigurationMetadataProperty
>
getMetadataProperties
(
InputStream
...
metadata
)
throws
IOException
{
ConfigurationMetadataRepositoryJsonBuilder
builder
=
ConfigurationMetadataRepositoryJsonBuilder
.
create
(
metadata
);
return
builder
.
build
().
getAllProperties
();
}
private
List
<
ConfigurationTable
>
createConfigTables
(
Map
<
String
,
ConfigurationMetadataProperty
>
all
Properties
,
Map
<
String
,
ConfigurationMetadataProperty
>
metadata
Properties
,
DocumentOptions
options
)
{
final
List
<
ConfigurationTable
>
tables
=
new
ArrayList
<>();
final
List
<
String
>
unmappedKeys
=
allProperties
.
values
().
stream
()
.
filter
((
prop
)
->
!
prop
.
isDeprecated
()).
map
((
prop
)
->
prop
.
getId
())
.
collect
(
Collectors
.
toList
());
final
Map
<
String
,
CompoundKeyEntry
>
overrides
=
getOverrides
(
allProperties
,
unmappedKeys
,
options
);
options
.
getMetadataSections
().
forEach
((
id
,
keyPrefixes
)
->
{
ConfigurationTable
table
=
new
ConfigurationTable
(
id
);
tables
.
add
(
table
);
for
(
String
keyPrefix
:
keyPrefixes
)
{
List
<
String
>
matchingOverrides
=
overrides
.
keySet
().
stream
()
.
filter
((
overrideKey
)
->
overrideKey
.
startsWith
(
keyPrefix
))
.
collect
(
Collectors
.
toList
());
matchingOverrides
.
forEach
((
match
)
->
table
.
addEntry
(
overrides
.
remove
(
match
)));
}
List
<
String
>
matchingKeys
=
unmappedKeys
.
stream
()
.
filter
((
key
)
->
keyPrefixes
.
stream
().
anyMatch
(
key:
:
startsWith
))
.
collect
(
Collectors
.
toList
());
for
(
String
matchingKey
:
matchingKeys
)
{
ConfigurationMetadataProperty
property
=
allProperties
.
get
(
matchingKey
);
table
.
addEntry
(
new
SingleKeyEntry
(
property
));
}
unmappedKeys
.
removeAll
(
matchingKeys
);
});
List
<
ConfigurationTable
>
tables
=
new
ArrayList
<>();
List
<
String
>
unmappedKeys
=
metadataProperties
.
values
().
stream
()
.
filter
((
property
)
->
!
property
.
isDeprecated
())
.
map
(
ConfigurationMetadataProperty:
:
getId
).
collect
(
Collectors
.
toList
());
Map
<
String
,
CompoundConfigurationTableEntry
>
overrides
=
getOverrides
(
metadataProperties
,
unmappedKeys
,
options
);
options
.
getMetadataSections
().
forEach
(
(
id
,
keyPrefixes
)
->
tables
.
add
(
createConfigTable
(
metadataProperties
,
unmappedKeys
,
overrides
,
id
,
keyPrefixes
)));
if
(!
unmappedKeys
.
isEmpty
())
{
throw
new
IllegalStateException
(
"The following keys were not written to the documentation: "
...
...
@@ -116,22 +97,21 @@ public class ConfigurationMetadataDocumentWriter {
"The following keys were not written to the documentation: "
+
String
.
join
(
", "
,
overrides
.
keySet
()));
}
return
tables
;
}
private
Map
<
String
,
Compound
Key
Entry
>
getOverrides
(
Map
<
String
,
ConfigurationMetadataProperty
>
all
Properties
,
private
Map
<
String
,
Compound
ConfigurationTable
Entry
>
getOverrides
(
Map
<
String
,
ConfigurationMetadataProperty
>
metadata
Properties
,
List
<
String
>
unmappedKeys
,
DocumentOptions
options
)
{
final
Map
<
String
,
CompoundKeyEntry
>
overrides
=
new
HashMap
<>();
Map
<
String
,
CompoundConfigurationTableEntry
>
overrides
=
new
HashMap
<>();
options
.
getOverrides
().
forEach
((
keyPrefix
,
description
)
->
{
final
CompoundKeyEntry
entry
=
new
CompoundKeyEntry
(
keyPrefix
,
description
);
CompoundConfigurationTableEntry
entry
=
new
CompoundConfigurationTableEntry
(
keyPrefix
,
description
);
List
<
String
>
matchingKeys
=
unmappedKeys
.
stream
()
.
filter
((
key
)
->
key
.
startsWith
(
keyPrefix
))
.
collect
(
Collectors
.
toList
());
for
(
String
matchingKey
:
matchingKeys
)
{
entry
.
addConfigurationKeys
(
all
Properties
.
get
(
matchingKey
));
entry
.
addConfigurationKeys
(
metadata
Properties
.
get
(
matchingKey
));
}
overrides
.
put
(
keyPrefix
,
entry
);
unmappedKeys
.
removeAll
(
matchingKeys
);
...
...
@@ -139,4 +119,37 @@ public class ConfigurationMetadataDocumentWriter {
return
overrides
;
}
private
ConfigurationTable
createConfigTable
(
Map
<
String
,
ConfigurationMetadataProperty
>
metadataProperties
,
List
<
String
>
unmappedKeys
,
Map
<
String
,
CompoundConfigurationTableEntry
>
overrides
,
String
id
,
List
<
String
>
keyPrefixes
)
{
ConfigurationTable
table
=
new
ConfigurationTable
(
id
);
for
(
String
keyPrefix
:
keyPrefixes
)
{
List
<
String
>
matchingOverrides
=
overrides
.
keySet
().
stream
()
.
filter
((
overrideKey
)
->
overrideKey
.
startsWith
(
keyPrefix
))
.
collect
(
Collectors
.
toList
());
matchingOverrides
.
forEach
((
match
)
->
table
.
addEntry
(
overrides
.
remove
(
match
)));
}
List
<
String
>
matchingKeys
=
unmappedKeys
.
stream
()
.
filter
((
key
)
->
keyPrefixes
.
stream
().
anyMatch
(
key:
:
startsWith
))
.
collect
(
Collectors
.
toList
());
for
(
String
matchingKey
:
matchingKeys
)
{
ConfigurationMetadataProperty
property
=
metadataProperties
.
get
(
matchingKey
);
table
.
addEntry
(
new
SingleConfigurationTableEntry
(
property
));
}
unmappedKeys
.
removeAll
(
matchingKeys
);
return
table
;
}
private
void
writeConfigurationTable
(
ConfigurationTable
table
,
Path
outputDirectory
)
throws
IOException
{
Path
outputFilePath
=
outputDirectory
.
resolve
(
table
.
getId
()
+
".adoc"
);
Files
.
deleteIfExists
(
outputFilePath
);
Files
.
createFile
(
outputFilePath
);
try
(
OutputStream
outputStream
=
Files
.
newOutputStream
(
outputFilePath
))
{
outputStream
.
write
(
table
.
toAsciidocTable
().
getBytes
(
StandardCharsets
.
UTF_8
));
}
}
}
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/ConfigurationTable.java
View file @
2dfd916c
...
...
@@ -27,11 +27,9 @@ import java.util.TreeSet;
*/
class
ConfigurationTable
{
private
static
final
String
NEWLINE
=
System
.
lineSeparator
();
private
final
String
id
;
private
final
Set
<
AbstractConfiguration
Entry
>
entries
;
private
final
Set
<
ConfigurationTable
Entry
>
entries
;
ConfigurationTable
(
String
id
)
{
this
.
id
=
id
;
...
...
@@ -42,20 +40,21 @@ class ConfigurationTable {
return
this
.
id
;
}
void
addEntry
(
AbstractConfiguration
Entry
...
entries
)
{
void
addEntry
(
ConfigurationTable
Entry
...
entries
)
{
this
.
entries
.
addAll
(
Arrays
.
asList
(
entries
));
}
String
toAsciidocTable
()
{
final
StringBuilder
builder
=
new
StringBuilder
();
builder
.
append
(
"[cols=\"1,1,2\", options=\"header\"]"
).
append
(
NEWLINE
);
builder
.
append
(
"|==="
).
append
(
NEWLINE
).
append
(
"|Key|Default Value|Description"
)
.
append
(
NEWLINE
).
append
(
NEWLINE
);
AsciidocBuilder
builder
=
new
AsciidocBuilder
();
builder
.
appendln
(
"[cols=\"1,1,2\", options=\"header\"]"
);
builder
.
appendln
(
"|==="
);
builder
.
appendln
(
"|Key|Default Value|Description"
);
builder
.
appendln
();
this
.
entries
.
forEach
((
entry
)
->
{
entry
.
write
Asciidoc
(
builder
);
builder
.
append
(
NEWLINE
);
entry
.
write
(
builder
);
builder
.
append
ln
(
);
});
return
builder
.
append
(
"|==="
).
append
(
NEWLINE
).
toString
();
return
builder
.
append
ln
(
"|==="
).
toString
();
}
}
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/
AbstractConfiguration
Entry.java
→
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/
ConfigurationTable
Entry.java
View file @
2dfd916c
...
...
@@ -16,17 +16,12 @@
package
org
.
springframework
.
boot
.
configurationdocs
;
import
java.util.Objects
;
/**
* Abstract class for entries in {@link ConfigurationTable}.
*
* @author Brian Clozel
*/
abstract
class
AbstractConfigurationEntry
implements
Comparable
<
AbstractConfigurationEntry
>
{
protected
static
final
String
NEWLINE
=
System
.
lineSeparator
();
abstract
class
ConfigurationTableEntry
implements
Comparable
<
ConfigurationTableEntry
>
{
protected
String
key
;
...
...
@@ -34,27 +29,27 @@ abstract class AbstractConfigurationEntry
return
this
.
key
;
}
public
abstract
void
write
Asciidoc
(
String
Builder
builder
);
public
abstract
void
write
(
Asciidoc
Builder
builder
);
@Override
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
{
public
boolean
equals
(
Object
o
bj
)
{
if
(
this
==
o
bj
)
{
return
true
;
}
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
{
if
(
o
bj
==
null
||
getClass
()
!=
obj
.
getClass
())
{
return
false
;
}
AbstractConfigurationEntry
that
=
(
AbstractConfigurationEntry
)
o
;
return
this
.
key
.
equals
(
that
.
key
);
ConfigurationTableEntry
other
=
(
ConfigurationTableEntry
)
obj
;
return
this
.
key
.
equals
(
other
.
key
);
}
@Override
public
int
hashCode
()
{
return
Objects
.
hash
(
this
.
key
);
return
this
.
key
.
hashCode
(
);
}
@Override
public
int
compareTo
(
AbstractConfiguration
Entry
other
)
{
public
int
compareTo
(
ConfigurationTable
Entry
other
)
{
return
this
.
key
.
compareTo
(
other
.
getKey
());
}
...
...
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/Single
Key
Entry.java
→
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/main/java/org/springframework/boot/configurationdocs/Single
ConfigurationTable
Entry.java
View file @
2dfd916c
...
...
@@ -26,58 +26,60 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataPrope
*
* @author Brian Clozel
*/
class
Single
KeyEntry
extends
AbstractConfiguration
Entry
{
class
Single
ConfigurationTableEntry
extends
ConfigurationTable
Entry
{
private
String
defaultValue
;
private
final
String
description
;
private
String
description
;
SingleKeyEntry
(
ConfigurationMetadataProperty
property
)
{
private
final
String
defaultValue
;
SingleConfigurationTableEntry
(
ConfigurationMetadataProperty
property
)
{
this
.
key
=
property
.
getId
();
if
(
property
.
getType
()
!=
null
&&
property
.
getType
().
startsWith
(
"java.util.Map"
))
{
this
.
key
+=
".*"
;
}
this
.
description
=
property
.
getDescription
();
if
(
property
.
getDefaultValue
()
!=
null
)
{
if
(
property
.
getDefaultValue
().
getClass
().
isArray
())
{
this
.
defaultValue
=
Arrays
.
stream
((
Object
[])
property
.
getDefaultValue
())
.
map
(
Object:
:
toString
).
collect
(
Collectors
.
joining
(
","
+
NEWLINE
));
this
.
defaultValue
=
getDefaultValue
(
property
.
getDefaultValue
());
}
else
{
this
.
defaultValue
=
property
.
getDefaultValue
().
toString
();
private
String
getDefaultValue
(
Object
defaultValue
)
{
if
(
defaultValue
==
null
)
{
return
null
;
}
if
(
defaultValue
.
getClass
().
isArray
())
{
return
Arrays
.
stream
((
Object
[])
defaultValue
).
map
(
Object:
:
toString
)
.
collect
(
Collectors
.
joining
(
","
+
System
.
lineSeparator
()));
}
return
defaultValue
.
toString
();
}
@Override
public
void
writeAsciidoc
(
StringBuilder
builder
)
{
builder
.
append
(
"|`+"
).
append
(
this
.
key
).
append
(
"+`"
).
append
(
NEWLINE
);
String
defaultValue
=
processDefaultValue
();
if
(!
defaultValue
.
isEmpty
())
{
builder
.
append
(
"|`+"
).
append
(
defaultValue
).
append
(
"+`"
).
append
(
NEWLINE
);
}
else
{
builder
.
append
(
"|"
).
append
(
NEWLINE
);
public
void
write
(
AsciidocBuilder
builder
)
{
builder
.
appendln
(
"|`+"
,
this
.
key
,
"+`"
);
writeDefaultValue
(
builder
);
writeDescription
(
builder
);
builder
.
appendln
();
}
if
(
this
.
description
!=
null
)
{
builder
.
append
(
"|+++"
).
append
(
this
.
description
).
append
(
"+++"
);
private
void
writeDefaultValue
(
AsciidocBuilder
builder
)
{
String
defaultValue
=
(
this
.
defaultValue
!=
null
)
?
this
.
defaultValue
:
""
;
defaultValue
=
defaultValue
.
replace
(
"\\"
,
"\\\\"
).
replace
(
"|"
,
"{vbar}"
+
System
.
lineSeparator
());
if
(
defaultValue
.
isEmpty
())
{
builder
.
appendln
(
"|"
);
}
else
{
builder
.
append
(
"|
"
);
builder
.
append
ln
(
"|`+"
,
defaultValue
,
"+`
"
);
}
builder
.
append
(
NEWLINE
);
}
private
String
processDefaultValue
()
{
if
(
this
.
defaultValue
!=
null
&&
!
this
.
defaultValue
.
isEmpty
())
{
return
this
.
defaultValue
.
replace
(
"\\"
,
"\\\\"
).
replace
(
"|"
,
"{vbar}"
+
NEWLINE
);
private
void
writeDescription
(
AsciidocBuilder
builder
)
{
if
(
this
.
description
==
null
||
this
.
description
.
isEmpty
())
{
builder
.
append
(
"|"
);
}
else
{
builder
.
append
(
"|+++"
,
this
.
description
,
"+++"
);
}
return
""
;
}
}
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/Compound
Key
EntryTests.java
→
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/Compound
ConfigurationTable
EntryTests.java
View file @
2dfd916c
...
...
@@ -23,9 +23,11 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataPrope
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
/**
* Tests for {@link CompoundConfigurationTableEntry}.
*
* @author Brian Clozel
*/
public
class
Compound
Key
EntryTests
{
public
class
Compound
ConfigurationTable
EntryTests
{
private
static
String
NEWLINE
=
System
.
lineSeparator
();
...
...
@@ -34,21 +36,17 @@ public class CompoundKeyEntryTests {
ConfigurationMetadataProperty
firstProp
=
new
ConfigurationMetadataProperty
();
firstProp
.
setId
(
"spring.test.first"
);
firstProp
.
setType
(
"java.lang.String"
);
ConfigurationMetadataProperty
secondProp
=
new
ConfigurationMetadataProperty
();
secondProp
.
setId
(
"spring.test.second"
);
secondProp
.
setType
(
"java.lang.String"
);
ConfigurationMetadataProperty
thirdProp
=
new
ConfigurationMetadataProperty
();
thirdProp
.
setId
(
"spring.test.third"
);
thirdProp
.
setType
(
"java.lang.String"
);
CompoundKeyEntry
entry
=
new
CompoundKeyEntry
(
"spring.test"
,
"This is a description."
);
CompoundConfigurationTableEntry
entry
=
new
CompoundConfigurationTableEntry
(
"spring.test"
,
"This is a description."
);
entry
.
addConfigurationKeys
(
firstProp
,
secondProp
,
thirdProp
);
StringBuilder
builder
=
new
StringBuilder
();
entry
.
writeAsciidoc
(
builder
);
AsciidocBuilder
builder
=
new
AsciidocBuilder
();
entry
.
write
(
builder
);
assertThat
(
builder
.
toString
()).
isEqualTo
(
"|`+++spring.test.first"
+
NEWLINE
+
"spring.test.second"
+
NEWLINE
+
"spring.test.third"
+
NEWLINE
+
"+++`"
+
NEWLINE
+
"|"
+
NEWLINE
+
"|+++This is a description.+++"
+
NEWLINE
);
...
...
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/ConfigurationTableTests.java
View file @
2dfd916c
...
...
@@ -23,6 +23,8 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataPrope
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
/**
* Tests for {@link ConfigurationTable}.
*
* @author Brian Clozel
*/
public
class
ConfigurationTableTests
{
...
...
@@ -32,22 +34,18 @@ public class ConfigurationTableTests {
@Test
public
void
simpleTable
()
{
ConfigurationTable
table
=
new
ConfigurationTable
(
"test"
);
ConfigurationMetadataProperty
first
=
new
ConfigurationMetadataProperty
();
first
.
setId
(
"spring.test.prop"
);
first
.
setDefaultValue
(
"something"
);
first
.
setDescription
(
"This is a description."
);
first
.
setType
(
"java.lang.String"
);
ConfigurationMetadataProperty
second
=
new
ConfigurationMetadataProperty
();
second
.
setId
(
"spring.test.other"
);
second
.
setDefaultValue
(
"other value"
);
second
.
setDescription
(
"This is another description."
);
second
.
setType
(
"java.lang.String"
);
table
.
addEntry
(
new
SingleKeyEntry
(
first
));
table
.
addEntry
(
new
SingleKeyEntry
(
second
));
table
.
addEntry
(
new
SingleConfigurationTableEntry
(
first
));
table
.
addEntry
(
new
SingleConfigurationTableEntry
(
second
));
assertThat
(
table
.
toAsciidocTable
())
.
isEqualTo
(
"[cols=\"1,1,2\", options=\"header\"]"
+
NEWLINE
+
"|==="
+
NEWLINE
+
"|Key|Default Value|Description"
+
NEWLINE
+
NEWLINE
...
...
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/Single
Key
EntryTests.java
→
spring-boot-project/spring-boot-tools/spring-boot-configuration-docs/src/test/java/org/springframework/boot/configurationdocs/Single
ConfigurationTable
EntryTests.java
View file @
2dfd916c
...
...
@@ -23,9 +23,11 @@ import org.springframework.boot.configurationmetadata.ConfigurationMetadataPrope
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
/**
* Tests for {@link SingleConfigurationTableEntry}.
*
* @author Brian Clozel
*/
public
class
Single
Key
EntryTests
{
public
class
Single
ConfigurationTable
EntryTests
{
private
static
String
NEWLINE
=
System
.
lineSeparator
();
...
...
@@ -36,11 +38,9 @@ public class SingleKeyEntryTests {
property
.
setDefaultValue
(
"something"
);
property
.
setDescription
(
"This is a description."
);
property
.
setType
(
"java.lang.String"
);
SingleKeyEntry
entry
=
new
SingleKeyEntry
(
property
);
StringBuilder
builder
=
new
StringBuilder
();
entry
.
writeAsciidoc
(
builder
);
SingleConfigurationTableEntry
entry
=
new
SingleConfigurationTableEntry
(
property
);
AsciidocBuilder
builder
=
new
AsciidocBuilder
();
entry
.
write
(
builder
);
assertThat
(
builder
.
toString
()).
isEqualTo
(
"|`+spring.test.prop+`"
+
NEWLINE
+
"|`+something+`"
+
NEWLINE
+
"|+++This is a description.+++"
+
NEWLINE
);
}
...
...
@@ -51,11 +51,9 @@ public class SingleKeyEntryTests {
property
.
setId
(
"spring.test.prop"
);
property
.
setDescription
(
"This is a description."
);
property
.
setType
(
"java.lang.String"
);
SingleKeyEntry
entry
=
new
SingleKeyEntry
(
property
);
StringBuilder
builder
=
new
StringBuilder
();
entry
.
writeAsciidoc
(
builder
);
SingleConfigurationTableEntry
entry
=
new
SingleConfigurationTableEntry
(
property
);
AsciidocBuilder
builder
=
new
AsciidocBuilder
();
entry
.
write
(
builder
);
assertThat
(
builder
.
toString
()).
isEqualTo
(
"|`+spring.test.prop+`"
+
NEWLINE
+
"|"
+
NEWLINE
+
"|+++This is a description.+++"
+
NEWLINE
);
}
...
...
@@ -67,11 +65,9 @@ public class SingleKeyEntryTests {
property
.
setDefaultValue
(
"first|second"
);
property
.
setDescription
(
"This is a description."
);
property
.
setType
(
"java.lang.String"
);
SingleKeyEntry
entry
=
new
SingleKeyEntry
(
property
);
StringBuilder
builder
=
new
StringBuilder
();
entry
.
writeAsciidoc
(
builder
);
SingleConfigurationTableEntry
entry
=
new
SingleConfigurationTableEntry
(
property
);
AsciidocBuilder
builder
=
new
AsciidocBuilder
();
entry
.
write
(
builder
);
assertThat
(
builder
.
toString
()).
isEqualTo
(
"|`+spring.test.prop+`"
+
NEWLINE
+
"|`+first{vbar}"
+
NEWLINE
+
"second+`"
+
NEWLINE
+
"|+++This is a description.+++"
+
NEWLINE
);
...
...
@@ -84,11 +80,9 @@ public class SingleKeyEntryTests {
property
.
setDefaultValue
(
"first\\second"
);
property
.
setDescription
(
"This is a description."
);
property
.
setType
(
"java.lang.String"
);
SingleKeyEntry
entry
=
new
SingleKeyEntry
(
property
);
StringBuilder
builder
=
new
StringBuilder
();
entry
.
writeAsciidoc
(
builder
);
SingleConfigurationTableEntry
entry
=
new
SingleConfigurationTableEntry
(
property
);
AsciidocBuilder
builder
=
new
AsciidocBuilder
();
entry
.
write
(
builder
);
assertThat
(
builder
.
toString
())
.
isEqualTo
(
"|`+spring.test.prop+`"
+
NEWLINE
+
"|`+first\\\\second+`"
+
NEWLINE
+
"|+++This is a description.+++"
+
NEWLINE
);
...
...
@@ -100,11 +94,9 @@ public class SingleKeyEntryTests {
property
.
setId
(
"spring.test.prop"
);
property
.
setDescription
(
"This is a description."
);
property
.
setType
(
"java.util.Map<java.lang.String,java.lang.String>"
);
SingleKeyEntry
entry
=
new
SingleKeyEntry
(
property
);
StringBuilder
builder
=
new
StringBuilder
();
entry
.
writeAsciidoc
(
builder
);
SingleConfigurationTableEntry
entry
=
new
SingleConfigurationTableEntry
(
property
);
AsciidocBuilder
builder
=
new
AsciidocBuilder
();
entry
.
write
(
builder
);
assertThat
(
builder
.
toString
()).
isEqualTo
(
"|`+spring.test.prop.*+`"
+
NEWLINE
+
"|"
+
NEWLINE
+
"|+++This is a description.+++"
+
NEWLINE
);
}
...
...
@@ -117,11 +109,9 @@ public class SingleKeyEntryTests {
property
.
setDescription
(
"This is a description."
);
property
.
setType
(
"java.util.List<java.lang.String>"
);
property
.
setDefaultValue
(
defaultValue
);
SingleKeyEntry
entry
=
new
SingleKeyEntry
(
property
);
StringBuilder
builder
=
new
StringBuilder
();
entry
.
writeAsciidoc
(
builder
);
SingleConfigurationTableEntry
entry
=
new
SingleConfigurationTableEntry
(
property
);
AsciidocBuilder
builder
=
new
AsciidocBuilder
();
entry
.
write
(
builder
);
assertThat
(
builder
.
toString
()).
isEqualTo
(
"|`+spring.test.prop+`"
+
NEWLINE
+
"|`+first,"
+
NEWLINE
+
"second,"
+
NEWLINE
+
"third+`"
+
NEWLINE
+
"|+++This is a description.+++"
+
NEWLINE
);
...
...
spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/ConstructorParameterPropertyDescriptor.java
View file @
2dfd916c
...
...
@@ -17,6 +17,7 @@
package
org
.
springframework
.
boot
.
configurationprocessor
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.stream.Collectors
;
import
javax.lang.model.element.AnnotationMirror
;
...
...
@@ -61,11 +62,8 @@ class ConstructorParameterPropertyDescriptor extends PropertyDescriptor<Variable
private
Object
getDefaultValueFromAnnotation
(
MetadataGenerationEnvironment
environment
,
Element
element
)
{
AnnotationMirror
defaultValueAnnotation
=
environment
.
getDefaultValueAnnotation
(
element
);
if
(
defaultValueAnnotation
!=
null
)
{
List
<
String
>
defaultValue
=
(
List
<
String
>)
environment
.
getAnnotationElementValues
(
defaultValueAnnotation
).
get
(
"value"
);
AnnotationMirror
annotation
=
environment
.
getDefaultValueAnnotation
(
element
);
List
<
String
>
defaultValue
=
getDefaultValue
(
environment
,
annotation
);
if
(
defaultValue
!=
null
)
{
try
{
TypeMirror
specificType
=
determineSpecificType
(
environment
);
...
...
@@ -78,12 +76,21 @@ class ConstructorParameterPropertyDescriptor extends PropertyDescriptor<Variable
}
catch
(
IllegalArgumentException
ex
)
{
environment
.
getMessager
().
printMessage
(
Kind
.
ERROR
,
ex
.
getMessage
(),
element
,
defaultValueA
nnotation
);
element
,
a
nnotation
);
}
}
return
null
;
}
@SuppressWarnings
(
"unchecked"
)
private
List
<
String
>
getDefaultValue
(
MetadataGenerationEnvironment
environment
,
AnnotationMirror
annotation
)
{
if
(
annotation
==
null
)
{
return
null
;
}
Map
<
String
,
Object
>
values
=
environment
.
getAnnotationElementValues
(
annotation
);
return
(
List
<
String
>)
values
.
get
(
"value"
);
}
private
TypeMirror
determineSpecificType
(
MetadataGenerationEnvironment
environment
)
{
TypeMirror
candidate
=
getSource
().
asType
();
...
...
spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/MetadataGenerationEnvironment.java
View file @
2dfd916c
...
...
@@ -17,6 +17,7 @@
package
org
.
springframework
.
boot
.
configurationprocessor
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.LinkedHashMap
;
...
...
@@ -49,7 +50,24 @@ class MetadataGenerationEnvironment {
private
static
final
String
NULLABLE_ANNOTATION
=
"org.springframework.lang.Nullable"
;
private
final
Set
<
String
>
typeExcludes
;
private
static
final
Set
<
String
>
TYPE_EXCLUDES
;
static
{
Set
<
String
>
excludes
=
new
HashSet
<>();
excludes
.
add
(
"com.zaxxer.hikari.IConnectionCustomizer"
);
excludes
.
add
(
"groovy.text.markup.MarkupTemplateEngine"
);
excludes
.
add
(
"java.io.Writer"
);
excludes
.
add
(
"java.io.PrintWriter"
);
excludes
.
add
(
"java.lang.ClassLoader"
);
excludes
.
add
(
"java.util.concurrent.ThreadFactory"
);
excludes
.
add
(
"javax.jms.XAConnectionFactory"
);
excludes
.
add
(
"javax.sql.DataSource"
);
excludes
.
add
(
"javax.sql.XADataSource"
);
excludes
.
add
(
"org.apache.tomcat.jdbc.pool.PoolConfiguration"
);
excludes
.
add
(
"org.apache.tomcat.jdbc.pool.Validator"
);
excludes
.
add
(
"org.flywaydb.core.api.callback.FlywayCallback"
);
excludes
.
add
(
"org.flywaydb.core.api.resolver.MigrationResolver"
);
TYPE_EXCLUDES
=
Collections
.
unmodifiableSet
(
excludes
);
}
private
final
TypeUtils
typeUtils
;
...
...
@@ -79,7 +97,6 @@ class MetadataGenerationEnvironment {
String
deprecatedConfigurationPropertyAnnotation
,
String
defaultValueAnnotation
,
String
endpointAnnotation
,
String
readOperationAnnotation
)
{
this
.
typeExcludes
=
determineTypeExcludes
();
this
.
typeUtils
=
new
TypeUtils
(
environment
);
this
.
elements
=
environment
.
getElementUtils
();
this
.
messager
=
environment
.
getMessager
();
...
...
@@ -92,24 +109,6 @@ class MetadataGenerationEnvironment {
this
.
readOperationAnnotation
=
readOperationAnnotation
;
}
private
static
Set
<
String
>
determineTypeExcludes
()
{
Set
<
String
>
excludes
=
new
HashSet
<>();
excludes
.
add
(
"com.zaxxer.hikari.IConnectionCustomizer"
);
excludes
.
add
(
"groovy.text.markup.MarkupTemplateEngine"
);
excludes
.
add
(
"java.io.Writer"
);
excludes
.
add
(
"java.io.PrintWriter"
);
excludes
.
add
(
"java.lang.ClassLoader"
);
excludes
.
add
(
"java.util.concurrent.ThreadFactory"
);
excludes
.
add
(
"javax.jms.XAConnectionFactory"
);
excludes
.
add
(
"javax.sql.DataSource"
);
excludes
.
add
(
"javax.sql.XADataSource"
);
excludes
.
add
(
"org.apache.tomcat.jdbc.pool.PoolConfiguration"
);
excludes
.
add
(
"org.apache.tomcat.jdbc.pool.Validator"
);
excludes
.
add
(
"org.flywaydb.core.api.callback.FlywayCallback"
);
excludes
.
add
(
"org.flywaydb.core.api.resolver.MigrationResolver"
);
return
excludes
;
}
private
static
FieldValuesParser
resolveFieldValuesParser
(
ProcessingEnvironment
env
)
{
try
{
return
new
JavaCompilerFieldValuesParser
(
env
);
...
...
@@ -147,7 +146,7 @@ class MetadataGenerationEnvironment {
if
(
typeName
.
endsWith
(
"[]"
))
{
typeName
=
typeName
.
substring
(
0
,
typeName
.
length
()
-
2
);
}
return
this
.
typeExcludes
.
contains
(
typeName
);
return
TYPE_EXCLUDES
.
contains
(
typeName
);
}
public
boolean
isDeprecated
(
Element
element
)
{
...
...
spring-boot-project/spring-boot-tools/spring-boot-configuration-processor/src/main/java/org/springframework/boot/configurationprocessor/TypeUtils.java
View file @
2dfd916c
...
...
@@ -164,10 +164,9 @@ class TypeUtils {
return
this
.
types
.
getDeclaredType
(
this
.
env
.
getElementUtils
()
.
getTypeElement
(
Object
.
class
.
getName
()));
}
else
{
// return type argument to Collection<...>
// return type argument to Collection<...>
return
declaredType
.
getTypeArguments
().
get
(
0
);
}
}
// recursively walk the supertypes, looking for Collection<...>
for
(
TypeMirror
superType
:
this
.
env
.
getTypeUtils
().
directSupertypes
(
type
))
{
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/cloud/CloudPlatform.java
View file @
2dfd916c
...
...
@@ -17,10 +17,10 @@
package
org
.
springframework
.
boot
.
cloud
;
import
org.springframework.core.env.ConfigurableEnvironment
;
import
org.springframework.core.env.EnumerablePropertySource
;
import
org.springframework.core.env.Environment
;
import
org.springframework.core.env.
Map
PropertySource
;
import
org.springframework.core.env.PropertySource
;
import
org.springframework.core.env.StandardEnvironment
;
import
org.springframework.util.StringUtils
;
/**
* Simple detection for well known cloud platforms. For more advanced cloud provider
...
...
@@ -73,27 +73,42 @@ public enum CloudPlatform {
* Kubernetes platform.
*/
KUBERNETES
{
private
static
final
String
SERVICE_HOST_SUFFIX
=
"_SERVICE_HOST"
;
private
static
final
String
SERVICE_PORT_SUFFIX
=
"_SERVICE_PORT"
;
@Override
public
boolean
isActive
(
Environment
environment
)
{
if
(
environment
instanceof
ConfigurableEnvironment
)
{
MapPropertySource
propertySource
=
(
MapPropertySource
)
((
ConfigurableEnvironment
)
environment
)
.
getPropertySources
()
return
isActive
((
ConfigurableEnvironment
)
environment
);
}
return
false
;
}
private
boolean
isActive
(
ConfigurableEnvironment
environment
)
{
PropertySource
<?>
environmentPropertySource
=
environment
.
getPropertySources
()
.
get
(
StandardEnvironment
.
SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
);
if
(
propertySource
!=
null
)
{
for
(
String
name
:
propertySource
.
getPropertyNames
())
{
if
(
name
.
endsWith
(
"_SERVICE_HOST"
))
{
String
serviceName
=
StringUtils
.
split
(
name
,
"_SERVICE_HOST"
)[
0
];
if
(
propertySource
.
getProperty
(
serviceName
+
"_SERVICE_PORT"
)
!=
null
)
{
return
true
;
if
(
environmentPropertySource
instanceof
EnumerablePropertySource
)
{
return
isActive
((
EnumerablePropertySource
<?>)
environmentPropertySource
);
}
return
false
;
}
private
boolean
isActive
(
EnumerablePropertySource
<?>
environmentPropertySource
)
{
for
(
String
propertyName
:
environmentPropertySource
.
getPropertyNames
())
{
if
(
propertyName
.
endsWith
(
SERVICE_HOST_SUFFIX
))
{
String
serviceName
=
propertyName
.
substring
(
0
,
propertyName
.
length
()
-
SERVICE_HOST_SUFFIX
.
length
());
if
(
environmentPropertySource
.
getProperty
(
serviceName
+
SERVICE_PORT_SUFFIX
)
!=
null
)
{
return
true
;
}
}
}
return
false
;
}
};
/**
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java
View file @
2dfd916c
...
...
@@ -116,6 +116,9 @@ public class ConfigFileApplicationListener
private
static
final
Bindable
<
String
[]>
STRING_ARRAY
=
Bindable
.
of
(
String
[].
class
);
private
static
final
Bindable
<
List
<
String
>>
STRING_LIST
=
Bindable
.
listOf
(
String
.
class
);
/**
* The "active profiles" property name.
*/
...
...
@@ -701,18 +704,6 @@ public class ConfigFileApplicationListener
return
new
LinkedHashSet
<>(
list
);
}
/**
* This ensures that the order of active profiles in the {@link Environment}
* matches the order in which the profiles were processed.
* @param processedProfiles the processed profiles
*/
private
void
resetEnvironmentProfiles
(
List
<
Profile
>
processedProfiles
)
{
String
[]
names
=
processedProfiles
.
stream
()
.
filter
((
profile
)
->
profile
!=
null
&&
!
profile
.
isDefaultProfile
())
.
map
(
Profile:
:
getName
).
toArray
(
String
[]::
new
);
this
.
environment
.
setActiveProfiles
(
names
);
}
private
void
addLoadedPropertySources
()
{
MutablePropertySources
destination
=
this
.
environment
.
getPropertySources
();
List
<
MutablePropertySources
>
loaded
=
new
ArrayList
<>(
this
.
loaded
.
values
());
...
...
@@ -773,8 +764,7 @@ public class ConfigFileApplicationListener
}
private
List
<
String
>
getDefaultProfiles
(
Binder
binder
,
String
property
)
{
return
Arrays
.
asList
(
binder
.
bind
(
property
,
STRING_ARRAY
).
orElse
(
new
String
[]
{}));
return
binder
.
bind
(
property
,
STRING_LIST
).
orElse
(
Collections
.
emptyList
());
}
}
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanDefinitionRegistrar.java
View file @
2dfd916c
...
...
@@ -91,12 +91,10 @@ final class ConfigurationPropertiesBeanDefinitionRegistrar {
if
(
canBindAtCreationTime
(
type
))
{
return
ConfigurationPropertiesBeanDefinition
.
from
(
beanFactory
,
name
,
type
);
}
else
{
GenericBeanDefinition
definition
=
new
GenericBeanDefinition
();
definition
.
setBeanClass
(
type
);
return
definition
;
}
}
private
static
boolean
canBindAtCreationTime
(
Class
<?>
type
)
{
List
<
Constructor
<?>>
constructors
=
determineConstructors
(
type
);
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesScanRegistrar.java
View file @
2dfd916c
...
...
@@ -72,8 +72,16 @@ class ConfigurationPropertiesScanRegistrar implements ImportBeanDefinitionRegist
scanner
.
addIncludeFilter
(
new
AnnotationTypeFilter
(
ConfigurationProperties
.
class
));
for
(
String
basePackage
:
packages
)
{
if
(
StringUtils
.
hasText
(
basePackage
))
{
for
(
BeanDefinition
candidate
:
scanner
.
findCandidateComponents
(
basePackage
))
{
scan
(
beanFactory
,
registry
,
scanner
,
basePackage
);
}
}
}
private
void
scan
(
ConfigurableListableBeanFactory
beanFactory
,
BeanDefinitionRegistry
registry
,
ClassPathScanningCandidateComponentProvider
scanner
,
String
basePackage
)
throws
LinkageError
{
for
(
BeanDefinition
candidate
:
scanner
.
findCandidateComponents
(
basePackage
))
{
String
beanClassName
=
candidate
.
getBeanClassName
();
try
{
Class
<?>
type
=
ClassUtils
.
forName
(
beanClassName
,
null
);
...
...
@@ -85,7 +93,5 @@ class ConfigurationPropertiesScanRegistrar implements ImportBeanDefinitionRegist
}
}
}
}
}
}
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/EnableConfigurationPropertiesImportSelector.java
View file @
2dfd916c
...
...
@@ -77,9 +77,8 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
}
private
List
<
Class
<?>>
collectClasses
(
List
<?>
values
)
{
return
values
.
stream
().
flatMap
((
value
)
->
Arrays
.
stream
((
Object
[])
value
))
.
map
((
o
)
->
(
Class
<?>)
o
).
filter
((
type
)
->
void
.
class
!=
type
)
.
collect
(
Collectors
.
toList
());
return
values
.
stream
().
flatMap
((
value
)
->
Arrays
.
stream
((
Class
<?>[])
value
))
.
filter
((
type
)
->
void
.
class
!=
type
).
collect
(
Collectors
.
toList
());
}
}
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ConstructorParametersBinder.java
View file @
2dfd916c
...
...
@@ -21,6 +21,7 @@ import java.lang.reflect.Modifier;
import
java.lang.reflect.Parameter
;
import
java.lang.reflect.Type
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
...
...
@@ -58,30 +59,25 @@ class ConstructorParametersBinder implements BeanBinder {
private
List
<
Object
>
bind
(
BeanPropertyBinder
propertyBinder
,
Bean
bean
,
BindConverter
converter
)
{
List
<
Object
>
boundParams
=
new
ArrayList
<>();
for
(
ConstructorParameter
parameter
:
bean
.
getParameters
().
values
())
{
Object
bound
=
bind
(
parameter
,
propertyBinder
);
if
(
bound
==
null
)
{
bound
=
getDefaultValue
(
parameter
,
converter
);
Collection
<
ConstructorParameter
>
parameters
=
bean
.
getParameters
().
values
();
List
<
Object
>
boundParameters
=
new
ArrayList
<>(
parameters
.
size
());
for
(
ConstructorParameter
parameter
:
parameters
)
{
Object
boundParameter
=
bind
(
parameter
,
propertyBinder
);
if
(
boundParameter
==
null
)
{
boundParameter
=
getDefaultValue
(
parameter
,
converter
);
}
boundParam
s
.
add
(
bound
);
boundParam
eters
.
add
(
boundParameter
);
}
return
boundParams
;
return
boundParam
eter
s
;
}
private
Object
getDefaultValue
(
ConstructorParameter
parameter
,
BindConverter
converter
)
{
if
(
parameter
.
getDefaultValue
()
!=
null
)
{
return
converter
.
convert
(
parameter
.
getDefaultValue
(),
parameter
.
getType
(),
parameter
.
getAnnotations
());
}
else
{
Class
<?>
resolve
=
parameter
.
getType
().
resolve
();
if
(
resolve
!=
null
&&
resolve
.
isPrimitive
())
{
if
(
parameter
.
getDefaultValue
()
==
null
)
{
return
null
;
}
}
return
null
;
return
converter
.
convert
(
parameter
.
getDefaultValue
(),
parameter
.
getType
(),
parameter
.
getAnnotations
())
;
}
private
Object
bind
(
ConstructorParameter
parameter
,
...
...
@@ -112,20 +108,9 @@ class ConstructorParametersBinder implements BeanBinder {
return
null
;
}
if
(
KOTLIN_PRESENT
&&
KotlinDetector
.
isKotlinType
(
type
))
{
Constructor
<?>
primaryConstructor
=
BeanUtils
.
findPrimaryConstructor
(
type
);
if
(
primaryConstructor
!=
null
&&
primaryConstructor
.
getParameterCount
()
>
0
)
{
return
KotlinBeanProvider
.
get
(
primaryConstructor
);
}
}
else
{
Constructor
<?>[]
constructors
=
type
.
getDeclaredConstructors
();
if
(
constructors
.
length
==
1
&&
constructors
[
0
].
getParameterCount
()
>
0
)
{
return
SimpleBeanProvider
.
get
(
constructors
[
0
]);
return
KotlinBeanProvider
.
get
(
type
);
}
}
return
null
;
return
SimpleBeanProvider
.
get
(
type
);
}
public
Map
<
String
,
ConstructorParameter
>
getParameters
()
{
...
...
@@ -138,50 +123,29 @@ class ConstructorParametersBinder implements BeanBinder {
}
/**
* A simple bean provider that uses `-parameters` to extract the parameter names.
*/
private
static
class
SimpleBeanProvider
{
public
static
Bean
get
(
Constructor
<?>
constructor
)
{
return
new
Bean
(
constructor
,
parseParameters
(
constructor
));
}
private
static
Map
<
String
,
ConstructorParameter
>
parseParameters
(
Constructor
<?>
constructor
)
{
Map
<
String
,
ConstructorParameter
>
parameters
=
new
LinkedHashMap
<>();
for
(
Parameter
parameter
:
constructor
.
getParameters
())
{
String
name
=
parameter
.
getName
();
DefaultValue
[]
annotationsByType
=
parameter
.
getAnnotationsByType
(
DefaultValue
.
class
);
String
[]
defaultValue
=
(
annotationsByType
.
length
>
0
)
?
annotationsByType
[
0
].
value
()
:
null
;
parameters
.
computeIfAbsent
(
name
,
(
s
)
->
new
ConstructorParameter
(
name
,
ResolvableType
.
forClass
(
parameter
.
getType
()),
parameter
.
getDeclaredAnnotations
(),
defaultValue
));
}
return
parameters
;
}
}
/**
* A bean provider for a Kotlin class. Uses the Kotlin constructor to extract the
* parameter names.
*/
private
static
class
KotlinBeanProvider
{
public
static
Bean
get
(
Constructor
<?>
constructor
)
{
public
static
Bean
get
(
Class
<?>
type
)
{
Constructor
<?>
primaryConstructor
=
BeanUtils
.
findPrimaryConstructor
(
type
);
if
(
primaryConstructor
!=
null
&&
primaryConstructor
.
getParameterCount
()
>
0
)
{
return
get
(
primaryConstructor
);
}
return
null
;
}
private
static
Bean
get
(
Constructor
<?>
constructor
)
{
KFunction
<?>
kotlinConstructor
=
ReflectJvmMapping
.
getKotlinFunction
(
constructor
);
if
(
kotlinConstructor
!=
null
)
{
return
new
Bean
(
constructor
,
parseParameters
(
kotlinConstructor
));
}
else
{
return
SimpleBeanProvider
.
get
(
constructor
);
}
}
private
static
Map
<
String
,
ConstructorParameter
>
parseParameters
(
KFunction
<?>
constructor
)
{
...
...
@@ -199,6 +163,42 @@ class ConstructorParametersBinder implements BeanBinder {
}
/**
* A simple bean provider that uses `-parameters` to extract the parameter names.
*/
private
static
class
SimpleBeanProvider
{
public
static
Bean
get
(
Class
<?>
type
)
{
Constructor
<?>[]
constructors
=
type
.
getDeclaredConstructors
();
if
(
constructors
.
length
==
1
&&
constructors
[
0
].
getParameterCount
()
>
0
)
{
return
SimpleBeanProvider
.
get
(
constructors
[
0
]);
}
return
null
;
}
public
static
Bean
get
(
Constructor
<?>
constructor
)
{
return
new
Bean
(
constructor
,
parseParameters
(
constructor
));
}
private
static
Map
<
String
,
ConstructorParameter
>
parseParameters
(
Constructor
<?>
constructor
)
{
Map
<
String
,
ConstructorParameter
>
parameters
=
new
LinkedHashMap
<>();
for
(
Parameter
parameter
:
constructor
.
getParameters
())
{
String
name
=
parameter
.
getName
();
DefaultValue
[]
annotationsByType
=
parameter
.
getAnnotationsByType
(
DefaultValue
.
class
);
String
[]
defaultValue
=
(
annotationsByType
.
length
>
0
)
?
annotationsByType
[
0
].
value
()
:
null
;
parameters
.
computeIfAbsent
(
name
,
(
key
)
->
new
ConstructorParameter
(
name
,
ResolvableType
.
forClass
(
parameter
.
getType
()),
parameter
.
getDeclaredAnnotations
(),
defaultValue
));
}
return
parameters
;
}
}
/**
* A constructor parameter being bound.
*/
...
...
spring-boot-project/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java
View file @
2dfd916c
...
...
@@ -174,7 +174,9 @@ class DefaultLogbackConfiguration {
private
DataSize
getDataSize
(
String
property
,
DataSize
defaultSize
)
{
String
value
=
this
.
patterns
.
getProperty
(
property
);
if
(
value
!=
null
)
{
if
(
value
==
null
)
{
return
defaultSize
;
}
try
{
return
DataSize
.
parse
(
value
);
}
...
...
@@ -183,9 +185,5 @@ class DefaultLogbackConfiguration {
return
DataSize
.
ofBytes
(
fileSize
.
getSize
());
}
}
else
{
return
defaultSize
;
}
}
}
spring-boot-project/spring-boot/src/test/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystemTests.java
View file @
2dfd916c
...
...
@@ -240,8 +240,8 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Test
public
void
springConfigLocations
()
{
String
[]
locations
=
getSpringConfigLocations
(
this
.
loggingSystem
);
assertThat
(
locations
).
isEqualTo
(
new
String
[]
{
"log4j2-spring.properties"
,
"log4j2-spring.xml"
}
);
assertThat
(
locations
).
containsExactly
(
"log4j2-spring.properties"
,
"log4j2-spring.xml"
);
}
@Test
...
...
spring-boot-samples/spring-boot-sample-tomcat/src/main/java/sample/tomcat/web/SampleController.java
View file @
2dfd916c
...
...
@@ -20,8 +20,10 @@ import sample.tomcat.service.HelloWorldService;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Controller
;
import
org.springframework.web.bind.annotation.ExceptionHandler
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.ResponseBody
;
import
org.springframework.web.servlet.ModelAndView
;
@Controller
public
class
SampleController
{
...
...
@@ -32,7 +34,12 @@ public class SampleController {
@GetMapping
(
"/"
)
@ResponseBody
public
String
helloWorld
()
{
return
this
.
helloWorldService
.
getHelloMessage
();
throw
new
RuntimeException
(
"Fail"
);
}
@ExceptionHandler
(
RuntimeException
.
class
)
public
ModelAndView
handle
(
RuntimeException
ex
)
{
return
null
;
}
}
spring-boot-samples/spring-boot-sample-tomcat/src/main/resources/application.properties
View file @
2dfd916c
server.compression.enabled
:
true
server.compression.min-response-size
:
1
server.connection-timeout
=
5000
spring.mvc.log-resolved-exception
=
true
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment