/** {@inheritDoc} */
  public void doShutdown() {
    if (protocolAcceptor != null) {
      try {
        protocolAcceptor.close();
      } catch (IOException ignore) {
      }
    }
    for (ClientSessionHandler handler : handlers.values()) {
      handler.shutdown();
    }
    handlers.clear();

    if (exporter != null) {
      try {
        exporter.unexport();
        logger.log(Level.FINEST, "client session server unexported");
      } catch (RuntimeException e) {
        logger.logThrow(Level.FINEST, e, "unexport server throws");
        // swallow exception
      }
    }

    synchronized (flushContextsLock) {
      flushContextsLock.notifyAll();
    }
  }
  /** {@inheritDoc} */
  public void doReady() throws Exception {
    channelService = txnProxy.getService(ChannelServiceImpl.class);
    try {
      protocolAcceptor.accept(protocolListener);
    } catch (IOException e) {
      if (logger.isLoggable(Level.CONFIG)) {
        logger.logThrow(Level.CONFIG, e, "Failed to start accepting connections");
      }
      throw e;
    }

    transactionScheduler.runTask(
        new AbstractKernelRunnable("AddProtocolDescriptorMapping") {
          public void run() {
            getProtocolDescriptorsMap()
                .put(localNodeId, Collections.singleton(protocolAcceptor.getDescriptor()));
          }
        },
        taskOwner);
  }
  /**
   * Constructs an instance of this class with the specified properties.
   *
   * @param properties service properties
   * @param systemRegistry system registry
   * @param txnProxy transaction proxy
   * @throws Exception if a problem occurs when creating the service
   */
  public ClientSessionServiceImpl(
      Properties properties, ComponentRegistry systemRegistry, TransactionProxy txnProxy)
      throws Exception {
    super(properties, systemRegistry, txnProxy, logger);
    logger.log(Level.CONFIG, "Creating ClientSessionServiceImpl properties:{0}", properties);
    PropertiesWrapper wrappedProps = new PropertiesWrapper(properties);
    try {
      /*
       * Get the property for controlling session event processing
       * and connection disconnection.
       */
      writeBufferSize =
          wrappedProps.getIntProperty(
              WRITE_BUFFER_SIZE_PROPERTY, DEFAULT_WRITE_BUFFER_SIZE, 8192, Integer.MAX_VALUE);
      eventsPerTxn =
          wrappedProps.getIntProperty(
              EVENTS_PER_TXN_PROPERTY, DEFAULT_EVENTS_PER_TXN, 1, Integer.MAX_VALUE);
      allowNewLogin = wrappedProps.getBooleanProperty(ALLOW_NEW_LOGIN_PROPERTY, false);

      /* Export the ClientSessionServer. */
      int serverPort =
          wrappedProps.getIntProperty(SERVER_PORT_PROPERTY, DEFAULT_SERVER_PORT, 0, 65535);
      serverImpl = new SessionServerImpl();
      exporter = new Exporter<ClientSessionServer>(ClientSessionServer.class);
      try {
        int port = exporter.export(serverImpl, serverPort);
        serverProxy = exporter.getProxy();
        if (logger.isLoggable(Level.CONFIG)) {
          logger.log(Level.CONFIG, "export successful. port:{0,number,#}", port);
        }
      } catch (Exception e) {
        try {
          exporter.unexport();
        } catch (RuntimeException re) {
        }
        throw e;
      }

      /* Get services and check service version. */
      flushContextsThread.start();
      contextFactory = new ContextFactory(txnProxy);
      watchdogService = txnProxy.getService(WatchdogService.class);
      nodeMapService = txnProxy.getService(NodeMappingService.class);
      taskService = txnProxy.getService(TaskService.class);
      localNodeId = watchdogService.getLocalNodeId();
      watchdogService.addRecoveryListener(new ClientSessionServiceRecoveryListener());

      transactionScheduler.runTask(
          new AbstractKernelRunnable("CheckServiceVersion") {
            public void run() {
              checkServiceVersion(VERSION_KEY, MAJOR_VERSION, MINOR_VERSION);
            }
          },
          taskOwner);

      /* Store the ClientSessionServer proxy in the data store. */
      transactionScheduler.runTask(
          new AbstractKernelRunnable("StoreClientSessionServiceProxy") {
            public void run() {
              dataService.setServiceBinding(
                  getClientSessionServerKey(localNodeId),
                  new ManagedSerializable<ClientSessionServer>(serverProxy));
            }
          },
          taskOwner);

      /*
       * Create the protocol listener and acceptor.
       */
      protocolListener = new ProtocolListenerImpl();

      protocolAcceptor =
          wrappedProps.getClassInstanceProperty(
              PROTOCOL_ACCEPTOR_PROPERTY,
              DEFAULT_PROTOCOL_ACCEPTOR,
              ProtocolAcceptor.class,
              new Class[] {Properties.class, ComponentRegistry.class, TransactionProxy.class},
              properties,
              systemRegistry,
              txnProxy);

      assert protocolAcceptor != null;

      /* Create our service profiling info and register our MBean */
      ProfileCollector collector = systemRegistry.getComponent(ProfileCollector.class);
      serviceStats = new ClientSessionServiceStats(collector);
      try {
        collector.registerMBean(serviceStats, ClientSessionServiceStats.MXBEAN_NAME);
      } catch (JMException e) {
        logger.logThrow(Level.CONFIG, e, "Could not register MBean");
      }

      /* Set the protocol descriptor in the ConfigMXBean. */
      ConfigManager config =
          (ConfigManager) collector.getRegisteredMBean(ConfigManager.MXBEAN_NAME);
      if (config == null) {
        logger.log(Level.CONFIG, "Could not find ConfigMXBean");
      } else {
        config.setProtocolDescriptor(protocolAcceptor.getDescriptor().toString());
      }

    } catch (Exception e) {
      if (logger.isLoggable(Level.CONFIG)) {
        logger.logThrow(Level.CONFIG, e, "Failed to create ClientSessionServiceImpl");
      }
      doShutdown();
      throw e;
    }
  }