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