Skip to content

Commit e4435e7

Browse files
author
Zhen Li
authored
Merge pull request #291 from lutovich/1.1-concurrent-driver-stop
Better handling of concurrent driver close
2 parents cec0652 + 668815c commit e4435e7

14 files changed

+1244
-333
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Copyright (c) 2002-2016 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal;
20+
21+
import java.io.IOException;
22+
import java.net.URI;
23+
import java.security.GeneralSecurityException;
24+
25+
import org.neo4j.driver.internal.cluster.RoutingSettings;
26+
import org.neo4j.driver.internal.net.BoltServerAddress;
27+
import org.neo4j.driver.internal.net.SocketConnector;
28+
import org.neo4j.driver.internal.net.pooling.PoolSettings;
29+
import org.neo4j.driver.internal.net.pooling.SocketConnectionPool;
30+
import org.neo4j.driver.internal.security.SecurityPlan;
31+
import org.neo4j.driver.internal.spi.ConnectionPool;
32+
import org.neo4j.driver.internal.spi.Connector;
33+
import org.neo4j.driver.internal.util.Clock;
34+
import org.neo4j.driver.v1.AuthToken;
35+
import org.neo4j.driver.v1.AuthTokens;
36+
import org.neo4j.driver.v1.Config;
37+
import org.neo4j.driver.v1.Driver;
38+
import org.neo4j.driver.v1.Logger;
39+
import org.neo4j.driver.v1.exceptions.ClientException;
40+
41+
import static java.lang.String.format;
42+
import static org.neo4j.driver.internal.security.SecurityPlan.insecure;
43+
import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED;
44+
45+
public class DriverFactory
46+
{
47+
public final Driver newInstance( URI uri, AuthToken authToken, RoutingSettings routingSettings, Config config )
48+
{
49+
BoltServerAddress address = BoltServerAddress.from( uri );
50+
SecurityPlan securityPlan = createSecurityPlan( address, config );
51+
ConnectionPool connectionPool = createConnectionPool( authToken, securityPlan, config );
52+
53+
try
54+
{
55+
return createDriver( address, uri.getScheme(), connectionPool, config, routingSettings, securityPlan );
56+
}
57+
catch ( Throwable driverError )
58+
{
59+
// we need to close the connection pool if driver creation threw exception
60+
try
61+
{
62+
connectionPool.close();
63+
}
64+
catch ( Throwable closeError )
65+
{
66+
driverError.addSuppressed( closeError );
67+
}
68+
throw driverError;
69+
}
70+
}
71+
72+
private Driver createDriver( BoltServerAddress address, String scheme, ConnectionPool connectionPool,
73+
Config config, RoutingSettings routingSettings, SecurityPlan securityPlan )
74+
{
75+
switch ( scheme.toLowerCase() )
76+
{
77+
case "bolt":
78+
return createDirectDriver( address, connectionPool, config, securityPlan );
79+
case "bolt+routing":
80+
return createRoutingDriver( address, connectionPool, config, routingSettings, securityPlan );
81+
default:
82+
throw new ClientException( format( "Unsupported URI scheme: %s", scheme ) );
83+
}
84+
}
85+
86+
/**
87+
* Creates new {@link DirectDriver}.
88+
* <p>
89+
* <b>This method is package-private only for testing</b>
90+
*/
91+
DirectDriver createDirectDriver( BoltServerAddress address, ConnectionPool connectionPool, Config config,
92+
SecurityPlan securityPlan )
93+
{
94+
return new DirectDriver( address, connectionPool, securityPlan, config.logging() );
95+
}
96+
97+
/**
98+
* Creates new {@link RoutingDriver}.
99+
* <p>
100+
* <b>This method is package-private only for testing</b>
101+
*/
102+
RoutingDriver createRoutingDriver( BoltServerAddress address, ConnectionPool connectionPool,
103+
Config config, RoutingSettings routingSettings, SecurityPlan securityPlan )
104+
{
105+
return new RoutingDriver( routingSettings, address, connectionPool, securityPlan, Clock.SYSTEM,
106+
config.logging() );
107+
}
108+
109+
/**
110+
* Creates new {@link ConnectionPool}.
111+
* <p>
112+
* <b>This method is package-private only for testing</b>
113+
*/
114+
ConnectionPool createConnectionPool( AuthToken authToken, SecurityPlan securityPlan, Config config )
115+
{
116+
authToken = authToken == null ? AuthTokens.none() : authToken;
117+
118+
ConnectionSettings connectionSettings = new ConnectionSettings( authToken );
119+
PoolSettings poolSettings = new PoolSettings( config.maxIdleConnectionPoolSize() );
120+
Connector connector = new SocketConnector( connectionSettings, securityPlan, config.logging() );
121+
122+
return new SocketConnectionPool( poolSettings, connector, Clock.SYSTEM, config.logging() );
123+
}
124+
125+
private static SecurityPlan createSecurityPlan( BoltServerAddress address, Config config )
126+
{
127+
try
128+
{
129+
return createSecurityPlanImpl( address, config );
130+
}
131+
catch ( GeneralSecurityException | IOException ex )
132+
{
133+
throw new ClientException( "Unable to establish SSL parameters", ex );
134+
}
135+
}
136+
137+
/*
138+
* Establish a complete SecurityPlan based on the details provided for
139+
* driver construction.
140+
*/
141+
private static SecurityPlan createSecurityPlanImpl( BoltServerAddress address, Config config )
142+
throws GeneralSecurityException, IOException
143+
{
144+
Config.EncryptionLevel encryptionLevel = config.encryptionLevel();
145+
boolean requiresEncryption = encryptionLevel.equals( REQUIRED );
146+
147+
if ( requiresEncryption )
148+
{
149+
Logger logger = config.logging().getLog( "session" );
150+
switch ( config.trustStrategy().strategy() )
151+
{
152+
153+
// DEPRECATED CASES //
154+
case TRUST_ON_FIRST_USE:
155+
logger.warn(
156+
"Option `TRUST_ON_FIRST_USE` has been deprecated and will be removed in a future " +
157+
"version of the driver. Please switch to use `TRUST_ALL_CERTIFICATES` instead." );
158+
return SecurityPlan.forTrustOnFirstUse( config.trustStrategy().certFile(), address, logger );
159+
case TRUST_SIGNED_CERTIFICATES:
160+
logger.warn(
161+
"Option `TRUST_SIGNED_CERTIFICATE` has been deprecated and will be removed in a future " +
162+
"version of the driver. Please switch to use `TRUST_CUSTOM_CA_SIGNED_CERTIFICATES` instead." );
163+
// intentional fallthrough
164+
// END OF DEPRECATED CASES //
165+
166+
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES:
167+
return SecurityPlan.forCustomCASignedCertificates( config.trustStrategy().certFile() );
168+
case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES:
169+
return SecurityPlan.forSystemCASignedCertificates();
170+
case TRUST_ALL_CERTIFICATES:
171+
return SecurityPlan.forAllCertificates();
172+
default:
173+
throw new ClientException(
174+
"Unknown TLS authentication strategy: " + config.trustStrategy().strategy().name() );
175+
}
176+
}
177+
else
178+
{
179+
return insecure();
180+
}
181+
}
182+
}

