@Override
  public void produceData(final IOSession iosession, final ServerState sessionState)
      throws IOException, SMTPProtocolException {
    Args.notNull(iosession, "IO session");
    Args.notNull(sessionState, "Session state");

    SessionOutputBuffer buf = this.iobuffers.getOutbuf();

    synchronized (sessionState) {
      if (this.actionFuture != null) {
        SMTPReply reply = getReply(this.actionFuture);
        this.actionFuture = null;
        this.writer.write(reply, buf);
      }

      if (this.actionFuture == null) {
        while (!this.pendingActions.isEmpty()) {
          Action<ServerState> action = this.pendingActions.remove();
          Future<SMTPReply> future =
              action.execute(sessionState, new OutputTrigger<SMTPReply>(sessionState, iosession));
          if (future.isDone()) {
            SMTPReply reply = getReply(future);
            this.writer.write(reply, buf);
          } else {
            this.actionFuture = future;
            break;
          }
        }
      }

      if (buf.hasData()) {
        buf.flush(iosession.channel());
      }
      if (!buf.hasData()) {
        if (sessionState.getDataType() != null) {
          this.completed = true;
        }
        if (sessionState.isTerminated()) {
          iosession.close();
        } else {
          iosession.clearEvent(SelectionKey.OP_WRITE);
        }
      }
    }
  }
 @Override
 public String next(final ProtocolCodecs<ServerState> codecs, final ServerState sessionState) {
   if (isCompleted()) {
     if (sessionState.isTerminated()) {
       return ProtocolState.QUIT.name();
     }
     return ProtocolState.DATA.name();
   } else {
     return null;
   }
 }
  @Override
  public void consumeData(final IOSession iosession, final ServerState sessionState)
      throws IOException, SMTPProtocolException {
    Args.notNull(iosession, "IO session");
    Args.notNull(sessionState, "Session state");

    SessionInputBuffer buf = this.iobuffers.getInbuf();

    synchronized (sessionState) {
      for (; ; ) {
        int bytesRead = buf.fill(iosession.channel());
        try {
          SMTPCommand command = this.parser.parse(buf, bytesRead == -1);
          if (command == null) {
            if (bytesRead == -1 && !sessionState.isTerminated() && this.pendingActions.isEmpty()) {
              throw new UnexpectedEndOfStreamException();
            } else {
              break;
            }
          }
          Action<ServerState> action = this.commandHandler.handle(command);
          this.pendingActions.add(action);
        } catch (SMTPErrorException ex) {
          SMTPReply reply = new SMTPReply(ex.getCode(), ex.getEnhancedCode(), ex.getMessage());
          this.pendingActions.add(new SimpleAction(reply));
        } catch (SMTPProtocolException ex) {
          SMTPReply reply =
              new SMTPReply(
                  SMTPCodes.ERR_PERM_SYNTAX_ERR_COMMAND, new SMTPCode(5, 3, 0), ex.getMessage());
          this.pendingActions.add(new SimpleAction(reply));
        }
      }

      if (!this.pendingActions.isEmpty()) {
        iosession.setEvent(SelectionKey.OP_WRITE);
      }
    }
  }