@Override
  public Session toManagementSession() {
    SessionBuilder builder = new SessionBuilder();

    builder.setSessionId(getSessionId());
    IpAddress address;
    InetAddress address1 = InetAddresses.forString(header.getAddress());
    if (address1 instanceof Inet4Address) {
      address = new IpAddress(new Ipv4Address(header.getAddress()));
    } else {
      address = new IpAddress(new Ipv6Address(header.getAddress()));
    }
    builder.setSourceHost(new Host(address));

    Preconditions.checkState(DateAndTime.PATTERN_CONSTANTS.size() == 1);
    String formattedDateTime = dateFormatter.format(loginTime);

    Matcher matcher = dateTimePattern.matcher(formattedDateTime);
    Preconditions.checkState(
        matcher.matches(),
        "Formatted datetime %s does not match pattern %s",
        formattedDateTime,
        dateTimePattern);
    builder.setLoginTime(new DateAndTime(formattedDateTime));

    builder.setInBadRpcs(new ZeroBasedCounter32(inRpcFail));
    builder.setInRpcs(new ZeroBasedCounter32(inRpcSuccess));
    builder.setOutRpcErrors(new ZeroBasedCounter32(outRpcError));

    builder.setUsername(header.getUserName());
    builder.setTransport(getTransportForString(header.getTransport()));

    builder.setOutNotifications(new ZeroBasedCounter32(outNotification));

    builder.setKey(new SessionKey(getSessionId()));

    Session1Builder builder1 = new Session1Builder();
    builder1.setSessionIdentifier(header.getSessionIdentifier());
    builder.addAugmentation(Session1.class, builder1.build());

    return builder.build();
  }
public final class NetconfServerSession
    extends AbstractNetconfSession<NetconfServerSession, NetconfServerSessionListener>
    implements NetconfManagementSession {

  private static final Logger LOG = LoggerFactory.getLogger(NetconfServerSession.class);
  private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

  private final NetconfHelloMessageAdditionalHeader header;
  private final NetconfServerSessionListener sessionListener;

  private ZonedDateTime loginTime;
  private long inRpcSuccess, inRpcFail, outRpcError, outNotification;
  private volatile boolean delayedClose;

  public NetconfServerSession(
      final NetconfServerSessionListener sessionListener,
      final Channel channel,
      final long sessionId,
      final NetconfHelloMessageAdditionalHeader header) {
    super(sessionListener, channel, sessionId);
    this.header = header;
    this.sessionListener = sessionListener;
    LOG.debug("Session {} created", toString());
  }

  @Override
  protected void sessionUp() {
    Preconditions.checkState(loginTime == null, "Session is already up");
    this.loginTime = Instant.now().atZone(ZoneId.systemDefault());
    super.sessionUp();
  }

  /**
   * Close this session after next message is sent. Suitable for close rpc that needs to send ok
   * response before the session is closed.
   */
  public void delayedClose() {
    this.delayedClose = true;
  }

  @Override
  public ChannelFuture sendMessage(final NetconfMessage netconfMessage) {
    final ChannelFuture channelFuture = super.sendMessage(netconfMessage);
    if (netconfMessage instanceof NetconfNotification) {
      outNotification++;
      sessionListener.onNotification(this, (NetconfNotification) netconfMessage);
    }
    // delayed close was set, close after the message was sent
    if (delayedClose) {
      channelFuture.addListener(
          new ChannelFutureListener() {
            @Override
            public void operationComplete(final ChannelFuture future) throws Exception {
              close();
            }
          });
    }
    return channelFuture;
  }

  public void onIncommingRpcSuccess() {
    inRpcSuccess++;
  }

  public void onIncommingRpcFail() {
    inRpcFail++;
  }

  public void onOutgoingRpcError() {
    outRpcError++;
  }

  private static final String dateTimePatternString = DateAndTime.PATTERN_CONSTANTS.get(0);
  private static final Pattern dateTimePattern = Pattern.compile(dateTimePatternString);

  @Override
  public Session toManagementSession() {
    SessionBuilder builder = new SessionBuilder();

    builder.setSessionId(getSessionId());
    IpAddress address;
    InetAddress address1 = InetAddresses.forString(header.getAddress());
    if (address1 instanceof Inet4Address) {
      address = new IpAddress(new Ipv4Address(header.getAddress()));
    } else {
      address = new IpAddress(new Ipv6Address(header.getAddress()));
    }
    builder.setSourceHost(new Host(address));

    Preconditions.checkState(DateAndTime.PATTERN_CONSTANTS.size() == 1);
    String formattedDateTime = dateFormatter.format(loginTime);

    Matcher matcher = dateTimePattern.matcher(formattedDateTime);
    Preconditions.checkState(
        matcher.matches(),
        "Formatted datetime %s does not match pattern %s",
        formattedDateTime,
        dateTimePattern);
    builder.setLoginTime(new DateAndTime(formattedDateTime));

    builder.setInBadRpcs(new ZeroBasedCounter32(inRpcFail));
    builder.setInRpcs(new ZeroBasedCounter32(inRpcSuccess));
    builder.setOutRpcErrors(new ZeroBasedCounter32(outRpcError));

    builder.setUsername(header.getUserName());
    builder.setTransport(getTransportForString(header.getTransport()));

    builder.setOutNotifications(new ZeroBasedCounter32(outNotification));

    builder.setKey(new SessionKey(getSessionId()));

    Session1Builder builder1 = new Session1Builder();
    builder1.setSessionIdentifier(header.getSessionIdentifier());
    builder.addAugmentation(Session1.class, builder1.build());

    return builder.build();
  }

  private static Class<? extends Transport> getTransportForString(final String transport) {
    switch (transport) {
      case "ssh":
        return NetconfSsh.class;
      case "tcp":
        return NetconfTcp.class;
      default:
        throw new IllegalArgumentException("Unknown transport type " + transport);
    }
  }

  @Override
  protected NetconfServerSession thisInstance() {
    return this;
  }

  @Override
  protected void addExiHandlers(
      final ByteToMessageDecoder decoder, final MessageToByteEncoder<NetconfMessage> encoder) {
    replaceMessageDecoder(decoder);
    replaceMessageEncoderAfterNextMessage(encoder);
  }

  @Override
  public void stopExiCommunication() {
    replaceMessageDecoder(new NetconfXMLToMessageDecoder());
    replaceMessageEncoderAfterNextMessage(new NetconfMessageToXMLEncoder());
  }
}