driver/src/main/java/org/neo4j/driver/internal/connector/socket/SocketUtils.java

Lines changed: 0 additions & 83 deletions
This file was deleted.

driver/src/main/java/org/neo4j/driver/internal/net/ConcurrencyGuardingConnection.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,15 +174,9 @@ public void receiveOne()
174174
@Override
175175
public void close()
176176
{
177-
try
178-
{
179-
markAsInUse();
180-
delegate.close();
181-
}
182-
finally
183-
{
184-
markAsAvailable();
185-
}
177+
// It is fine to call close concurrently with this connection being used somewhere else.
178+
// This could happen when driver is closed while there still exist sessions that do some work.
179+
delegate.close();
186180
}
187181

188182
@Override
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) 2002-2016 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal.net;
20+
21+
import java.util.Map;
22+
23+
import org.neo4j.driver.internal.ConnectionSettings;
24+
import org.neo4j.driver.internal.security.InternalAuthToken;
25+
import org.neo4j.driver.internal.security.SecurityPlan;
26+
import org.neo4j.driver.internal.spi.Connection;
27+
import org.neo4j.driver.internal.spi.Connector;
28+
import org.neo4j.driver.v1.AuthToken;
29+
import org.neo4j.driver.v1.AuthTokens;
30+
import org.neo4j.driver.v1.Logging;
31+
import org.neo4j.driver.v1.Value;
32+
import org.neo4j.driver.v1.exceptions.ClientException;
33+
34+
public class SocketConnector implements Connector
35+
{
36+
private final ConnectionSettings connectionSettings;
37+
private final SecurityPlan securityPlan;
38+
private final Logging logging;
39+
40+
public SocketConnector( ConnectionSettings connectionSettings, SecurityPlan securityPlan, Logging logging )
41+
{
42+
this.connectionSettings = connectionSettings;
43+
this.securityPlan = securityPlan;
44+
this.logging = logging;
45+
}
46+
47+
@Override
48+
public final Connection connect( BoltServerAddress address )
49+
{
50+
Connection connection = createConnection( address, securityPlan, logging );
51+
52+
// Because SocketConnection is not thread safe, wrap it in this guard
53+
// to ensure concurrent access leads causes application errors
54+
connection = new ConcurrencyGuardingConnection( connection );
55+
56+
try
57+
{
58+
connection.init( connectionSettings.userAgent(), tokenAsMap( connectionSettings.authToken() ) );
59+
}
60+
catch ( Throwable initError )
61+
{
62+
connection.close();
63+
throw initError;
64+
}
65+
66+
return connection;
67+
}
68+
69+
/**
70+
* Create new connection.
71+
* <p>
72+
* <b>This method is package-private only for testing</b>
73+
*/
74+
Connection createConnection( BoltServerAddress address, SecurityPlan securityPlan, Logging logging )
75+
{
76+
return new SocketConnection( address, securityPlan, logging );
77+
}
78+
79+
private static Map<String,Value> tokenAsMap( AuthToken token )
80+
{
81+
if ( token instanceof InternalAuthToken )
82+
{
83+
return ((InternalAuthToken) token).toMap();
84+
}
85+
else
86+
{
87+
throw new ClientException(
88+
"Unknown authentication token, `" + token + "`. Please use one of the supported " +
89+
"tokens from `" + AuthTokens.class.getSimpleName() + "`." );
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)