Replace Preconditions.CheckNotNull per RequireNonNull
[bgpcep.git] / bgp / bmp-impl / src / main / java / org / opendaylight / protocol / bmp / impl / session / BmpSessionImpl.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.protocol.bmp.impl.session;
10
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.MoreObjects.ToStringHelper;
15 import com.google.common.base.Preconditions;
16 import io.netty.channel.Channel;
17 import io.netty.channel.ChannelHandlerContext;
18 import io.netty.channel.SimpleChannelInboundHandler;
19 import java.io.IOException;
20 import java.net.InetAddress;
21 import java.net.InetSocketAddress;
22 import javax.annotation.Nonnull;
23 import org.opendaylight.protocol.bmp.api.BmpSession;
24 import org.opendaylight.protocol.bmp.api.BmpSessionListener;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.InitiationMessage;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.Reason;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.TerminationMessage;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev150512.termination.Tlvs;
29 import org.opendaylight.yangtools.yang.binding.Notification;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 public final class BmpSessionImpl extends SimpleChannelInboundHandler<Notification> implements BmpSession {
34
35     private static final Logger LOG = LoggerFactory.getLogger(BmpSessionImpl.class);
36
37     private final BmpSessionListener listener;
38
39     private Channel channel;
40
41     private State state;
42
43     public BmpSessionImpl(@Nonnull final BmpSessionListener listener) {
44         this.listener = requireNonNull(listener);
45         this.state = State.IDLE;
46     }
47
48     @Override
49     protected void channelRead0(final ChannelHandlerContext channelHandlerContext, final Notification msg) throws Exception {
50         this.handleMessage(msg);
51     }
52
53     @Override
54     public void channelInactive(final ChannelHandlerContext ctx) {
55         LOG.debug("Channel {} inactive.", ctx.channel());
56         this.endOfInput();
57
58         try {
59             super.channelInactive(ctx);
60         } catch (final Exception e) {
61             throw new IllegalStateException("Failed to delegate channel inactive event on channel " + ctx.channel(), e);
62         }
63     }
64
65     @Override
66     public void channelActive(final ChannelHandlerContext ctx) throws Exception {
67         this.channel = ctx.channel();
68         LOG.info("Starting session {} <-> {}.", this.channel.localAddress(), this.channel.remoteAddress());
69         sessionUp();
70     }
71
72     @Override
73     public synchronized void close() {
74         LOG.info("Closing session: {}", this);
75         if (this.channel != null) {
76             this.channel.close();
77             this.channel = null;
78             this.state = State.IDLE;
79         }
80     }
81
82     @Override
83     public InetAddress getRemoteAddress() {
84         requireNonNull(this.channel.remoteAddress(), "BMP Channel doesn't have a valid remote address.");
85         return ((InetSocketAddress) this.channel.remoteAddress()).getAddress();
86     }
87
88     @Override
89     public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
90         LOG.error("Exception caught in BMP Session.", cause);
91         close();
92         this.listener.onSessionDown(new IllegalStateException(cause));
93     }
94
95     @Override
96     public String toString() {
97         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
98     }
99
100     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
101         toStringHelper.add("channel", this.channel);
102         return toStringHelper;
103     }
104
105     private synchronized void handleMessage(final Notification msg) {
106         switch (this.state) {
107         case UP:
108             if (msg instanceof InitiationMessage) {
109                 this.state = State.INITIATED;
110                 this.listener.onMessage(msg);
111             } else {
112                 LOG.warn("Unexpected message received {}, expected was BMP Initiation Message. Closing session.", msg);
113                 close();
114             }
115             break;
116         case INITIATED:
117             if (msg instanceof TerminationMessage) {
118                 LOG.info("Session {} terminated by remote with reason: {}", this, getTerminationReason((TerminationMessage) msg));
119                 close();
120             } else {
121                 this.listener.onMessage(msg);
122             }
123             break;
124         case IDLE:
125             throw new IllegalStateException("Received message " + msg + " while BMP Session " + this + " was not active.");
126         default:
127             break;
128         }
129     }
130
131     private static Reason getTerminationReason(final TerminationMessage terminationMessage) {
132         final Tlvs tlvs = terminationMessage.getTlvs();
133         if (tlvs != null && tlvs.getReasonTlv() != null) {
134             return tlvs.getReasonTlv().getReason();
135         }
136         return null;
137     }
138
139     private void endOfInput() {
140         this.listener.onSessionDown(new IOException("End of input detected. Closing the session."));
141     }
142
143     private void sessionUp() {
144         Preconditions.checkArgument(State.IDLE == this.state);
145         this.listener.onSessionUp(this);
146         this.state = State.UP;
147     }
148
149     protected enum State {
150         /**
151          * Waiting for connection to be established.
152          */
153         IDLE,
154         /**
155          * The connection has been established. Waiting for Initiation Message.
156          */
157         UP,
158         /**
159          * The Initiation Messages has been received. Pass incoming messages to session listener.
160          */
161         INITIATED
162     }
163
164 }