d88c29bffe1e8f1391441fa9232b009a46745ef0
[bgpcep.git] / bmp / 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 javax.annotation.concurrent.GuardedBy;
24 import org.opendaylight.protocol.bmp.api.BmpSession;
25 import org.opendaylight.protocol.bmp.api.BmpSessionListener;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.InitiationMessage;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.Reason;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.TerminationMessage;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bmp.message.rev171207.termination.Tlvs;
30 import org.opendaylight.yangtools.yang.binding.Notification;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 public final class BmpSessionImpl extends SimpleChannelInboundHandler<Notification> implements BmpSession {
35
36     private static final Logger LOG = LoggerFactory.getLogger(BmpSessionImpl.class);
37
38     private final BmpSessionListener listener;
39     @GuardedBy("this")
40     private Channel channel;
41     @GuardedBy("this")
42     private State state;
43
44     public BmpSessionImpl(@Nonnull final BmpSessionListener listener) {
45         this.listener = requireNonNull(listener);
46         this.state = State.IDLE;
47     }
48
49     @Override
50     protected void channelRead0(final ChannelHandlerContext channelHandlerContext, final Notification msg) {
51         this.handleMessage(msg);
52     }
53
54     @Override
55     @SuppressWarnings("checkstyle:IllegalCatch")
56     public void channelInactive(final ChannelHandlerContext ctx) {
57         LOG.debug("Channel {} inactive.", ctx.channel());
58         this.endOfInput();
59
60         try {
61             super.channelInactive(ctx);
62         } catch (final Exception e) {
63             throw new IllegalStateException("Failed to delegate channel inactive event on channel " + ctx.channel(), e);
64         }
65     }
66
67     @Override
68     public synchronized void channelActive(final ChannelHandlerContext ctx) {
69         this.channel = ctx.channel();
70         LOG.info("Starting session {} <-> {}.", this.channel.localAddress(), this.channel.remoteAddress());
71         Preconditions.checkArgument(State.IDLE == this.state);
72         this.listener.onSessionUp(this);
73         this.state = State.UP;
74     }
75
76     @Override
77     public synchronized void close() {
78         LOG.info("Closing session: {}", this);
79         if (this.channel != null) {
80             this.channel.close();
81             this.channel = null;
82             this.state = State.IDLE;
83         }
84     }
85
86     @Override
87     public synchronized InetAddress getRemoteAddress() {
88         final InetSocketAddress address = (InetSocketAddress) this.channel.remoteAddress();
89         requireNonNull(address, "BMP Channel doesn't have a valid remote address.");
90         return address.getAddress();
91     }
92
93     @Override
94     public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
95         LOG.error("Exception caught in BMP Session.", cause);
96         close();
97         this.listener.onSessionDown(new IllegalStateException(cause));
98     }
99
100     @Override
101     public String toString() {
102         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
103     }
104
105     private synchronized ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
106         toStringHelper.add("channel", this.channel);
107         return toStringHelper;
108     }
109
110     private synchronized void handleMessage(final Notification msg) {
111         switch (this.state) {
112             case UP:
113                 if (msg instanceof InitiationMessage) {
114                     this.state = State.INITIATED;
115                     this.listener.onMessage(msg);
116                 } else {
117                     LOG.warn("Unexpected message received {}, "
118                             + "expected was BMP Initiation Message. Closing session.", msg);
119                     close();
120                 }
121                 break;
122             case INITIATED:
123                 if (msg instanceof TerminationMessage) {
124                     LOG.info("Session {} terminated by remote with reason: {}",
125                             this, getTerminationReason((TerminationMessage) msg));
126                     close();
127                 } else {
128                     this.listener.onMessage(msg);
129                 }
130                 break;
131             case IDLE:
132                 throw new IllegalStateException("Received message " + msg
133                         + " while BMP Session " + this + " was not active.");
134             default:
135                 break;
136         }
137     }
138
139     private static Reason getTerminationReason(final TerminationMessage terminationMessage) {
140         final Tlvs tlvs = terminationMessage.getTlvs();
141         if (tlvs != null && tlvs.getReasonTlv() != null) {
142             return tlvs.getReasonTlv().getReason();
143         }
144         return null;
145     }
146
147     private void endOfInput() {
148         this.listener.onSessionDown(new IOException("End of input detected. Closing the session."));
149     }
150
151     protected enum State {
152         /**
153          * Waiting for connection to be established.
154          */
155         IDLE,
156         /**
157          * The connection has been established. Waiting for Initiation Message.
158          */
159         UP,
160         /**
161          * The Initiation Messages has been received. Pass incoming messages to session listener.
162          */
163         INITIATED
164     }
165
166 }