Skip to content

Commit 8c418ad

Browse files
committed
Polish "Add auto-configuration for Neo4j driver"
See gh-22301
1 parent 6134ff1 commit 8c418ad

File tree

10 files changed

+398
-509
lines changed

10 files changed

+398
-509
lines changed

buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
* {@link Task} used to document auto-configuration classes.
3535
*
3636
* @author Andy Wilkinson
37-
* @author Michael J. Simons
3837
*/
3938
public class DocumentConfigurationProperties extends DefaultTask {
4039

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,23 @@
1616

1717
package org.springframework.boot.autoconfigure.neo4j;
1818

19-
import java.util.Collections;
20-
import java.util.Set;
21-
22-
import org.neo4j.driver.net.ServerAddress;
23-
import org.neo4j.driver.net.ServerAddressResolver;
19+
import org.neo4j.driver.Config;
20+
import org.neo4j.driver.Config.ConfigBuilder;
2421

2522
/**
26-
* Resolver used only for configuration tests.
23+
* Callback interface that can be implemented by beans wishing to customize the
24+
* {@link Config} via a {@link ConfigBuilder} whilst retaining default auto-configuration.
2725
*
28-
* @author Michael J. Simons
26+
* @author Stephane Nicoll
27+
* @since 2.4.0
2928
*/
30-
class TestServerAddressResolver implements ServerAddressResolver {
29+
@FunctionalInterface
30+
public interface ConfigBuilderCustomizer {
3131

32-
@Override
33-
public Set<ServerAddress> resolve(ServerAddress address) {
34-
return Collections.emptySet();
35-
}
32+
/**
33+
* Customize the {@link ConfigBuilder}.
34+
* @param configBuilder the {@link ConfigBuilder} to customize
35+
*/
36+
void customize(ConfigBuilder configBuilder);
3637

3738
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/Neo4jAutoConfiguration.java

Lines changed: 58 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,36 @@
1919
import java.io.File;
2020
import java.net.URI;
2121
import java.time.Duration;
22+
import java.util.List;
2223
import java.util.Locale;
2324
import java.util.concurrent.TimeUnit;
25+
import java.util.stream.Collectors;
2426

2527
import org.neo4j.driver.AuthToken;
2628
import org.neo4j.driver.AuthTokens;
2729
import org.neo4j.driver.Config;
30+
import org.neo4j.driver.Config.TrustStrategy;
2831
import org.neo4j.driver.Driver;
2932
import org.neo4j.driver.GraphDatabase;
3033
import org.neo4j.driver.internal.Scheme;
31-
import org.neo4j.driver.net.ServerAddressResolver;
3234

33-
import org.springframework.beans.BeanUtils;
35+
import org.springframework.beans.factory.ObjectProvider;
36+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
3437
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3538
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
39+
import org.springframework.boot.autoconfigure.neo4j.Neo4jProperties.Pool;
40+
import org.springframework.boot.autoconfigure.neo4j.Neo4jProperties.Security;
3641
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3742
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
3843
import org.springframework.context.annotation.Bean;
3944
import org.springframework.context.annotation.Configuration;
4045
import org.springframework.util.StringUtils;
4146

4247
/**
43-
* Automatic configuration of Neo4j's Java Driver.
48+
* {@link EnableAutoConfiguration Auto-configuration} for Neo4j.
4449
*
4550
* @author Michael J. Simons
51+
* @author Stephane Nicoll
4652
* @since 2.4.0
4753
*/
4854
@Configuration(proxyBeanMethods = false)
@@ -51,14 +57,16 @@
5157
public class Neo4jAutoConfiguration {
5258

5359
@Bean
54-
@ConditionalOnMissingBean(Driver.class)
55-
Driver neo4jDriver(Neo4jProperties properties) {
56-
AuthToken authToken = asAuthToken(properties.getAuthentication());
57-
Config config = asDriverConfig(properties);
60+
@ConditionalOnMissingBean
61+
public Driver neo4jDriver(Neo4jProperties properties,
62+
ObjectProvider<ConfigBuilderCustomizer> configBuilderCustomizers) {
63+
AuthToken authToken = mapAuthToken(properties.getAuthentication());
64+
Config config = mapDriverConfig(properties,
65+
configBuilderCustomizers.orderedStream().collect(Collectors.toList()));
5866
return GraphDatabase.driver(properties.getUri(), authToken, config);
5967
}
6068

61-
static AuthToken asAuthToken(Neo4jProperties.Authentication authentication) {
69+
AuthToken mapAuthToken(Neo4jProperties.Authentication authentication) {
6270
String username = authentication.getUsername();
6371
String password = authentication.getPassword();
6472
String kerberosTicket = authentication.getKerberosTicket();
@@ -69,125 +77,110 @@ static AuthToken asAuthToken(Neo4jProperties.Authentication authentication) {
6977
boolean hasKerberosTicket = StringUtils.hasText(kerberosTicket);
7078

7179
if (hasUsername && hasKerberosTicket) {
72-
throw new InvalidConfigurationPropertyValueException("org.neo4j.driver.authentication",
73-
"username=" + username + ",kerberos-ticket=" + kerberosTicket,
74-
"Cannot specify both username and kerberos ticket.");
80+
throw new IllegalStateException(String.format(
81+
"Cannot specify both username ('%s') and kerberos ticket ('%s')", username, kerberosTicket));
7582
}
76-
7783
if (hasUsername && hasPassword) {
7884
return AuthTokens.basic(username, password, realm);
7985
}
80-
8186
if (hasKerberosTicket) {
8287
return AuthTokens.kerberos(kerberosTicket);
8388
}
84-
8589
return AuthTokens.none();
8690
}
8791

88-
static Config asDriverConfig(Neo4jProperties properties) {
92+
Config mapDriverConfig(Neo4jProperties properties, List<ConfigBuilderCustomizer> customizers) {
8993
Config.ConfigBuilder builder = Config.builder();
90-
applyTo(builder, properties.getPool());
94+
configurePoolSettings(builder, properties.getPool());
9195
URI uri = properties.getUri();
9296
String scheme = (uri != null) ? uri.getScheme() : "bolt";
93-
applyTo(builder, properties.getConfig(), isSimpleScheme(scheme));
94-
95-
return builder.withLogging(new Neo4jSpringJclLogging()).build();
97+
configureDriverSettings(builder, properties, isSimpleScheme(scheme));
98+
builder.withLogging(new Neo4jSpringJclLogging());
99+
customizers.forEach((customizer) -> customizer.customize(builder));
100+
return builder.build();
96101
}
97102

98-
static boolean isSimpleScheme(String scheme) {
103+
private boolean isSimpleScheme(String scheme) {
99104
String lowerCaseScheme = scheme.toLowerCase(Locale.ENGLISH);
100105
try {
101106
Scheme.validateScheme(lowerCaseScheme);
102107
}
103108
catch (IllegalArgumentException ex) {
104109
throw new IllegalArgumentException(String.format("'%s' is not a supported scheme.", scheme));
105110
}
106-
107111
return lowerCaseScheme.equals("bolt") || lowerCaseScheme.equals("neo4j");
108112
}
109113

110-
private static void applyTo(Config.ConfigBuilder builder, Neo4jProperties.PoolSettings poolSettings) {
111-
if (poolSettings.isLogLeakedSessions()) {
114+
private void configurePoolSettings(Config.ConfigBuilder builder, Pool pool) {
115+
if (pool.isLogLeakedSessions()) {
112116
builder.withLeakedSessionsLogging();
113117
}
114-
builder.withMaxConnectionPoolSize(poolSettings.getMaxConnectionPoolSize());
115-
Duration idleTimeBeforeConnectionTest = poolSettings.getIdleTimeBeforeConnectionTest();
118+
builder.withMaxConnectionPoolSize(pool.getMaxConnectionPoolSize());
119+
Duration idleTimeBeforeConnectionTest = pool.getIdleTimeBeforeConnectionTest();
116120
if (idleTimeBeforeConnectionTest != null) {
117121
builder.withConnectionLivenessCheckTimeout(idleTimeBeforeConnectionTest.toMillis(), TimeUnit.MILLISECONDS);
118122
}
119-
builder.withMaxConnectionLifetime(poolSettings.getMaxConnectionLifetime().toMillis(), TimeUnit.MILLISECONDS);
120-
builder.withConnectionAcquisitionTimeout(poolSettings.getConnectionAcquisitionTimeout().toMillis(),
123+
builder.withMaxConnectionLifetime(pool.getMaxConnectionLifetime().toMillis(), TimeUnit.MILLISECONDS);
124+
builder.withConnectionAcquisitionTimeout(pool.getConnectionAcquisitionTimeout().toMillis(),
121125
TimeUnit.MILLISECONDS);
122-
123-
if (poolSettings.isMetricsEnabled()) {
126+
if (pool.isMetricsEnabled()) {
124127
builder.withDriverMetrics();
125128
}
126129
else {
127130
builder.withoutDriverMetrics();
128131
}
129132
}
130133

131-
private static void applyTo(Config.ConfigBuilder builder, Neo4jProperties.DriverSettings driverSettings,
134+
private void configureDriverSettings(Config.ConfigBuilder builder, Neo4jProperties properties,
132135
boolean withEncryptionAndTrustSettings) {
133136
if (withEncryptionAndTrustSettings) {
134-
applyEncryptionAndTrustSettings(builder, driverSettings);
135-
}
136-
137-
builder.withConnectionTimeout(driverSettings.getConnectionTimeout().toMillis(), TimeUnit.MILLISECONDS);
138-
builder.withMaxTransactionRetryTime(driverSettings.getMaxTransactionRetryTime().toMillis(),
139-
TimeUnit.MILLISECONDS);
140-
141-
Class<? extends ServerAddressResolver> serverAddressResolverClass = driverSettings
142-
.getServerAddressResolverClass();
143-
if (serverAddressResolverClass != null) {
144-
builder.withResolver(BeanUtils.instantiateClass(serverAddressResolverClass));
137+
applyEncryptionAndTrustSettings(builder, properties.getSecurity());
145138
}
139+
builder.withConnectionTimeout(properties.getConnectionTimeout().toMillis(), TimeUnit.MILLISECONDS);
140+
builder.withMaxTransactionRetryTime(properties.getMaxTransactionRetryTime().toMillis(), TimeUnit.MILLISECONDS);
146141
}
147142

148-
private static void applyEncryptionAndTrustSettings(Config.ConfigBuilder builder,
149-
Neo4jProperties.DriverSettings driverSettings) {
150-
if (driverSettings.isEncrypted()) {
143+
private void applyEncryptionAndTrustSettings(Config.ConfigBuilder builder,
144+
Neo4jProperties.Security securityProperties) {
145+
if (securityProperties.isEncrypted()) {
151146
builder.withEncryption();
152147
}
153148
else {
154149
builder.withoutEncryption();
155150
}
156-
builder.withTrustStrategy(toInternalRepresentation(driverSettings.getTrustSettings()));
151+
builder.withTrustStrategy(mapTrustStrategy(securityProperties));
157152
}
158153

159-
static Config.TrustStrategy toInternalRepresentation(Neo4jProperties.TrustSettings trustSettings) {
160-
String propertyName = "org.neo4j.driver.config.trust-settings";
154+
private Config.TrustStrategy mapTrustStrategy(Neo4jProperties.Security securityProperties) {
155+
String propertyName = "spring.neo4j.security.trust-strategy";
156+
Security.TrustStrategy strategy = securityProperties.getTrustStrategy();
157+
TrustStrategy trustStrategy = createTrustStrategy(securityProperties, propertyName, strategy);
158+
if (securityProperties.isHostnameVerificationEnabled()) {
159+
trustStrategy.withHostnameVerification();
160+
}
161+
else {
162+
trustStrategy.withoutHostnameVerification();
163+
}
164+
return trustStrategy;
165+
}
161166

162-
Config.TrustStrategy internalRepresentation;
163-
Neo4jProperties.TrustSettings.Strategy strategy = trustSettings.getStrategy();
167+
private TrustStrategy createTrustStrategy(Neo4jProperties.Security securityProperties, String propertyName,
168+
Security.TrustStrategy strategy) {
164169
switch (strategy) {
165170
case TRUST_ALL_CERTIFICATES:
166-
internalRepresentation = Config.TrustStrategy.trustAllCertificates();
167-
break;
171+
return TrustStrategy.trustAllCertificates();
168172
case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES:
169-
internalRepresentation = Config.TrustStrategy.trustSystemCertificates();
170-
break;
173+
return TrustStrategy.trustSystemCertificates();
171174
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES:
172-
File certFile = trustSettings.getCertFile();
175+
File certFile = securityProperties.getCertFile();
173176
if (certFile == null || !certFile.isFile()) {
174177
throw new InvalidConfigurationPropertyValueException(propertyName, strategy.name(),
175178
"Configured trust strategy requires a certificate file.");
176179
}
177-
internalRepresentation = Config.TrustStrategy.trustCustomCertificateSignedBy(certFile);
178-
break;
180+
return TrustStrategy.trustCustomCertificateSignedBy(certFile);
179181
default:
180182
throw new InvalidConfigurationPropertyValueException(propertyName, strategy.name(), "Unknown strategy.");
181183
}
182-
183-
if (trustSettings.isHostnameVerificationEnabled()) {
184-
internalRepresentation.withHostnameVerification();
185-
}
186-
else {
187-
internalRepresentation.withoutHostnameVerification();
188-
}
189-
190-
return internalRepresentation;
191184
}
192185

193186
}

0 commit comments

Comments
 (0)