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