8b0e3bbd99eeb4d1cf6e28e355e102f73dba4c43
[bgpcep.git] / pcep / impl / src / main / java / org / opendaylight / protocol / pcep / impl / PCEPSessionImpl.java
1 /*
2  * Copyright (c) 2013 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 package org.opendaylight.protocol.pcep.impl;
9
10 import io.netty.channel.ChannelHandlerContext;
11
12 import java.io.IOException;
13 import java.util.ArrayList;
14 import java.util.Date;
15 import java.util.LinkedList;
16 import java.util.List;
17 import java.util.Queue;
18 import java.util.Timer;
19 import java.util.TimerTask;
20
21 import org.opendaylight.protocol.framework.DeserializerException;
22 import org.opendaylight.protocol.framework.DocumentedException;
23 import org.opendaylight.protocol.framework.ProtocolMessage;
24 import org.opendaylight.protocol.framework.ProtocolMessageFactory;
25 import org.opendaylight.protocol.framework.ProtocolSession;
26 import org.opendaylight.protocol.framework.SessionParent;
27 import org.opendaylight.protocol.pcep.PCEPCloseTermination;
28 import org.opendaylight.protocol.pcep.PCEPConnection;
29 import org.opendaylight.protocol.pcep.PCEPDeserializerException;
30 import org.opendaylight.protocol.pcep.PCEPErrorTermination;
31 import org.opendaylight.protocol.pcep.PCEPErrors;
32 import org.opendaylight.protocol.pcep.PCEPMessage;
33 import org.opendaylight.protocol.pcep.PCEPSession;
34 import org.opendaylight.protocol.pcep.PCEPSessionListener;
35 import org.opendaylight.protocol.pcep.PCEPSessionPreferences;
36 import org.opendaylight.protocol.pcep.PCEPSessionProposalChecker;
37 import org.opendaylight.protocol.pcep.PCEPTlv;
38 import org.opendaylight.protocol.pcep.impl.message.PCEPRawMessage;
39 import org.opendaylight.protocol.pcep.message.PCEPCloseMessage;
40 import org.opendaylight.protocol.pcep.message.PCEPErrorMessage;
41 import org.opendaylight.protocol.pcep.message.PCEPKeepAliveMessage;
42 import org.opendaylight.protocol.pcep.message.PCEPOpenMessage;
43 import org.opendaylight.protocol.pcep.object.PCEPCloseObject;
44 import org.opendaylight.protocol.pcep.object.PCEPCloseObject.Reason;
45 import org.opendaylight.protocol.pcep.object.PCEPErrorObject;
46 import org.opendaylight.protocol.pcep.object.PCEPOpenObject;
47 import org.opendaylight.protocol.pcep.tlv.NodeIdentifierTlv;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 /**
52  * Implementation of PCEPSession. (Not final for testing.)
53  */
54 class PCEPSessionImpl implements PCEPSession, ProtocolSession, PCEPSessionRuntimeMXBean {
55
56         /**
57          * KeepAlive Timer is to be scheduled periodically, each time it starts, it sends KeepAlive Message.
58          */
59         private class KeepAliveTimer extends TimerTask {
60                 private final PCEPSessionImpl parent;
61
62                 public KeepAliveTimer(final PCEPSessionImpl parent) {
63                         this.parent = parent;
64                 }
65
66                 @Override
67                 public void run() {
68                         this.parent.handleKeepaliveTimer();
69                 }
70         }
71
72         /**
73          * DeadTimer is to be scheduled periodically, when it expires, it closes PCEP session.
74          */
75         private class DeadTimer extends TimerTask {
76                 private final PCEPSessionImpl parent;
77
78                 public DeadTimer(final PCEPSessionImpl parent) {
79                         this.parent = parent;
80                 }
81
82                 @Override
83                 public void run() {
84                         this.parent.handleDeadtimer();
85                 }
86         }
87
88         /**
89          * OpenWaitTimer runs just once, but can be rescheduled or canceled before expiration. When it expires, it sends an
90          * error message (1, 2)
91          */
92         private class OpenWaitTimer extends TimerTask {
93
94                 private final PCEPSessionImpl parent;
95
96                 public OpenWaitTimer(final PCEPSessionImpl parent) {
97                         this.parent = parent;
98                 }
99
100                 @Override
101                 public void run() {
102                         this.parent.handleOpenWait();
103                 }
104         }
105
106         /**
107          * KeepWaitTimer runs just once, but can be rescheduled or canceled before expiration. When it expires, it sends an
108          * error message (1, 7)
109          */
110         private class KeepWaitTimer extends TimerTask {
111
112                 private final PCEPSessionImpl parent;
113
114                 public KeepWaitTimer(final PCEPSessionImpl parent) {
115                         this.parent = parent;
116                 }
117
118                 @Override
119                 public void run() {
120                         this.parent.handleKeepWait();
121                 }
122         }
123
124         /**
125          * Possible states for Finite State Machine
126          */
127         private enum State {
128                 IDLE, OPEN_WAIT, KEEP_WAIT, UP
129         }
130
131         /**
132          * In seconds.
133          */
134         public static final int OPEN_WAIT_TIMER_VALUE = 60;
135
136         public static final int KEEP_WAIT_TIMER_VALUE = 60;
137
138         public int KEEP_ALIVE_TIMER_VALUE = 3;
139
140         public int DEAD_TIMER_VALUE = 4 * this.KEEP_ALIVE_TIMER_VALUE;
141
142         /**
143          * Actual state of the FSM.
144          */
145         private State state;
146
147         private OpenWaitTimer openWaitTimer;
148
149         private KeepWaitTimer keepWaitTimer;
150
151         /**
152          * System.nanoTime value about when was sent the last message Protected to be updated also in tests.
153          */
154         protected long lastMessageSentAt;
155
156         /**
157          * System.nanoTime value about when was received the last message
158          */
159         private long lastMessageReceivedAt;
160
161         private boolean localOK = false;
162
163         private boolean remoteOK = false;
164
165         private boolean openRetry = false;
166
167         private final int sessionId;
168
169         /**
170          * Protected for testing.
171          */
172         protected int maxUnknownMessages = 5;
173
174         protected final Queue<Long> unknownMessagesTimes = new LinkedList<Long>();
175
176         private final PCEPSessionListener listener;
177
178         private PCEPSessionProposalChecker checker = null;
179
180         /**
181          * Open Object with session characteristics that were accepted by another PCE (sent from this session).
182          */
183         private PCEPOpenObject localOpen = null;
184
185         /**
186          * Open Object with session characteristics for this session (sent from another PCE).
187          */
188         private PCEPOpenObject remoteOpen = null;
189
190         private static final Logger logger = LoggerFactory.getLogger(PCEPSessionImpl.class);
191
192         /**
193          * Timer object grouping FSM Timers
194          */
195         private final Timer stateTimer;
196
197         private final SessionParent parent;
198
199         private final PCEPMessageFactory factory;
200
201         private int sentMsgCount = 0;
202
203         private int receivedMsgCount = 0;
204
205         private final String peerAddress;
206
207         private final ChannelHandlerContext ctx;
208
209         PCEPSessionImpl(final SessionParent parent, final Timer timer, final PCEPConnection connection, final PCEPMessageFactory factory,
210                         final int maxUnknownMessages, final int sessionId, final ChannelHandlerContext ctx) {
211                 this.state = State.IDLE;
212                 this.listener = connection.getListener();
213                 this.checker = connection.getProposalChecker();
214                 this.sessionId = sessionId;
215                 this.localOpen = connection.getProposal().getOpenObject();
216                 this.peerAddress = connection.getPeerAddress().getHostString();
217                 this.stateTimer = timer;
218                 this.parent = parent;
219                 this.factory = factory;
220                 this.ctx = ctx;
221                 if (this.maxUnknownMessages != 0)
222                         this.maxUnknownMessages = maxUnknownMessages;
223         }
224
225         @Override
226         public void startSession() {
227                 logger.debug("Session started.");
228                 this.sendMessage(new PCEPOpenMessage(this.localOpen));
229                 this.restartOpenWait();
230                 this.changeState(State.OPEN_WAIT);
231         }
232
233         /**
234          * OpenWait timer can be canceled or rescheduled before its expiration. When it expires, it sends particular
235          * PCEPErrorMessage and closes PCEP session.
236          */
237         private synchronized void handleOpenWait() {
238                 if (this.state != State.IDLE) {
239                         this.terminate(PCEPErrors.NO_OPEN_BEFORE_EXP_OPENWAIT); // 1, 1
240                 }
241         }
242
243         /**
244          * KeepWait timer can be canceled or rescheduled before its expiration. When it expires, it sends particular
245          * PCEPErrorMessage and closes PCEP session.
246          */
247         private synchronized void handleKeepWait() {
248                 if (this.state != State.IDLE) {
249                         this.terminate(PCEPErrors.NO_MSG_BEFORE_EXP_KEEPWAIT); // 1, 7
250                 }
251         }
252
253         /**
254          * If DeadTimer expires, the session ends. If a message (whichever) was received during this period, the DeadTimer
255          * will be rescheduled by DEAD_TIMER_VALUE + the time that has passed from the start of the DeadTimer to the time at
256          * which the message was received. If the session was closed by the time this method starts to execute (the session
257          * state will become IDLE), that rescheduling won't occur.
258          */
259         private synchronized void handleDeadtimer() {
260                 final long ct = System.nanoTime();
261
262                 final long nextDead = (long) (this.lastMessageReceivedAt + (this.DEAD_TIMER_VALUE * 1E9));
263
264                 if (this.state != State.IDLE) {
265                         if (ct >= nextDead) {
266                                 logger.debug("DeadTimer expired. " + new Date());
267                                 this.terminate(Reason.EXP_DEADTIMER);
268                                 return;
269                         }
270
271                         this.stateTimer.schedule(new DeadTimer(this), (long) ((nextDead - ct) / 1E6));
272                 }
273         }
274
275         /**
276          * If KeepAlive Timer expires, sends KeepAlive message. If a message (whichever) was send during this period, the
277          * KeepAlive Timer will be rescheduled by KEEP_ALIVE_TIMER_VALUE + the time that has passed from the start of the
278          * KeepAlive timer to the time at which the message was sent. If the session was closed by the time this method
279          * starts to execute (the session state will become IDLE), that rescheduling won't occur.
280          */
281         private synchronized void handleKeepaliveTimer() {
282                 final long ct = System.nanoTime();
283
284                 long nextKeepalive = (long) (this.lastMessageSentAt + (this.KEEP_ALIVE_TIMER_VALUE * 1E9));
285
286                 if (this.state != State.IDLE) {
287                         if (ct >= nextKeepalive) {
288                                 this.sendMessage(new PCEPKeepAliveMessage());
289                                 nextKeepalive = (long) (this.lastMessageSentAt + (this.KEEP_ALIVE_TIMER_VALUE * 1E9));
290                         }
291
292                         this.stateTimer.schedule(new KeepAliveTimer(this), (long) ((nextKeepalive - ct) / 1E6));
293                 }
294         }
295
296         private void changeState(final State finalState) {
297                 switch (finalState) {
298                 case IDLE:
299                         logger.debug("Changed to state: " + State.IDLE);
300                         this.state = State.IDLE;
301                         return;
302                 case OPEN_WAIT:
303                         logger.debug("Changed to state: " + State.OPEN_WAIT);
304                         if (this.state == State.UP) {
305                                 throw new IllegalArgumentException("Cannot change state from " + this.state + " to " + State.OPEN_WAIT);
306                         }
307                         this.state = State.OPEN_WAIT;
308                         return;
309                 case KEEP_WAIT:
310                         logger.debug("Changed to state: " + State.KEEP_WAIT);
311                         if (this.state == State.UP || this.state == State.IDLE) {
312                                 throw new IllegalArgumentException("Cannot change state from " + this.state + " to " + State.KEEP_WAIT);
313                         }
314                         this.state = State.KEEP_WAIT;
315                         return;
316                 case UP:
317                         logger.debug("Changed to state: " + State.UP);
318                         if (this.state == State.IDLE || this.state == State.UP) {
319                                 throw new IllegalArgumentException("Cannot change state from " + this.state + " to " + State.UP);
320                         }
321                         this.state = State.UP;
322                         return;
323                 }
324         }
325
326         private void restartOpenWait() {
327                 if (this.state == State.OPEN_WAIT && this.openWaitTimer != null) {
328                         this.openWaitTimer.cancel();
329                 }
330                 this.openWaitTimer = new OpenWaitTimer(this);
331                 this.stateTimer.schedule(this.openWaitTimer, OPEN_WAIT_TIMER_VALUE * 1000);
332         }
333
334         private void restartKeepWaitTimer() {
335                 if (this.state == State.KEEP_WAIT && this.keepWaitTimer != null) {
336                         this.keepWaitTimer.cancel();
337                 }
338                 this.keepWaitTimer = new KeepWaitTimer(this);
339                 this.stateTimer.schedule(this.keepWaitTimer, KEEP_WAIT_TIMER_VALUE * 1000);
340         }
341
342         /**
343          * Makes a callback to check if the session characteristics that FSM received, are acceptable.
344          * 
345          * @param keepAliveTimerValue
346          * @param deadTimerValue
347          * @param tlvs
348          * @return
349          */
350         private boolean checkSessionCharacteristics(final PCEPOpenObject openObj) {
351                 return this.checker.checkSessionCharacteristics(new PCEPSessionPreferences(openObj));
352         }
353
354         private PCEPOpenObject getNewProposal() {
355                 return this.checker.getNewProposal(new PCEPSessionPreferences(this.localOpen)).getOpenObject();
356         }
357
358         /**
359          * Sends message to serialization.
360          * 
361          * @param msg to be sent
362          */
363         @Override
364         public void sendMessage(final PCEPMessage msg) {
365                 try {
366                         this.ctx.writeAndFlush(msg);
367                         this.lastMessageSentAt = System.nanoTime();
368                         if (!(msg instanceof PCEPKeepAliveMessage))
369                                 logger.debug("Sent message: " + msg);
370                         this.sentMsgCount++;
371                 } catch (final Exception e) {
372                         logger.warn("Message {} was not sent.", msg, e);
373                 }
374         }
375
376         private void commonClose() {
377                 this.changeState(State.IDLE);
378                 this.parent.onSessionClosed(this);
379         }
380
381         /**
382          * Closes PCEP session from the parent with given reason. A message needs to be sent, but parent doesn't have to be
383          * modified, because he initiated the closing. (To prevent concurrent modification exception).
384          * 
385          * @param closeObject
386          */
387         void closeWithoutMessage() {
388                 logger.debug("Closing session: {}", this);
389                 commonClose();
390         }
391
392         /**
393          * Closes PCEP session, cancels all timers, returns to state Idle WITHOUT sending the Close Message. KeepAlive and
394          * DeadTimer are cancelled if the state of the session changes to IDLE. This method is used to close the PCEP
395          * session from inside the session or from the listener, therefore the parent of this session should be informed.
396          * The only closing reason is UNKNOWN.
397          */
398         @Override
399         public synchronized void close() {
400                 logger.debug("Closing session: {}", this);
401                 this.sendMessage(new PCEPCloseMessage(new PCEPCloseObject(Reason.UNKNOWN)));
402                 commonClose();
403         }
404
405         private void terminate(final PCEPCloseObject.Reason reason) {
406                 this.sendMessage(new PCEPCloseMessage(new PCEPCloseObject(reason)));
407                 this.closeWithoutMessage();
408                 this.listener.onSessionTerminated(this, new PCEPCloseTermination(reason));
409         }
410
411         private void terminate(final PCEPErrors error) {
412                 this.sendErrorMessage(error);
413                 this.closeWithoutMessage();
414                 this.listener.onSessionTerminated(this, new PCEPErrorTermination(error));
415         }
416
417         @Override
418         public void endOfInput() {
419                 if (this.state != State.IDLE) {
420                         this.listener.onSessionDown(this, null, new IOException("End of input detected. Close the session."));
421                 }
422         }
423
424         @Override
425         public int maximumMessageSize() {
426                 return 65535;
427         }
428
429         private void sendErrorMessage(final PCEPErrors value) {
430                 this.sendErrorMessage(value, null);
431         }
432
433         /**
434          * Sends PCEP Error Message with one PCEPError and Open Object.
435          * 
436          * @param value
437          * @param open
438          */
439         private void sendErrorMessage(final PCEPErrors value, final PCEPOpenObject open) {
440                 final PCEPErrorObject error = new PCEPErrorObject(value);
441                 final List<PCEPErrorObject> errors = new ArrayList<PCEPErrorObject>();
442                 errors.add(error);
443                 this.sendMessage(new PCEPErrorMessage(open, errors, null));
444         }
445
446         @Override
447         public void handleMalformedMessage(final DeserializerException e) {
448                 // FIXME rewrite
449
450         }
451
452         @Override
453         public void handleMalformedMessage(final DocumentedException e) {
454                 // FIXME rewrite
455
456         }
457
458         /**
459          * The fact, that a message is malformed, comes from parser. In case of unrecognized message a particular error is
460          * sent (CAPABILITY_NOT_SUPPORTED) and the method checks if the MAX_UNKNOWN_MSG per minute wasn't overstepped.
461          * Second, any other error occurred that is specified by rfc. In this case, the an error message is generated and
462          * sent.
463          * 
464          * @param error documented error in RFC5440 or draft
465          */
466         public void handleMalformedMessage(final PCEPErrors error) {
467                 final long ct = System.nanoTime();
468                 this.sendErrorMessage(error);
469                 if (error == PCEPErrors.CAPABILITY_NOT_SUPPORTED) {
470                         this.unknownMessagesTimes.add(ct);
471                         while (ct - this.unknownMessagesTimes.peek() > 60 * 1E9) {
472                                 this.unknownMessagesTimes.poll();
473                         }
474                         if (this.unknownMessagesTimes.size() > this.maxUnknownMessages) {
475                                 this.terminate(Reason.TOO_MANY_UNKNOWN_MSG);
476                         }
477                 }
478         }
479
480         /**
481          * In case of syntactic error or some parsing error, the session needs to be closed with the Reason: malformed
482          * message. The user needs to be notified about this error.
483          * 
484          * @param e exception that was thrown from parser
485          */
486         public void handleMalformedMessage(final Exception e) {
487                 logger.warn("PCEP byte stream corruption detected", e);
488                 this.terminate(Reason.MALFORMED_MSG);
489         }
490
491         /**
492          * Open message should be handled only if the FSM is in OPEN_WAIT state.
493          * 
494          * @param msg
495          */
496         private void handleOpenMessage(final PCEPOpenMessage msg) {
497                 this.remoteOpen = msg.getOpenObject();
498                 logger.debug("Received message: " + msg.toString());
499                 if (this.state != State.OPEN_WAIT) {
500                         this.sendErrorMessage(PCEPErrors.ATTEMPT_2ND_SESSION);
501                         return;
502                 }
503                 final Boolean result = this.checkSessionCharacteristics(this.remoteOpen);
504                 if (result == null) {
505                         this.terminate(PCEPErrors.NON_ACC_NON_NEG_SESSION_CHAR); // 1, 3
506                         return;
507                 } else if (result) {
508                         this.DEAD_TIMER_VALUE = this.remoteOpen.getDeadTimerValue();
509                         this.KEEP_ALIVE_TIMER_VALUE = this.remoteOpen.getKeepAliveTimerValue();
510                         logger.debug("Session chars are acceptable. Overwriting: deadtimer: " + this.DEAD_TIMER_VALUE + "keepalive: "
511                                         + this.KEEP_ALIVE_TIMER_VALUE);
512                         this.remoteOK = true;
513                         this.openWaitTimer.cancel();
514                         this.sendMessage(new PCEPKeepAliveMessage());
515                         // if the timer is not disabled
516                         if (this.KEEP_ALIVE_TIMER_VALUE != 0) {
517                                 this.stateTimer.schedule(new KeepAliveTimer(this), this.KEEP_ALIVE_TIMER_VALUE * 1000);
518                         }
519                         if (this.localOK) {
520                                 // if the timer is not disabled
521                                 if (this.DEAD_TIMER_VALUE != 0) {
522                                         this.stateTimer.schedule(new DeadTimer(this), this.DEAD_TIMER_VALUE * 1000);
523                                 }
524                                 this.changeState(State.UP);
525                                 this.listener.onSessionUp(this, this.localOpen, this.remoteOpen);
526                         } else {
527                                 this.restartKeepWaitTimer();
528                                 this.changeState(State.KEEP_WAIT);
529                         }
530                         return;
531                 } else if (!result) {
532                         this.localOpen = this.getNewProposal();
533                         if (this.openRetry) {
534                                 this.terminate(PCEPErrors.SECOND_OPEN_MSG); // 1, 5
535                         } else {
536                                 this.openRetry = true;
537                                 this.sendErrorMessage(PCEPErrors.NON_ACC_NEG_SESSION_CHAR, this.localOpen); // 1, 4
538                                 if (this.localOK) {
539                                         this.restartOpenWait();
540                                         this.changeState(State.OPEN_WAIT);
541                                 } else {
542                                         this.restartKeepWaitTimer();
543                                         this.changeState(State.KEEP_WAIT);
544                                 }
545                         }
546                 }
547         }
548
549         /**
550          * Error message should be handled in FSM if its state is KEEP_WAIT, otherwise it is just passed to session listener
551          * for handling.
552          * 
553          * @param msg
554          */
555         private void handleErrorMessage(final PCEPErrorMessage msg) {
556                 this.remoteOpen = msg.getOpenObject();
557                 final Boolean result = this.checkSessionCharacteristics(this.remoteOpen);
558                 if (result == null || !result) {
559                         this.terminate(PCEPErrors.PCERR_NON_ACC_SESSION_CHAR); // 1, 6
560                         return;
561                 } else {
562                         this.KEEP_ALIVE_TIMER_VALUE = this.remoteOpen.getKeepAliveTimerValue();
563                         this.DEAD_TIMER_VALUE = this.remoteOpen.getDeadTimerValue();
564                         logger.debug("New values for keepalive: " + this.remoteOpen.getKeepAliveTimerValue() + " deadtimer "
565                                         + this.remoteOpen.getDeadTimerValue());
566                         this.sendMessage(new PCEPOpenMessage(this.remoteOpen));
567                         if (this.remoteOK) {
568                                 this.restartKeepWaitTimer();
569                                 this.changeState(State.KEEP_WAIT);
570                         } else {
571                                 this.keepWaitTimer.cancel();
572                                 this.restartOpenWait();
573                                 this.changeState(State.OPEN_WAIT);
574                         }
575                 }
576         }
577
578         /**
579          * KeepAlive message should be explicitly parsed in FSM when its state is KEEP_WAIT. Otherwise is handled by the
580          * KeepAliveTimer or it's invalid.
581          */
582         private void handleKeepAliveMessage() {
583                 if (this.state == State.KEEP_WAIT) {
584                         this.localOK = true;
585                         this.keepWaitTimer.cancel();
586                         if (this.remoteOK) {
587                                 if (this.DEAD_TIMER_VALUE != 0) {
588                                         this.stateTimer.schedule(new DeadTimer(this), this.DEAD_TIMER_VALUE * 1000);
589                                 }
590                                 this.changeState(State.UP);
591                                 this.listener.onSessionUp(this, this.localOpen, this.remoteOpen);
592                         } else {
593                                 this.restartOpenWait();
594                                 this.changeState(State.OPEN_WAIT);
595                         }
596                 }
597         }
598
599         /**
600          * Handles incoming message. If the session is up, it notifies the user. The user is notified about every message
601          * except KeepAlive.
602          * 
603          * @param msg incoming message
604          */
605         @Override
606         public void handleMessage(final ProtocolMessage msg) {
607                 // Update last reception time
608                 final PCEPMessage pcepMsg = (PCEPMessage) msg;
609
610                 this.lastMessageReceivedAt = System.nanoTime();
611                 this.receivedMsgCount++;
612
613                 if (pcepMsg instanceof PCEPRawMessage) {
614                         List<PCEPMessage> msgs;
615                         try {
616                                 msgs = PCEPMessageValidator.getValidator(((PCEPRawMessage) pcepMsg).getMsgType()).validate(
617                                                 ((PCEPRawMessage) pcepMsg).getAllObjects());
618                                 for (final PCEPMessage tmpMsg : msgs) {
619                                         this.handleMessage(tmpMsg);
620                                 }
621                         } catch (final PCEPDeserializerException e) {
622                                 logger.error("Malformed message, terminating. ", e);
623                                 this.terminate(Reason.MALFORMED_MSG);
624                         }
625                         return;
626                 }
627
628                 // Keepalives are handled internally
629                 if (pcepMsg instanceof PCEPKeepAliveMessage) {
630                         this.handleKeepAliveMessage();
631                         return;
632                 }
633
634                 // Open messages are handled internally
635                 if (pcepMsg instanceof PCEPOpenMessage) {
636                         this.handleOpenMessage((PCEPOpenMessage) pcepMsg);
637                         return;
638                 }
639
640                 /*
641                  * During initial handshake we handle all the messages.
642                  */
643                 if (this.state != State.UP) {
644                         /*
645                          * In KEEP_WAIT, an Error message is a valid thing to see, because
646                          * it is used in negotiation.
647                          */
648                         if (pcepMsg instanceof PCEPErrorMessage && this.state == State.KEEP_WAIT
649                                         && ((PCEPErrorMessage) pcepMsg).getOpenObject() != null) {
650                                 this.handleErrorMessage((PCEPErrorMessage) pcepMsg);
651                                 return;
652                         }
653
654                         /*
655                          * OPEN and KEEPALIVE messages are handled at the top. ERROR
656                          * messages are handled in the specific case of KEEP_WAIT above, so
657                          * anything else is invalid here.
658                          */
659                         this.terminate(PCEPErrors.NON_OR_INVALID_OPEN_MSG);
660                         return;
661                 }
662
663                 /*
664                  * Session is up, we are reporting all messages to user. One notable
665                  * exception is CLOSE message, which needs to be converted into a
666                  * session DOWN event.
667                  */
668                 if (pcepMsg instanceof PCEPCloseMessage) {
669                         this.listener.onSessionTerminated(this, new PCEPCloseTermination(((PCEPCloseMessage) pcepMsg).getCloseObject().getReason()));
670                         this.closeWithoutMessage();
671                         return;
672                 }
673                 this.listener.onMessage(this, pcepMsg);
674         }
675
676         @Override
677         public ProtocolMessageFactory getMessageFactory() {
678                 return this.factory;
679         }
680
681         @Override
682         public void onConnectionFailed(final IOException e) {
683                 logger.info("Connection failed before finishing: {}", e.getMessage(), e);
684                 this.listener.onSessionDown(this, new PCEPCloseObject(Reason.UNKNOWN), e);
685         }
686
687         /**
688          * @return the sentMsgCount
689          */
690
691         @Override
692         public Integer getSentMsgCount() {
693                 return this.sentMsgCount;
694         }
695
696         /**
697          * @return the receivedMsgCount
698          */
699
700         @Override
701         public Integer getReceivedMsgCount() {
702                 return this.receivedMsgCount;
703         }
704
705         @Override
706         public Integer getDeadTimerValue() {
707                 return this.DEAD_TIMER_VALUE;
708         }
709
710         @Override
711         public Integer getKeepAliveTimerValue() {
712                 return this.KEEP_ALIVE_TIMER_VALUE;
713         }
714
715         @Override
716         public String getPeerAddress() {
717                 return this.peerAddress;
718         }
719
720         @Override
721         public void tearDown() throws IOException {
722                 this.close();
723         }
724
725         @Override
726         public String getNodeIdentifier() {
727                 if (!this.remoteOpen.getTlvs().isEmpty()) {
728                         final PCEPTlv tlv = this.remoteOpen.getTlvs().iterator().next();
729                         if (tlv != null && tlv instanceof NodeIdentifierTlv) {
730                                 return tlv.toString();
731                         }
732                 }
733                 return "";
734         }
735
736         @Override
737         public String toString() {
738                 final StringBuilder builder = new StringBuilder();
739                 builder.append("PCEPSessionImpl [state=");
740                 builder.append(this.state);
741                 builder.append(", localOK=");
742                 builder.append(this.localOK);
743                 builder.append(", remoteOK=");
744                 builder.append(this.remoteOK);
745                 builder.append(", openRetry=");
746                 builder.append(this.openRetry);
747                 builder.append(", sessionId=");
748                 builder.append(this.sessionId);
749                 builder.append(", checker=");
750                 builder.append(this.checker);
751                 builder.append(", localOpen=");
752                 builder.append(this.localOpen);
753                 builder.append(", remoteOpen=");
754                 builder.append(this.remoteOpen);
755                 builder.append("]");
756                 return builder.toString();
757         }
758 }