19
19
import java .io .File ;
20
20
import java .net .URI ;
21
21
import java .time .Duration ;
22
+ import java .util .List ;
22
23
import java .util .Locale ;
23
24
import java .util .concurrent .TimeUnit ;
25
+ import java .util .stream .Collectors ;
24
26
25
27
import org .neo4j .driver .AuthToken ;
26
28
import org .neo4j .driver .AuthTokens ;
27
29
import org .neo4j .driver .Config ;
30
+ import org .neo4j .driver .Config .TrustStrategy ;
28
31
import org .neo4j .driver .Driver ;
29
32
import org .neo4j .driver .GraphDatabase ;
30
33
import org .neo4j .driver .internal .Scheme ;
31
- import org .neo4j .driver .net .ServerAddressResolver ;
32
34
33
- import org .springframework .beans .BeanUtils ;
35
+ import org .springframework .beans .factory .ObjectProvider ;
36
+ import org .springframework .boot .autoconfigure .EnableAutoConfiguration ;
34
37
import org .springframework .boot .autoconfigure .condition .ConditionalOnClass ;
35
38
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 ;
36
41
import org .springframework .boot .context .properties .EnableConfigurationProperties ;
37
42
import org .springframework .boot .context .properties .source .InvalidConfigurationPropertyValueException ;
38
43
import org .springframework .context .annotation .Bean ;
39
44
import org .springframework .context .annotation .Configuration ;
40
45
import org .springframework .util .StringUtils ;
41
46
42
47
/**
43
- * Automatic configuration of Neo4j's Java Driver .
48
+ * {@link EnableAutoConfiguration Auto- configuration} for Neo4j.
44
49
*
45
50
* @author Michael J. Simons
51
+ * @author Stephane Nicoll
46
52
* @since 2.4.0
47
53
*/
48
54
@ Configuration (proxyBeanMethods = false )
51
57
public class Neo4jAutoConfiguration {
52
58
53
59
@ 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 ()));
58
66
return GraphDatabase .driver (properties .getUri (), authToken , config );
59
67
}
60
68
61
- static AuthToken asAuthToken (Neo4jProperties .Authentication authentication ) {
69
+ AuthToken mapAuthToken (Neo4jProperties .Authentication authentication ) {
62
70
String username = authentication .getUsername ();
63
71
String password = authentication .getPassword ();
64
72
String kerberosTicket = authentication .getKerberosTicket ();
@@ -69,125 +77,110 @@ static AuthToken asAuthToken(Neo4jProperties.Authentication authentication) {
69
77
boolean hasKerberosTicket = StringUtils .hasText (kerberosTicket );
70
78
71
79
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 ));
75
82
}
76
-
77
83
if (hasUsername && hasPassword ) {
78
84
return AuthTokens .basic (username , password , realm );
79
85
}
80
-
81
86
if (hasKerberosTicket ) {
82
87
return AuthTokens .kerberos (kerberosTicket );
83
88
}
84
-
85
89
return AuthTokens .none ();
86
90
}
87
91
88
- static Config asDriverConfig (Neo4jProperties properties ) {
92
+ Config mapDriverConfig (Neo4jProperties properties , List < ConfigBuilderCustomizer > customizers ) {
89
93
Config .ConfigBuilder builder = Config .builder ();
90
- applyTo (builder , properties .getPool ());
94
+ configurePoolSettings (builder , properties .getPool ());
91
95
URI uri = properties .getUri ();
92
96
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 ();
96
101
}
97
102
98
- static boolean isSimpleScheme (String scheme ) {
103
+ private boolean isSimpleScheme (String scheme ) {
99
104
String lowerCaseScheme = scheme .toLowerCase (Locale .ENGLISH );
100
105
try {
101
106
Scheme .validateScheme (lowerCaseScheme );
102
107
}
103
108
catch (IllegalArgumentException ex ) {
104
109
throw new IllegalArgumentException (String .format ("'%s' is not a supported scheme." , scheme ));
105
110
}
106
-
107
111
return lowerCaseScheme .equals ("bolt" ) || lowerCaseScheme .equals ("neo4j" );
108
112
}
109
113
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 ()) {
112
116
builder .withLeakedSessionsLogging ();
113
117
}
114
- builder .withMaxConnectionPoolSize (poolSettings .getMaxConnectionPoolSize ());
115
- Duration idleTimeBeforeConnectionTest = poolSettings .getIdleTimeBeforeConnectionTest ();
118
+ builder .withMaxConnectionPoolSize (pool .getMaxConnectionPoolSize ());
119
+ Duration idleTimeBeforeConnectionTest = pool .getIdleTimeBeforeConnectionTest ();
116
120
if (idleTimeBeforeConnectionTest != null ) {
117
121
builder .withConnectionLivenessCheckTimeout (idleTimeBeforeConnectionTest .toMillis (), TimeUnit .MILLISECONDS );
118
122
}
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 (),
121
125
TimeUnit .MILLISECONDS );
122
-
123
- if (poolSettings .isMetricsEnabled ()) {
126
+ if (pool .isMetricsEnabled ()) {
124
127
builder .withDriverMetrics ();
125
128
}
126
129
else {
127
130
builder .withoutDriverMetrics ();
128
131
}
129
132
}
130
133
131
- private static void applyTo (Config .ConfigBuilder builder , Neo4jProperties . DriverSettings driverSettings ,
134
+ private void configureDriverSettings (Config .ConfigBuilder builder , Neo4jProperties properties ,
132
135
boolean withEncryptionAndTrustSettings ) {
133
136
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 ());
145
138
}
139
+ builder .withConnectionTimeout (properties .getConnectionTimeout ().toMillis (), TimeUnit .MILLISECONDS );
140
+ builder .withMaxTransactionRetryTime (properties .getMaxTransactionRetryTime ().toMillis (), TimeUnit .MILLISECONDS );
146
141
}
147
142
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 ()) {
151
146
builder .withEncryption ();
152
147
}
153
148
else {
154
149
builder .withoutEncryption ();
155
150
}
156
- builder .withTrustStrategy (toInternalRepresentation ( driverSettings . getTrustSettings () ));
151
+ builder .withTrustStrategy (mapTrustStrategy ( securityProperties ));
157
152
}
158
153
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
+ }
161
166
162
- Config . TrustStrategy internalRepresentation ;
163
- Neo4jProperties . TrustSettings . Strategy strategy = trustSettings . getStrategy ();
167
+ private TrustStrategy createTrustStrategy ( Neo4jProperties . Security securityProperties , String propertyName ,
168
+ Security . TrustStrategy strategy ) {
164
169
switch (strategy ) {
165
170
case TRUST_ALL_CERTIFICATES :
166
- internalRepresentation = Config .TrustStrategy .trustAllCertificates ();
167
- break ;
171
+ return TrustStrategy .trustAllCertificates ();
168
172
case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES :
169
- internalRepresentation = Config .TrustStrategy .trustSystemCertificates ();
170
- break ;
173
+ return TrustStrategy .trustSystemCertificates ();
171
174
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES :
172
- File certFile = trustSettings .getCertFile ();
175
+ File certFile = securityProperties .getCertFile ();
173
176
if (certFile == null || !certFile .isFile ()) {
174
177
throw new InvalidConfigurationPropertyValueException (propertyName , strategy .name (),
175
178
"Configured trust strategy requires a certificate file." );
176
179
}
177
- internalRepresentation = Config .TrustStrategy .trustCustomCertificateSignedBy (certFile );
178
- break ;
180
+ return TrustStrategy .trustCustomCertificateSignedBy (certFile );
179
181
default :
180
182
throw new InvalidConfigurationPropertyValueException (propertyName , strategy .name (), "Unknown strategy." );
181
183
}
182
-
183
- if (trustSettings .isHostnameVerificationEnabled ()) {
184
- internalRepresentation .withHostnameVerification ();
185
- }
186
- else {
187
- internalRepresentation .withoutHostnameVerification ();
188
- }
189
-
190
- return internalRepresentation ;
191
184
}
192
185
193
186
}
0 commit comments