11package org .neo4j .shell .state ;
22
3+ import java .util .HashMap ;
34import java .util .List ;
45import java .util .Map ;
56import java .util .Optional ;
@@ -51,7 +52,7 @@ public class BoltStateHandler implements TransactionHandler, Connector, Database
5152 private String activeDatabaseNameAsSetByUser ;
5253 private String actualDatabaseNameAsReportedByServer ;
5354 private final boolean isInteractive ;
54- private Bookmark systemBookmark ;
55+ private final Map < String , Bookmark > bookmarks = new HashMap <>() ;
5556 private Transaction tx = null ;
5657
5758 public BoltStateHandler (boolean isInteractive ) {
@@ -75,15 +76,15 @@ public void setActiveDatabase(String databaseName) throws CommandException
7576 activeDatabaseNameAsSetByUser = databaseName ;
7677 try {
7778 if (isConnected ()) {
78- reconnect (true );
79+ reconnect (databaseName , previousDatabaseName );
7980 }
8081 }
8182 catch (ClientException e ) {
8283 if (isInteractive ) {
8384 // We want to try to connect to the previous database
8485 activeDatabaseNameAsSetByUser = previousDatabaseName ;
8586 try {
86- reconnect (true );
87+ reconnect (previousDatabaseName , previousDatabaseName );
8788 }
8889 catch (Exception e2 ) {
8990 e .addSuppressed (e2 );
@@ -161,10 +162,11 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
161162 }
162163 final AuthToken authToken = AuthTokens .basic (connectionConfig .username (), connectionConfig .password ());
163164 try {
165+ String previousDatabaseName = activeDatabaseNameAsSetByUser ;
164166 try {
165- setActiveDatabase ( connectionConfig .database () );
167+ activeDatabaseNameAsSetByUser = connectionConfig .database ();
166168 driver = getDriver (connectionConfig , authToken );
167- reconnect (command );
169+ reconnect (activeDatabaseNameAsSetByUser , previousDatabaseName , command );
168170 } catch (org .neo4j .driver .exceptions .ServiceUnavailableException e ) {
169171 String scheme = connectionConfig .scheme ();
170172 String fallbackScheme ;
@@ -191,7 +193,7 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
191193 connectionConfig .encryption (),
192194 connectionConfig .database ());
193195 driver = getDriver (connectionConfig , authToken );
194- reconnect (command );
196+ reconnect (activeDatabaseNameAsSetByUser , previousDatabaseName , command );
195197 }
196198 } catch (Throwable t ) {
197199 try {
@@ -203,35 +205,24 @@ public void connect( @Nonnull ConnectionConfig connectionConfig, ThrowingAction<
203205 }
204206 }
205207
206- private void reconnect () throws CommandException {
207- reconnect (true , null );
208+ private void reconnect (String databaseToConnectTo , String previousDatabase ) throws CommandException {
209+ reconnect (databaseToConnectTo , previousDatabase , null );
208210 }
209211
210- private void reconnect (ThrowingAction <CommandException > command ) throws CommandException {
211- reconnect (true , command );
212- }
213-
214- private void reconnect (boolean keepBokmark ) throws CommandException {
215- reconnect (keepBokmark , null );
216- }
217-
218- private void reconnect ( boolean keepBookmark , ThrowingAction <CommandException > command ) throws CommandException {
212+ private void reconnect ( String databaseToConnectTo ,
213+ String previousDatabase ,
214+ ThrowingAction <CommandException > command ) throws CommandException {
219215 SessionConfig .Builder builder = SessionConfig .builder ();
220216 builder .withDefaultAccessMode ( AccessMode .WRITE );
221- if ( !ABSENT_DB_NAME .equals ( activeDatabaseNameAsSetByUser ) )
217+ if ( !ABSENT_DB_NAME .equals ( databaseToConnectTo ) )
222218 {
223- builder .withDatabase ( activeDatabaseNameAsSetByUser );
219+ builder .withDatabase ( databaseToConnectTo );
224220 }
225- if ( session != null && keepBookmark )
221+ closeSession ( previousDatabase );
222+ final Bookmark bookmarkForDBToConnectTo = bookmarks .get ( databaseToConnectTo );
223+ if ( bookmarkForDBToConnectTo != null )
226224 {
227- // Save the last bookmark and close the session
228- final Bookmark bookmark = session .lastBookmark ();
229- session .close ();
230- builder .withBookmarks ( bookmark );
231- }
232- else if ( systemBookmark != null )
233- {
234- builder .withBookmarks ( systemBookmark );
225+ builder .withBookmarks ( bookmarkForDBToConnectTo );
235226 }
236227
237228 session = driver .session ( builder .build () );
@@ -240,6 +231,22 @@ else if ( systemBookmark != null )
240231 connect (command );
241232 }
242233
234+ /**
235+ * Closes the session, if there is any.
236+ * Saves a bookmark for the database currently connected to.
237+ * @param databaseName the name of the database currently connected to
238+ */
239+ private void closeSession ( String databaseName )
240+ {
241+ if ( session != null )
242+ {
243+ // Save the last bookmark and close the session
244+ final Bookmark bookmarkForPreviousDB = session .lastBookmark ();
245+ session .close ();
246+ bookmarks .put (databaseName , bookmarkForPreviousDB );
247+ }
248+ }
249+
243250 private void connect ( ThrowingAction <CommandException > command ) throws CommandException
244251 {
245252 ThrowingAction <CommandException > toCall = command == null ? getPing () : () ->
@@ -323,7 +330,7 @@ public Optional<BoltResult> runCypher(@Nonnull String cypher,
323330 } catch (SessionExpiredException e ) {
324331 // Server is no longer accepting writes, reconnect and try again.
325332 // If it still fails, leave it up to the user
326- reconnect ();
333+ reconnect (activeDatabaseNameAsSetByUser , activeDatabaseNameAsSetByUser );
327334 return getBoltResult (cypher , queryParams );
328335 }
329336 }
@@ -348,10 +355,9 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
348355 try {
349356 driver = getDriver (connectionConfig , authToken );
350357
351- SessionConfig .Builder builder = SessionConfig .builder ()
352- .withDefaultAccessMode (AccessMode .WRITE )
353- .withDatabase (SYSTEM_DB_NAME );
354- session = driver .session (builder .build ());
358+ activeDatabaseNameAsSetByUser = SYSTEM_DB_NAME ;
359+ // Supply empty command, so that we do not run ping.
360+ reconnect ( SYSTEM_DB_NAME , SYSTEM_DB_NAME , () -> {} );
355361
356362 String command ;
357363 Value parameters ;
@@ -370,17 +376,19 @@ public void changePassword( @Nonnull ConnectionConfig connectionConfig )
370376 connectionConfig .setPassword (connectionConfig .newPassword ());
371377 connectionConfig .setNewPassword (null );
372378
373- // Save a system bookmark to make sure we wait for the password change to propagate on reconnection
374- systemBookmark = session .lastBookmark ();
375-
376379 silentDisconnect ();
377380 } catch (Throwable t ) {
378381 try {
379382 silentDisconnect ();
380383 } catch (Exception e ) {
381384 t .addSuppressed (e );
382385 }
383- throw t ;
386+ if (t instanceof RuntimeException ) {
387+ throw (RuntimeException ) t ;
388+ }
389+ // The only checked exception is CommandException and we know that
390+ // we cannot get that since we supply an empty command.
391+ throw new RuntimeException (t );
384392 }
385393 }
386394
@@ -419,9 +427,7 @@ private void resetActualDbName() {
419427 */
420428 void silentDisconnect () {
421429 try {
422- if (session != null ) {
423- session .close ();
424- }
430+ closeSession ( activeDatabaseNameAsSetByUser );
425431 if (driver != null ) {
426432 driver .close ();
427433 }
0 commit comments