@Override
public synchronized void close() {
- if (this.state != State.IDLE && !this.terminationReasonNotified) {
- this.writeAndFlush(new NotifyBuilder().setErrorCode(BGPError.CEASE.getCode()).setErrorSubcode(BGPError.CEASE.getSubcode()).build());
+ if (this.state != State.IDLE) {
+ if (!this.terminationReasonNotified) {
+ this.writeAndFlush(new NotifyBuilder().setErrorCode(BGPError.CEASE.getCode())
+ .setErrorSubcode(BGPError.CEASE.getSubcode()).build());
+ }
this.closeWithoutMessage();
}
}
private synchronized void notifyTerminationReasonAndCloseWithoutMessage(final Short errorCode, final Short errorSubcode) {
this.terminationReasonNotified = true;
- this.listener.onSessionTerminated(this, new BGPTerminationReason(
- BGPError.forValue(errorCode, errorSubcode)));
this.closeWithoutMessage();
+ this.listener.onSessionTerminated(this, new BGPTerminationReason(
+ BGPError.forValue(errorCode, errorSubcode)));
}
synchronized void endOfInput() {
*
* @param e BGPDocumentedException
*/
- private synchronized void terminate(final BGPDocumentedException e) {
+ @VisibleForTesting
+ synchronized void terminate(final BGPDocumentedException e) {
final BGPError error = e.getError();
final byte[] data = e.getData();
final NotifyBuilder builder = new NotifyBuilder().setErrorCode(error.getCode()).setErrorSubcode(error.getSubcode());
protected synchronized void sessionUp() {
this.sessionStats.startSessionStopwatch();
this.state = State.UP;
- this.sessionState.setSessionState(this.state);
- this.listener.onSessionUp(this);
+ try {
+ this.sessionState.setSessionState(this.state);
+ this.listener.onSessionUp(this);
+ } catch (final Exception e) {
+ handleException(e);
+ throw e;
+ }
}
public synchronized State getState() {
@Override
public synchronized void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
+ handleException(cause);
+ }
+
+ /**
+ * Handle exception occurred in the BGP session. The session in error state should be closed
+ * properly so that it can be restored later.
+ */
+ @VisibleForTesting
+ void handleException(final Throwable cause) {
LOG.warn("BGP session encountered error", cause);
if (cause.getCause() instanceof BGPDocumentedException) {
this.terminate((BGPDocumentedException) cause.getCause());
} else {
- this.close();
+ this.terminate(new BGPDocumentedException(BGPError.CEASE));
}
}
import org.opendaylight.protocol.bgp.parser.BGPError;
import org.opendaylight.protocol.bgp.parser.BgpExtendedMessageUtil;
import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
+import org.opendaylight.protocol.bgp.rib.spi.BGPSessionListener;
+import org.opendaylight.protocol.bgp.rib.spi.BGPTerminationReason;
import org.opendaylight.protocol.bgp.rib.spi.State;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
Assert.assertEquals(BGPError.HOLD_TIMER_EXPIRED.getSubcode(), error.getErrorSubcode().shortValue());
Mockito.verify(this.speakerListener).close();
}
+
+ @Test
+ public void testSessionRecoveryOnException() throws Exception {
+ final BGPSessionListener listener = mock(BGPSessionListener.class);
+ Mockito.doThrow(new RuntimeException("Mocked runtime exception."))
+ .when(listener).onSessionUp(Matchers.any());
+ this.bgpSession = Mockito.spy(new BGPSessionImpl(listener, this.speakerListener, this.classicOpen,
+ this.classicOpen.getHoldTimer(), null));
+ this.bgpSession.setChannelExtMsgCoder(this.classicOpen);
+
+ Mockito.verify(this.bgpSession, Mockito.never()).handleException(Matchers.any());
+ Mockito.verify(this.bgpSession, Mockito.never()).writeAndFlush(Matchers.any(Notification.class));
+ Mockito.verify(this.bgpSession, Mockito.never()).terminate(Matchers.any(BGPDocumentedException.class));
+ try {
+ this.bgpSession.sessionUp();
+ Assert.fail(); // expect the exception to be populated
+ } catch (final RuntimeException ignored) {}
+ Assert.assertNotEquals(State.UP, this.bgpSession.getState());
+ Mockito.verify(this.bgpSession).handleException(Matchers.any());
+ Mockito.verify(this.bgpSession).writeAndFlush(Matchers.any(Notification.class));
+ Mockito.verify(this.bgpSession).terminate(Matchers.any(BGPDocumentedException.class));
+ Mockito.verify(listener).onSessionTerminated(this.bgpSession, new BGPTerminationReason(BGPError.CEASE));
+ }
}
package org.opendaylight.protocol.bgp.rib.spi;
import com.google.common.base.MoreObjects;
+import java.util.Objects;
import org.opendaylight.protocol.bgp.parser.BGPError;
public final class BGPTerminationReason {
return this.error.toString();
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.error);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ return (o instanceof BGPTerminationReason) &&
+ Objects.equals(this.error, ((BGPTerminationReason) o).error);
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(this)