All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.scalecube.cluster.transport.api.Message Maven / Gradle / Ivy

package io.scalecube.cluster.transport.api;

import io.scalecube.net.Address;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;

/**
 * The Class Message introduces generic protocol used for point to point communication by transport.
 */
public final class Message implements Externalizable {

  private static final long serialVersionUID = 1L;

  /**
   * This header is supposed to be used by application in case if same data type can be reused for
   * several messages so it will allow to qualify the specific message type.
   */
  public static final String HEADER_QUALIFIER = "q";

  /**
   * This header is supposed to be used by application in order to correlate request and response
   * messages.
   */
  public static final String HEADER_CORRELATION_ID = "cid";

  /**
   * This header represents sender address of type {@link Address}. It's an address of message
   * originator. This header is optional.
   */
  public static final String HEADER_SENDER = "sender";

  private Map headers = Collections.emptyMap();
  private Object data;

  public Message() {}

  private Message(Builder builder) {
    this.data = builder.data;
    this.headers = Collections.unmodifiableMap(Objects.requireNonNull(builder.headers));
  }

  /**
   * Instantiates a new message with the given data and without headers.
   *
   * @param data the data to build a message from
   * @return the built message
   */
  public static Message fromData(Object data) {
    return withData(data).build();
  }

  /**
   * Instantiates a new message builder with the given data and without headers.
   *
   * @param data the initial data for the builder
   * @return a builder with initial data
   */
  public static Builder withData(Object data) {
    return builder().data(data);
  }

  /**
   * Instantiates a new message with the given headers and with empty data.
   *
   * @param headers an initial headers to build a message from
   * @return the built message
   */
  public static Message fromHeaders(Map headers) {
    return withHeaders(headers).build();
  }

  /**
   * Instantiates a new message builder with the given headers and with empty data.
   *
   * @param headers the initial headers for the builder
   * @return a builder with initial headers
   */
  public static Builder withHeaders(Map headers) {
    return builder().headers(headers);
  }

  /**
   * Instantiates a new message with the given qualifier header and with empty data.
   *
   * @param qualifier the qualifier to build a message from
   * @return the built message
   */
  public static Message fromQualifier(String qualifier) {
    return withQualifier(qualifier).build();
  }

  /**
   * Instantiates a new message builder with the given qualifier header and with empty data.
   *
   * @param qualifier the initial qualifier for the builder
   * @return a builder with initial qualifier
   */
  public static Builder withQualifier(String qualifier) {
    return builder().qualifier(qualifier);
  }

  /**
   * Instantiates new message with the same data and headers as at given message.
   *
   * @param message the message to be copied
   * @return a new message, with the same data and headers
   */
  public static Message from(Message message) {
    return with(message).build();
  }

  /**
   * Instantiates new message builder with the same data and headers as at given message.
   *
   * @param message the message to instantiate the new builder from
   * @return a builder with initial data and headers from the message
   */
  public static Builder with(Message message) {
    return withData(message.data).headers(message.headers);
  }

  /**
   * Instantiates new empty message builder.
   *
   * @return new builder
   */
  public static Builder builder() {
    return new Builder();
  }

  /**
   * Returns the message headers.
   *
   * @return message headers
   */
  public Map headers() {
    return headers;
  }

  /**
   * Returns header value by given header name.
   *
   * @param name header name
   * @return the message header by given header name
   */
  public String header(String name) {
    return headers.get(name);
  }

  /**
   * Returns message qualifier.
   *
   * @return qualifier string
   */
  public String qualifier() {
    return header(HEADER_QUALIFIER);
  }

  /**
   * Returns message correlation id.
   *
   * @return correlation id
   */
  public String correlationId() {
    return header(HEADER_CORRELATION_ID);
  }

  /**
   * Return the message data, which can be byte array, string or any type.
   *
   * @param  data type
   * @return payload of the message or null if message is without any payload
   */
  public  T data() {
    // noinspection unchecked
    return (T) data;
  }

  /**
   * Returns {@link Address} of the sender of this message.
   *
   * @return address
   */
  public Address sender() {
    return Optional.ofNullable(header(HEADER_SENDER)).map(Address::from).orElse(null);
  }

  @Override
  public String toString() {
    return new StringJoiner(", ", Message.class.getSimpleName() + "[", "]")
        .add("headers=" + headers)
        .add("data=" + data)
        .toString();
  }

  @Override
  public void writeExternal(ObjectOutput out) throws IOException {
    // headers
    out.writeInt(headers.size());
    for (Entry header : headers.entrySet()) {
      out.writeUTF(header.getKey());
      out.writeUTF(header.getValue());
    }
    // data
    out.writeObject(data);
  }

  @Override
  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    // headers
    int headersSize = in.readInt();
    Map headers = new HashMap<>(headersSize);
    for (int i = 0; i < headersSize; i++) {
      String name = in.readUTF();
      String value = in.readUTF();
      headers.put(name, value);
    }
    this.headers = Collections.unmodifiableMap(headers);
    // data
    data = in.readObject();
  }

  public static class Builder {

    private Map headers = new HashMap<>();
    private Object data;

    private Builder() {}

    private Object data() {
      return this.data;
    }

    public Builder data(Object data) {
      this.data = data;
      return this;
    }

    private Map headers() {
      return this.headers;
    }

    /**
     * Bulk setter for headers. Delegates to {@link #header(String, String)}.
     *
     * @param headers headers
     * @return builder
     */
    public Builder headers(Map headers) {
      headers.forEach(this::header);
      return this;
    }

    /**
     * Setter for header.
     *
     * @param key key; required parameter.
     * @param value value; required parameter.
     * @return builder
     */
    public Builder header(String key, String value) {
      Objects.requireNonNull(key);
      Objects.requireNonNull(value);
      headers.put(key, value);
      return this;
    }

    public Builder qualifier(String qualifier) {
      return header(HEADER_QUALIFIER, qualifier);
    }

    public Builder correlationId(String correlationId) {
      return header(HEADER_CORRELATION_ID, correlationId);
    }

    public Builder sender(Address sender) {
      return header(HEADER_SENDER, sender.toString());
    }

    public Message build() {
      return new Message(this);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy