diff --git a/driver/src/main/java/org/neo4j/driver/exceptions/AuthorizationExpiredException.java b/driver/src/main/java/org/neo4j/driver/exceptions/AuthorizationExpiredException.java
index 451ec7667d..d005e82096 100644
--- a/driver/src/main/java/org/neo4j/driver/exceptions/AuthorizationExpiredException.java
+++ b/driver/src/main/java/org/neo4j/driver/exceptions/AuthorizationExpiredException.java
@@ -23,7 +23,7 @@
*
* Error code: Neo.ClientError.Security.AuthorizationExpired
*/
-public class AuthorizationExpiredException extends SecurityException
+public class AuthorizationExpiredException extends SecurityException implements RetryableException
{
public static final String DESCRIPTION = "Authorization information kept on the server has expired, this connection is no longer valid.";
diff --git a/driver/src/main/java/org/neo4j/driver/exceptions/RetryableException.java b/driver/src/main/java/org/neo4j/driver/exceptions/RetryableException.java
new file mode 100644
index 0000000000..1f7be6b358
--- /dev/null
+++ b/driver/src/main/java/org/neo4j/driver/exceptions/RetryableException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) "Neo4j"
+ * Neo4j Sweden AB [http://neo4j.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.neo4j.driver.exceptions;
+
+/**
+ * A marker interface for retryable exceptions.
+ *
+ * This indicates whether an operation that resulted in retryable exception is worth retrying.
+ */
+public interface RetryableException
+{
+}
diff --git a/driver/src/main/java/org/neo4j/driver/exceptions/ServiceUnavailableException.java b/driver/src/main/java/org/neo4j/driver/exceptions/ServiceUnavailableException.java
index d4329835c0..51f997ae9f 100644
--- a/driver/src/main/java/org/neo4j/driver/exceptions/ServiceUnavailableException.java
+++ b/driver/src/main/java/org/neo4j/driver/exceptions/ServiceUnavailableException.java
@@ -20,9 +20,10 @@
/**
* An ServiceUnavailableException indicates that the driver cannot communicate with the cluster.
+ *
* @since 1.1
*/
-public class ServiceUnavailableException extends Neo4jException
+public class ServiceUnavailableException extends Neo4jException implements RetryableException
{
public ServiceUnavailableException( String message )
{
@@ -31,6 +32,6 @@ public ServiceUnavailableException( String message )
public ServiceUnavailableException( String message, Throwable throwable )
{
- super( message, throwable);
+ super( message, throwable );
}
}
diff --git a/driver/src/main/java/org/neo4j/driver/exceptions/SessionExpiredException.java b/driver/src/main/java/org/neo4j/driver/exceptions/SessionExpiredException.java
index 8bbd010866..3aa60e1cdc 100644
--- a/driver/src/main/java/org/neo4j/driver/exceptions/SessionExpiredException.java
+++ b/driver/src/main/java/org/neo4j/driver/exceptions/SessionExpiredException.java
@@ -19,14 +19,14 @@
package org.neo4j.driver.exceptions;
/**
- * A SessionExpiredException indicates that the session can no longer satisfy the criteria under which it
- * was acquired, e.g. a server no longer accepts write requests. A new session needs to be acquired from the driver
- * and all actions taken on the expired session must be replayed.
+ * A SessionExpiredException indicates that the session can no longer satisfy the criteria under which it was acquired, e.g. a server no longer accepts
+ * write requests. A new session needs to be acquired from the driver and all actions taken on the expired session must be replayed.
+ *
* @since 1.1
*/
-public class SessionExpiredException extends Neo4jException
+public class SessionExpiredException extends Neo4jException implements RetryableException
{
- public SessionExpiredException( String message)
+ public SessionExpiredException( String message )
{
super( message );
}
diff --git a/driver/src/main/java/org/neo4j/driver/exceptions/TransientException.java b/driver/src/main/java/org/neo4j/driver/exceptions/TransientException.java
index 37631dd11a..57ee40e7b8 100644
--- a/driver/src/main/java/org/neo4j/driver/exceptions/TransientException.java
+++ b/driver/src/main/java/org/neo4j/driver/exceptions/TransientException.java
@@ -19,11 +19,12 @@
package org.neo4j.driver.exceptions;
/**
- * A TransientException signals a temporary fault that may be worked around by retrying.
- * The error code provided can be used to determine further detail for the problem.
+ * A TransientException signals a temporary fault that may be worked around by retrying. The error code provided can be used to determine further
+ * detail for the problem.
+ *
* @since 1.0
*/
-public class TransientException extends Neo4jException
+public class TransientException extends Neo4jException implements RetryableException
{
public TransientException( String code, String message )
{
diff --git a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java
index cb9d7a7e26..2fcd1a9971 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogic.java
@@ -38,15 +38,11 @@
import org.neo4j.driver.Logger;
import org.neo4j.driver.Logging;
-import org.neo4j.driver.exceptions.AuthorizationExpiredException;
import org.neo4j.driver.exceptions.ClientException;
import org.neo4j.driver.exceptions.Neo4jException;
-import org.neo4j.driver.exceptions.ServiceUnavailableException;
-import org.neo4j.driver.exceptions.SessionExpiredException;
-import org.neo4j.driver.exceptions.TransientException;
+import org.neo4j.driver.exceptions.RetryableException;
import org.neo4j.driver.internal.util.Clock;
import org.neo4j.driver.internal.util.Futures;
-import org.neo4j.driver.util.Experimental;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -148,14 +144,7 @@ public Publisher retryRx( Publisher work )
protected boolean canRetryOn( Throwable error )
{
- return isRetryable( error );
- }
-
- @Experimental
- public static boolean isRetryable( Throwable error )
- {
- return error instanceof SessionExpiredException || error instanceof ServiceUnavailableException || error instanceof AuthorizationExpiredException ||
- isTransientError( error );
+ return error instanceof RetryableException;
}
/**
@@ -351,25 +340,6 @@ private void verifyAfterConstruction()
}
}
- private static boolean isTransientError( Throwable error )
- {
- if ( error instanceof TransientException )
- {
- String code = ((TransientException) error).code();
- // Retries should not happen when transaction was explicitly terminated by the user.
- // Termination of transaction might result in two different error codes depending on where it was
- // terminated. These are really client errors but classification on the server is not entirely correct and
- // they are classified as transient.
- if ( "Neo.TransientError.Transaction.Terminated".equals( code ) ||
- "Neo.TransientError.Transaction.LockClientStopped".equals( code ) )
- {
- return false;
- }
- return true;
- }
- return false;
- }
-
private static List recordError( Throwable error, List errors )
{
if ( errors == null )
diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java b/driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java
index 2abfa62fe6..c2912b730d 100644
--- a/driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java
+++ b/driver/src/main/java/org/neo4j/driver/internal/util/ErrorUtil.java
@@ -100,7 +100,20 @@ else if ( code.equalsIgnoreCase( "Neo.ClientError.Security.TokenExpired" ) )
}
}
case "TransientError":
- return new TransientException( code, message );
+ // Since 5.0 these 2 errors have been moved to ClientError class.
+ // This mapping is required if driver is connection to earlier server versions.
+ if ( "Neo.TransientError.Transaction.Terminated".equals( code ) )
+ {
+ return new ClientException( "Neo.ClientError.Transaction.Terminated", message );
+ }
+ else if ( "Neo.TransientError.Transaction.LockClientStopped".equals( code ) )
+ {
+ return new ClientException( "Neo.ClientError.Transaction.LockClientStopped", message );
+ }
+ else
+ {
+ return new TransientException( code, message );
+ }
default:
return new DatabaseException( code, message );
}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java b/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java
index 0a2acb57b3..1297272ec7 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/retry/ExponentialBackoffRetryLogicTest.java
@@ -471,7 +471,7 @@ void throwsWhenTransactionTerminatedError() throws Exception
ExponentialBackoffRetryLogic logic = newRetryLogic( 1, 13, 1, 0, clock );
Supplier workMock = newWorkMock();
- TransientException error = new TransientException( "Neo.TransientError.Transaction.Terminated", "" );
+ ClientException error = new ClientException( "Neo.ClientError.Transaction.Terminated", "" );
when( workMock.get() ).thenThrow( error ).thenReturn( null );
Exception e = assertThrows( Exception.class, () -> logic.retry( workMock ) );
@@ -489,7 +489,7 @@ void doesNotRetryOnTransactionTerminatedErrorAsync()
ExponentialBackoffRetryLogic retryLogic = newRetryLogic( 1, 13, 1, 0, clock );
Supplier> workMock = newWorkMock();
- TransientException error = new TransientException( "Neo.TransientError.Transaction.Terminated", "" );
+ ClientException error = new ClientException( "Neo.ClientError.Transaction.Terminated", "" );
when( workMock.get() ).thenReturn( failedFuture( error ) );
Exception e = assertThrows( Exception.class, () -> await( retryLogic.retryAsync( workMock ) ) );
@@ -506,7 +506,7 @@ void throwsWhenTransactionLockClientStoppedError() throws Exception
ExponentialBackoffRetryLogic logic = newRetryLogic( 1, 13, 1, 0, clock );
Supplier workMock = newWorkMock();
- TransientException error = new TransientException( "Neo.TransientError.Transaction.LockClientStopped", "" );
+ ClientException error = new ClientException( "Neo.ClientError.Transaction.LockClientStopped", "" );
when( workMock.get() ).thenThrow( error ).thenReturn( null );
Exception e = assertThrows( Exception.class, () -> logic.retry( workMock ) );
@@ -524,7 +524,7 @@ void doesNotRetryOnTransactionLockClientStoppedErrorAsync()
ExponentialBackoffRetryLogic retryLogic = newRetryLogic( 1, 15, 1, 0, clock );
Supplier> workMock = newWorkMock();
- TransientException error = new TransientException( "Neo.TransientError.Transaction.LockClientStopped", "" );
+ ClientException error = new ClientException( "Neo.ClientError.Transaction.LockClientStopped", "" );
when( workMock.get() ).thenReturn( failedFuture( error ) );
Exception e = assertThrows( Exception.class, () -> await( retryLogic.retryAsync( workMock ) ) );
@@ -1437,8 +1437,8 @@ private static Stream cannotBeRetriedErrors()
{
return Stream.of(
new IllegalStateException(),
- new TransientException( "Neo.TransientError.Transaction.Terminated", "" ),
- new TransientException( "Neo.TransientError.Transaction.LockClientStopped", "" )
+ new ClientException( "Neo.ClientError.Transaction.Terminated", "" ),
+ new ClientException( "Neo.ClientError.Transaction.LockClientStopped", "" )
);
}
}
diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java b/driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java
index 25d0c5caae..3dd475fbc6 100644
--- a/driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java
+++ b/driver/src/test/java/org/neo4j/driver/internal/util/ErrorUtilTest.java
@@ -189,4 +189,30 @@ void shouldCreateTokenExpiredException()
assertEquals( code, error.code() );
assertEquals( message, error.getMessage() );
}
+
+ @Test
+ void shouldMapTransientTransactionTerminatedToClientException()
+ {
+ String code = "Neo.TransientError.Transaction.Terminated";
+ String message = "message";
+
+ Neo4jException error = newNeo4jError( code, message );
+
+ assertThat( error, instanceOf( ClientException.class ) );
+ assertEquals( "Neo.ClientError.Transaction.Terminated", error.code() );
+ assertEquals( message, error.getMessage() );
+ }
+
+ @Test
+ void shouldMapTransientTransactionLockClientStoppedToClientException()
+ {
+ String code = "Neo.TransientError.Transaction.LockClientStopped";
+ String message = "message";
+
+ Neo4jException error = newNeo4jError( code, message );
+
+ assertThat( error, instanceOf( ClientException.class ) );
+ assertEquals( "Neo.ClientError.Transaction.LockClientStopped", error.code() );
+ assertEquals( message, error.getMessage() );
+ }
}
diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java
index 80c64cd266..babf5f8576 100644
--- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java
+++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestProcessorHandler.java
@@ -89,7 +89,6 @@ public void channelRead( ChannelHandlerContext ctx, Object msg )
{
if ( throwable != null )
{
-// throwable.printStackTrace();
ctx.writeAndFlush( createErrorResponse( throwable ) );
}
else if ( response != null )
diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java
index 28814d4f87..d599257a76 100644
--- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java
+++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/channel/handler/TestkitRequestResponseMapperHandler.java
@@ -36,7 +36,6 @@ public class TestkitRequestResponseMapperHandler extends ChannelDuplexHandler
public void channelRead( ChannelHandlerContext ctx, Object msg )
{
String testkitMessage = (String) msg;
- System.out.println( testkitMessage );
TestkitRequest testkitRequest;
try
{
@@ -54,7 +53,6 @@ public void write( ChannelHandlerContext ctx, Object msg, ChannelPromise promise
{
TestkitResponse testkitResponse = (TestkitResponse) msg;
String responseStr = objectMapper.writeValueAsString( testkitResponse );
- System.out.println( responseStr );
ctx.writeAndFlush( responseStr, promise );
}