@Override
  protected void messageReceived(ChannelHandlerContext context, CmdlineRemoteProto.Message message) {
    SessionData sessionData = context.channel().attr(Holder.SESSION_DATA).get();
    final boolean isFirstMessage = sessionData == null;
    final UUID sessionId;
    if (isFirstMessage) {
      // this is the first message for this session, so fill session data with missing info
      final CmdlineRemoteProto.Message.UUID id = message.getSessionId();
      sessionId = new UUID(id.getMostSigBits(), id.getLeastSigBits());

      sessionData = mySessionDescriptors.get(sessionId);
      if (sessionData != null) {
        sessionData.channel = context.channel();
        context.channel().attr(Holder.SESSION_DATA).set(sessionData);
      }
    }
    else {
      sessionId = sessionData.sessionId;
    }

    final BuilderMessageHandler handler = sessionData != null? sessionData.handler : null;
    try {
      if (handler == null) {
        if (!isBuilderEvent(message)) {
          // do not pollute logs, just silently skip events in case handler is missing
          LOG.info("No message handler registered for session " + sessionId);
        }
        return;
      }

      final CmdlineRemoteProto.Message.Type messageType = message.getType();
      switch (messageType) {
        case FAILURE -> handler.handleFailure(sessionId, message.getFailure());
        case BUILDER_MESSAGE -> {
          final CmdlineRemoteProto.Message.BuilderMessage builderMessage = message.getBuilderMessage();
          final CmdlineRemoteProto.Message.BuilderMessage.Type msgType = builderMessage.getType();
          if (msgType == CmdlineRemoteProto.Message.BuilderMessage.Type.PARAM_REQUEST) {
            //noinspection SynchronizationOnLocalVariableOrMethodParameter
            synchronized (sessionData) {
              final CmdlineRemoteProto.Message.ControllerMessage params = sessionData.params;
              if (params != null) {
                sessionData.state = SessionData.State.RUNNING;
                handler.buildStarted(sessionId);
                sessionData.params = null;
                context.writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, params));
              }
              else {
                if (sessionData.state == SessionData.State.INITIAL) {
                  sessionData.state = SessionData.State.WAITING_PARAMS;
                }
                else {
                  // this message is expected to be sent only once.
                  // To be on the safe side, cancel the session
                  cancelSession(sessionId);
                }
              }
            }
          }
          else {
            handler.handleBuildMessage(context.channel(), sessionId, builderMessage);
          }
        }
        default -> LOG.info("Unsupported message type " + messageType);
      }
    }
    finally {
      if ((isFirstMessage && myCanceledSessions.contains(sessionId)) || (handler == null && !isBuilderEvent(message))) {
        // handle the case when the session had been cancelled before communication even started
        // or if message handling is not possible due to missing handler
        context.channel().writeAndFlush(CmdlineProtoUtil.toMessage(sessionId, CmdlineProtoUtil.createCancelCommand()));
      }
    }
  }
@Override
  protected void messageReceived(ChannelHandlerContext context, CmdlineRemoteProto.Message message)