/**
   * Callback from the interceptor stack. <br>
   * When a message is received from a remote node, this method will be invoked by the previous
   * interceptor.<br>
   * This method can also be used to send a message to other components within the same application,
   * but its an extreme case, and you're probably better off doing that logic between the
   * applications itself.
   *
   * @param msg ChannelMessage
   */
  @Override
  public void messageReceived(ChannelMessage msg) {
    if (msg == null) return;
    try {
      if (Logs.MESSAGES.isTraceEnabled()) {
        Logs.MESSAGES.trace(
            "GroupChannel - Received msg:"
                + new UniqueId(msg.getUniqueId())
                + " at "
                + new java.sql.Timestamp(System.currentTimeMillis())
                + " from "
                + msg.getAddress().getName());
      }

      Serializable fwd = null;
      if ((msg.getOptions() & SEND_OPTIONS_BYTE_MESSAGE) == SEND_OPTIONS_BYTE_MESSAGE) {
        fwd = new ByteMessage(msg.getMessage().getBytes());
      } else {
        try {
          fwd =
              XByteBuffer.deserialize(
                  msg.getMessage().getBytesDirect(), 0, msg.getMessage().getLength());
        } catch (Exception sx) {
          log.error(sm.getString("groupChannel.unable.deserialize", msg), sx);
          return;
        }
      }
      if (Logs.MESSAGES.isTraceEnabled()) {
        Logs.MESSAGES.trace(
            "GroupChannel - Receive Message:" + new UniqueId(msg.getUniqueId()) + " is " + fwd);
      }

      // get the actual member with the correct alive time
      Member source = msg.getAddress();
      boolean rx = false;
      boolean delivered = false;
      for (int i = 0; i < channelListeners.size(); i++) {
        ChannelListener channelListener = (ChannelListener) channelListeners.get(i);
        if (channelListener != null && channelListener.accept(fwd, source)) {
          channelListener.messageReceived(fwd, source);
          delivered = true;
          // if the message was accepted by an RPC channel, that channel
          // is responsible for returning the reply, otherwise we send an absence reply
          if (channelListener instanceof RpcChannel) rx = true;
        }
      } // for
      if ((!rx) && (fwd instanceof RpcMessage)) {
        // if we have a message that requires a response,
        // but none was given, send back an immediate one
        sendNoRpcChannelReply((RpcMessage) fwd, source);
      }
      if (Logs.MESSAGES.isTraceEnabled()) {
        Logs.MESSAGES.trace(
            "GroupChannel delivered[" + delivered + "] id:" + new UniqueId(msg.getUniqueId()));
      }

    } catch (Exception x) {
      // this could be the channel listener throwing an exception, we should log it
      // as a warning.
      if (log.isWarnEnabled()) log.warn(sm.getString("groupChannel.receiving.error"), x);
      throw new RemoteProcessException("Exception:" + x.getMessage(), x);
    }
  }