BUG-9079 Make PCEP session recoverable from exception 72/62472/14
authorKevin Wang <kevixw@gmail.com>
Wed, 30 Aug 2017 22:46:39 +0000 (15:46 -0700)
committerKevin Wang <kevixw@gmail.com>
Mon, 23 Oct 2017 21:10:35 +0000 (14:10 -0700)
This patch makes PCEP session recoverable from any netty exception.
Whenever an exception happens, PCEP session will be closed. So it
will be at a clean state when the next session retry comes.

Change-Id: I38a983c44519fd5c12cb9cd0da09fa14c4177ac7
Signed-off-by: Kevin Wang <kevixw@gmail.com>
pcep/impl/src/main/java/org/opendaylight/protocol/pcep/impl/PCEPSessionImpl.java
pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/PCEPSessionImplTest.java
pcep/impl/src/test/java/org/opendaylight/protocol/pcep/impl/SimpleSessionListener.java

index 22ff18e53e4c088556ee7bf8c20ceb8e828bc3a3..e85362b386b30dfa3e83f3740fd7ac43dbb4bd94 100644 (file)
@@ -175,6 +175,16 @@ public class PCEPSessionImpl extends SimpleChannelInboundHandler<Message> implem
         }
     }
 
+    /**
+     * Handle exception occurred in the PCEP session. The session in error state should be closed
+     * properly so that it can be restored later.
+     */
+    @VisibleForTesting
+    void handleException(final Throwable cause) {
+        LOG.error("Exception captured for session {}, closing session.", this, cause);
+        terminate(TerminationReason.UNKNOWN);
+    }
+
     /**
      * Sends message to serialization.
      *
@@ -360,12 +370,17 @@ public class PCEPSessionImpl extends SimpleChannelInboundHandler<Message> implem
     }
 
     @VisibleForTesting
-    public void sessionUp() {
-        this.listener.onSessionUp(this);
+    void sessionUp() {
+        try {
+            this.listener.onSessionUp(this);
+        } catch (final Exception e) {
+            handleException(e);
+            throw e;
+        }
     }
 
     @VisibleForTesting
-    protected final Queue<Long> getUnknownMessagesTimes() {
+    final Queue<Long> getUnknownMessagesTimes() {
         return this.unknownMessagesTimes;
     }
 
@@ -417,6 +432,11 @@ public class PCEPSessionImpl extends SimpleChannelInboundHandler<Message> implem
         this.sessionUp();
     }
 
+    @Override
+    public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
+        handleException(cause);
+    }
+
     @Override
     public Tlvs localSessionCharacteristics() {
         return this.localOpen.getTlvs();
index 033324a078fdb2079efa2116b8e2bac8aa6152b4..c6267f455e0d774a23f78d3f4de445642b9bc563 100644 (file)
@@ -12,7 +12,9 @@ import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Matchers;
 import org.mockito.Mockito;
+import org.opendaylight.protocol.pcep.PCEPSession;
 import org.opendaylight.protocol.pcep.TerminationReason;
 import org.opendaylight.protocol.pcep.impl.spi.Util;
 import org.opendaylight.protocol.pcep.spi.PCEPErrors;
@@ -149,4 +151,39 @@ public class PCEPSessionImplTest extends AbstractPCEPSessionTest {
         Assert.assertEquals(PCEPErrors.UNKNOWN_PLSP_ID.getErrorType(), errMsgs2.getLastSentError().getErrorType().shortValue());
         Assert.assertEquals(PCEPErrors.UNKNOWN_PLSP_ID.getErrorValue(), errMsgs2.getLastSentError().getErrorValue().shortValue());
     }
+
+    @Test
+    public void testExceptionCaught() throws Exception {
+        Assert.assertFalse(this.session.isClosed());
+        Assert.assertTrue(this.listener.up);
+        this.session.exceptionCaught(null, new Throwable("PCEP exception."));
+        Assert.assertFalse(this.listener.up);
+        Assert.assertTrue(this.session.isClosed());
+    }
+
+    @Test
+    public void testSessionRecoveryOnException() throws Exception {
+        this.listener = new SimpleExceptionSessionListener();
+        this.session = Mockito.spy(new PCEPSessionImpl(this.listener, 0, this.channel,
+                this.openMsg.getOpenMessage().getOpen(), this.openMsg.getOpenMessage().getOpen()));
+        Mockito.verify(this.session, Mockito.never()).handleException(Matchers.any());
+        Mockito.verify(this.session, Mockito.never()).sendMessage(Matchers.any());
+        Mockito.verify(this.session, Mockito.never()).closeChannel();
+        try {
+            this.session.sessionUp();
+            Assert.fail();  // expect the exception to be populated
+        } catch (final RuntimeException ignored) {}
+        Assert.assertFalse(this.listener.up);
+        Mockito.verify(this.session).handleException(Matchers.any());
+        Mockito.verify(this.session).sendMessage(Matchers.any(CloseMessage.class));
+        Mockito.verify(this.session).closeChannel();
+    }
+
+    private static class SimpleExceptionSessionListener extends SimpleSessionListener {
+        @Override
+        public synchronized void onSessionUp(final PCEPSession session) {
+            super.onSessionUp(session);
+            throw new RuntimeException("Mocked runtime exception.");
+        }
+    }
 }
index b0d3cf7250d52f7ada00b926d7fac85a4a95ebd7..4c3ba69252dba5f65107b8682231fd56d69e1740 100644 (file)
@@ -55,5 +55,6 @@ public class SimpleSessionListener implements PCEPSessionListener {
     @Override
     public void onSessionTerminated(final PCEPSession session, final PCEPTerminationReason cause) {
         LOG.debug("Session terminated. Cause : {}", cause.toString());
+        this.up = false;
     }
 }