11package org .neo4j .shell .state ;
22
33import java .util .ArrayList ;
4+ import java .util .HashMap ;
45import java .util .List ;
56import java .util .Map ;
67import java .util .Optional ;
@@ -54,7 +55,7 @@ public class BoltStateHandler implements TransactionHandler, Connector, Database
5455 private String activeDatabaseNameAsSetByUser ;
5556 private String actualDatabaseNameAsReportedByServer ;
5657 private final boolean isInteractive ;
57- private Bookmark systemBookmark ;
58+ private final Map < String , Bookmark > bookmarks = new HashMap <>() ;
5859
5960 public BoltStateHandler (boolean isInteractive ) {
6061 this (GraphDatabase ::driver , isInteractive );
@@ -77,15 +78,15 @@ public void setActiveDatabase(String databaseName) throws CommandException
7778 activeDatabaseNameAsSetByUser = databaseName ;
7879 try {
7980 if (isConnected ()) {
80- reconnect (true );
81+ reconnect (databaseName , previousDatabaseName );
8182 }
8283 }
8384 catch (ClientException e ) {
8485 if (isInteractive ) {
8586 // We want to try to connect to the previous database
8687 activeDatabaseNameAsSetByUser = previousDatabaseName ;
8788 try {
88- reconnect (true );
89+ reconnect (previousDatabaseName , previousDatabaseName );
8990 }
9091 catch (Exception e2 ) {
9192 e .addSuppressed (e2 );
@@ -158,10 +159,11 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
158159 }
159160 final AuthToken authToken = AuthTokens .basic (connectionConfig .username (), connectionConfig .password ());
160161 try {
162+ String previousDatabaseName = activeDatabaseNameAsSetByUser ;
161163 try {
162- setActiveDatabase ( connectionConfig .database () );
164+ activeDatabaseNameAsSetByUser = connectionConfig .database ();
163165 driver = getDriver (connectionConfig , authToken );
164- reconnect (command );
166+ reconnect (activeDatabaseNameAsSetByUser , previousDatabaseName , command );
165167 } catch (org .neo4j .driver .exceptions .ServiceUnavailableException e ) {
166168 String scheme = connectionConfig .scheme ();
167169 String fallbackScheme ;
@@ -188,7 +190,7 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
188190 connectionConfig .encryption (),
189191 connectionConfig .database ());
190192 driver = getDriver (connectionConfig , authToken );
191- reconnect ();
193+ reconnect (activeDatabaseNameAsSetByUser , previousDatabaseName );
192194 }
193195 } catch (Throwable t ) {
194196 try {
@@ -200,35 +202,24 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
200202 }
201203 }
202204
203- private void reconnect () throws CommandException {
204- reconnect (true , null );
205+ private void reconnect (String databaseToConnectTo , String previousDatabase ) throws CommandException {
206+ reconnect (databaseToConnectTo , previousDatabase , null );
205207 }
206208
207- private void reconnect (ThrowingAction <CommandException > command ) throws CommandException {
208- reconnect (true , command );
209- }
210-
211- private void reconnect (boolean keepBokmark ) throws CommandException {
212- reconnect (keepBokmark , null );
213- }
214-
215- private void reconnect ( boolean keepBookmark , ThrowingAction <CommandException > command ) throws CommandException {
209+ private void reconnect ( String databaseToConnectTo ,
210+ String previousDatabase ,
211+ ThrowingAction <CommandException > command ) throws CommandException {
216212 SessionConfig .Builder builder = SessionConfig .builder ();
217213 builder .withDefaultAccessMode ( AccessMode .WRITE );
218- if ( !ABSENT_DB_NAME .equals ( activeDatabaseNameAsSetByUser ) )
214+ if ( !ABSENT_DB_NAME .equals ( databaseToConnectTo ) )
219215 {
220- builder .withDatabase ( activeDatabaseNameAsSetByUser );
216+ builder .withDatabase ( databaseToConnectTo );
221217 }
222- if ( session != null && keepBookmark )
218+ closeSession ( previousDatabase );
219+ final Bookmark bookmarkForDBToConnectTo = bookmarks .get ( databaseToConnectTo );
220+ if ( bookmarkForDBToConnectTo != null )
223221 {
224- // Save the last bookmark and close the session
225- final Bookmark bookmark = session .lastBookmark ();
226- session .close ();
227- builder .withBookmarks ( bookmark );
228- }
229- else if ( systemBookmark != null )
230- {
231- builder .withBookmarks ( systemBookmark );
222+ builder .withBookmarks ( bookmarkForDBToConnectTo );
232223 }
233224
234225 session = driver .session ( builder .build () );
@@ -237,6 +228,22 @@ else if ( systemBookmark != null )
237228 connect (command );
238229 }
239230
231+ /**
232+ * Closes the session, if there is any.
233+ * Saves a bookmark for the database currently connected to.
234+ * @param databaseName the name of the database currently connected to
235+ */
236+ private void closeSession ( String databaseName )
237+ {
238+ if ( session != null )
239+ {
240+ // Save the last bookmark and close the session
241+ final Bookmark bookmarkForPreviousDB = session .lastBookmark ();
242+ session .close ();
243+ bookmarks .put (databaseName , bookmarkForPreviousDB );
244+ }
245+ }
246+
240247 private void connect ( ThrowingAction <CommandException > command ) throws CommandException
241248 {
242249 ThrowingAction <CommandException > toCall = command == null ? getPing () : () ->
@@ -314,7 +321,7 @@ public Optional<BoltResult> runCypher(@Nonnull String cypher,
314321 } catch (SessionExpiredException e ) {
315322 // Server is no longer accepting writes, reconnect and try again.
316323 // If it still fails, leave it up to the user
317- reconnect ();
324+ reconnect (activeDatabaseNameAsSetByUser , activeDatabaseNameAsSetByUser );
318325 return getBoltResult (cypher , queryParams );
319326 }
320327 }
@@ -339,10 +346,9 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
339346 try {
340347 driver = getDriver (connectionConfig , authToken );
341348
342- SessionConfig .Builder builder = SessionConfig .builder ()
343- .withDefaultAccessMode (AccessMode .WRITE )
344- .withDatabase (SYSTEM_DB_NAME );
345- session = driver .session (builder .build ());
349+ activeDatabaseNameAsSetByUser = SYSTEM_DB_NAME ;
350+ // Supply empty command, so that we do not run ping.
351+ reconnect ( SYSTEM_DB_NAME , SYSTEM_DB_NAME , () -> {} );
346352
347353 String command ;
348354 Value parameters ;
@@ -361,17 +367,19 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
361367 connectionConfig .setPassword (connectionConfig .newPassword ());
362368 connectionConfig .setNewPassword (null );
363369
364- // Save a system bookmark to make sure we wait for the password change to propagate on reconnection
365- systemBookmark = session .lastBookmark ();
366-
367370 silentDisconnect ();
368371 } catch (Throwable t ) {
369372 try {
370373 silentDisconnect ();
371374 } catch (Exception e ) {
372375 t .addSuppressed (e );
373376 }
374- throw t ;
377+ if (t instanceof RuntimeException ) {
378+ throw (RuntimeException ) t ;
379+ }
380+ // The only checked exception is CommandException and we know that
381+ // we cannot get that since we supply an empty command.
382+ throw new RuntimeException (t );
375383 }
376384 }
377385
@@ -404,9 +412,7 @@ private void resetActualDbName() {
404412 */
405413 void silentDisconnect () {
406414 try {
407- if (session != null ) {
408- session .close ();
409- }
415+ closeSession ( activeDatabaseNameAsSetByUser );
410416 if (driver != null ) {
411417 driver .close ();
412418 }
0 commit comments