/**
   * Creates a new default SmppServer.
   *
   * @param configuration The server configuration to create this server with
   * @param serverHandler The handler implementation for handling bind requests and
   *     creating/destroying sessions.
   * @param monitorExecutor The scheduled executor that all sessions will share to monitor
   *     themselves and expire requests. If null monitoring will be disabled.
   * @param bossGroup Specify the EventLoopGroup to accept new connections and handle accepted
   *     connections. The {@link EventLoopGroup} is used to handle all the events and IO for {@link
   *     SocketChannel}.
   * @param workerGroup The {@link EventLoopGroup} is used to handle all the events and IO for
   *     {@link Channel}.
   */
  public DefaultSmppServer(
      final SmppServerConfiguration configuration,
      SmppServerHandler serverHandler,
      ScheduledExecutorService monitorExecutor,
      EventLoopGroup bossGroup,
      EventLoopGroup workerGroup) {
    this.configuration = configuration;
    // the same group we'll put every server channel
    this.channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    this.serverHandler = serverHandler;

    // tie the server bootstrap to this server socket channel factory
    this.serverBootstrap = new ServerBootstrap();

    // a factory for creating channels (connections)
    if (configuration.isNonBlockingSocketsEnabled()) {
      this.serverBootstrap.channel(NioServerSocketChannel.class);
    } else {
      this.serverBootstrap.channel(OioServerSocketChannel.class);
    }

    this.bossGroup = bossGroup;
    this.workerGroup = workerGroup;
    this.serverBootstrap.group(this.bossGroup, this.workerGroup);

    // set options for the server socket that are useful
    this.serverBootstrap.option(ChannelOption.SO_REUSEADDR, configuration.isReuseAddress());

    // we use the same default pipeline for all new channels - no need for a factory
    this.serverConnector = new SmppServerConnector(channels, this);

    this.serverBootstrap.childHandler(
        new ChannelInitializer<SocketChannel>() {
          @Override
          public void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline()
                .addLast(SmppChannelConstants.PIPELINE_SERVER_CONNECTOR_NAME, serverConnector);
          }
        });

    // a shared timer used to make sure new channels are bound within X milliseconds
    this.bindTimer = new Timer(configuration.getName() + "-BindTimer0", true);
    // NOTE: this would permit us to customize the "transcoding" context for a server if needed
    this.transcoder = new DefaultPduTranscoder(new DefaultPduTranscoderContext());
    this.sessionIdSequence = new AtomicLong(0);
    this.monitorExecutor = monitorExecutor;
    this.counters = new DefaultSmppServerCounters();
    if (configuration.isJmxEnabled()) {
      registerMBean();
    }
  }
  protected void destroySession(Long sessionId, DefaultSmppSession session) {
    // session destroyed, now pass it upstream
    counters.incrementSessionDestroyedAndGet();
    decrementSessionSizeCounters(session);
    serverHandler.sessionDestroyed(sessionId, session);

    // unregister this session as an mbean
    if (configuration.isJmxEnabled()) {
      session.unregisterMBean(
          configuration.getJmxDomain()
              + ":type="
              + configuration.getName()
              + "Sessions,name="
              + sessionId);
    }
  }
 private void registerMBean() {
   if (configuration == null) {
     return;
   }
   if (configuration.isJmxEnabled()) {
     // register the this queue manager as an mbean
     try {
       ObjectName name =
           new ObjectName(configuration.getJmxDomain() + ":name=" + configuration.getName());
       ManagementFactory.getPlatformMBeanServer().registerMBean(this, name);
     } catch (Exception e) {
       // log the error, but don't throw an exception for this datasource
       logger.error("Unable to register DefaultSmppServerMXBean [{}]", configuration.getName(), e);
     }
   }
 }
  protected void createSession(
      Long sessionId,
      Channel channel,
      SmppSessionConfiguration config,
      BaseBindResp preparedBindResponse)
      throws SmppProcessingException {
    // NOTE: exactly one PDU (bind request) was read from the channel, we
    // now need to delegate permitting this bind request by calling a method
    // further upstream.  Only after the server-side is completely ready to
    // start processing requests from this session, do we want to actually
    // return the bind response and start reading further requests -- we'll
    // initially block reading from the channel first -- this will be turned
    // back on via the "serverReady()" method call on the session object

    // make sure the channel is not being read/processed (until we flag we're ready later on)
    channel.config().setAutoRead(false);

    // auto negotiate the interface version in use based on the requested interface version
    byte interfaceVersion = this.autoNegotiateInterfaceVersion(config.getInterfaceVersion());

    // create a new server session associated with this server
    DefaultSmppSession session =
        new DefaultSmppSession(
            SmppSession.Type.SERVER,
            config,
            channel,
            this,
            sessionId,
            preparedBindResponse,
            interfaceVersion,
            monitorExecutor);

    // replace name of thread used for renaming
    SmppSessionThreadRenamer threadRenamer =
        (SmppSessionThreadRenamer)
            channel.pipeline().get(SmppChannelConstants.PIPELINE_SESSION_THREAD_RENAMER_NAME);
    threadRenamer.setThreadName(config.getName());

    // add a logging handler after the thread renamer
    SmppSessionLogger loggingHandler =
        new SmppSessionLogger(
            DefaultSmppSession.class.getCanonicalName(), config.getLoggingOptions());
    channel
        .pipeline()
        .addAfter(
            SmppChannelConstants.PIPELINE_SESSION_THREAD_RENAMER_NAME,
            SmppChannelConstants.PIPELINE_SESSION_LOGGER_NAME,
            loggingHandler);

    // decoder in pipeline is ok (keep it)

    // create a new wrapper around a session to pass the pdu up the chain
    channel.pipeline().remove(SmppChannelConstants.PIPELINE_SESSION_WRAPPER_NAME);
    channel
        .pipeline()
        .addLast(
            SmppChannelConstants.PIPELINE_SESSION_WRAPPER_NAME, new SmppSessionWrapper(session));

    // check if the # of channels exceeds maxConnections
    if (this.channels.size() > this.configuration.getMaxConnectionSize()) {
      logger.warn(
          "The current connection size [{}] exceeds the configured max connection size [{}]",
          this.channels.size(),
          this.configuration.getMaxConnectionSize());
    }

    // session created, now pass it upstream
    counters.incrementSessionCreatedAndGet();
    incrementSessionSizeCounters(session);
    this.serverHandler.sessionCreated(sessionId, session, preparedBindResponse);

    // register this session as an mbean
    if (configuration.isJmxEnabled()) {
      session.registerMBean(
          configuration.getJmxDomain()
              + ":type="
              + configuration.getName()
              + "Sessions,name="
              + sessionId);
    }
  }