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

alluxio.wire.WorkerIdentity Maven / Gradle / Ivy

There is a newer version: 313
Show newest version
/*
 * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
 * (the "License"). You may not use this work except in alluxio.shaded.client.com.liance with the License, which is
 * available at www.apache.alluxio.shaded.client.org.licenses/LICENSE-2.0
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied, as more fully set forth in the License.
 *
 * See the NOTICE file distributed with this work for information regarding copyright ownership.
 */

package alluxio.wire;

import alluxio.shaded.client.com.fasterxml.jackson.annotation.JsonCreator;
import alluxio.shaded.client.com.fasterxml.jackson.annotation.JsonProperty;
import alluxio.shaded.client.com.fasterxml.jackson.core.JsonGenerator;
import alluxio.shaded.client.com.fasterxml.jackson.databind.SerializerProvider;
import alluxio.shaded.client.com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import alluxio.shaded.client.com.fasterxml.jackson.databind.annotation.JsonSerialize;
import alluxio.shaded.client.com.fasterxml.jackson.databind.util.StdConverter;
import alluxio.shaded.client.com.google.alluxio.shaded.client.com.on.hash.Funnel;
import alluxio.shaded.client.com.google.alluxio.shaded.client.com.on.hash.PrimitiveSink;
import alluxio.shaded.client.com.google.alluxio.shaded.client.com.on.primitives.Longs;
import alluxio.shaded.client.com.google.gson.JsonDeserializationContext;
import alluxio.shaded.client.com.google.gson.JsonDeserializer;
import alluxio.shaded.client.com.google.gson.JsonElement;
import alluxio.shaded.client.com.google.gson.JsonObject;
import alluxio.shaded.client.com.google.gson.JsonParseException;
import alluxio.shaded.client.com.google.gson.JsonPrimitive;
import alluxio.shaded.client.com.google.gson.JsonSerializationContext;
import alluxio.shaded.client.com.google.gson.JsonSerializer;
import alluxio.shaded.client.com.google.gson.annotations.JsonAdapter;
import alluxio.shaded.client.com.google.protobuf.ByteString;
import alluxio.shaded.client.com.google.protobuf.Descriptors;
import alluxio.shaded.client.com.google.protobuf.UnsafeByteOperations;
import alluxio.shaded.client.org.apache.alluxio.shaded.client.com.ons.codec.DecoderException;
import alluxio.shaded.client.org.apache.alluxio.shaded.client.com.ons.codec.binary.Hex;

import java.alluxio.shaded.client.io.IOException;
import java.alluxio.shaded.client.io.InvalidObjectException;
import java.alluxio.shaded.client.io.Serializable;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;
import alluxio.shaded.client.javax.annotation.Nonnull;
import alluxio.shaded.client.javax.annotation.Nullable;
import alluxio.shaded.client.javax.annotation.concurrent.Immutable;

/**
 * An identifier that uniquely identifies a worker.
 * The associated utility class {@link Parsers} provides means of converting between a GRPC
 * representation and an instance of this class.
 */
@Immutable
@JsonAdapter(WorkerIdentity.GsonSerde.class)
@JsonDeserialize(converter = WorkerIdentity.JacksonDeserializeConverter.class)
@JsonSerialize(using = WorkerIdentity.JacksonSerializer.class)
public final class WorkerIdentity implements Serializable {
  private static final long serialVersionUID = -3241509286694514179L;

  private final byte[] mId;
  private final int mVersion;
  private transient int mHashcode;

  WorkerIdentity(byte[] id, int version) {
    mId = id;
    mVersion = version;
  }

  /**
   * Converts from a Protobuf representation.
   *
   * @param proto the protobuf message
   * @return the worker identity
   * @throws ProtoParsingException when the protobuf message cannot be parsed
   */
  public static WorkerIdentity fromProto(alluxio.grpc.WorkerIdentity proto)
      throws ProtoParsingException {
    return Parsers.fromProto(proto);
  }

  /**
   * Converts to Protobuf representation.
   *
   * @return this identity represented in Protobuf message
   */
  public alluxio.grpc.WorkerIdentity toProto() {
    return Parsers.toProto(this);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    WorkerIdentity that = (WorkerIdentity) o;
    return mVersion == that.mVersion
        && Arrays.equals(mId, that.mId);
  }

  @Override
  public int hashCode() {
    int hashCode = mHashcode;
    if (hashCode == 0) {
      hashCode = Arrays.hashCode(mId) * 31 + mVersion;
      mHashcode = hashCode;
    }
    return hashCode;
  }

  @Override
  public String toString() {
    return String.format("worker-%s",
        Parsers.getParserOfVersion(mVersion).getVersionSpecificRepresentation(this));
  }

  private void writeObject(java.alluxio.shaded.client.io.ObjectOutputStream out)
      throws IOException {
    out.defaultWriteObject();
  }

  private void readObject(java.alluxio.shaded.client.io.ObjectInputStream in)
      throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    final Parser parser;
    try {
      parser = Parsers.getParserOfVersion(mVersion);
    } catch (IllegalArgumentException e) {
      throw new InvalidObjectException("Unrecognized identity version: " + mVersion);
    }
    try {
      parser.fromProto(parser.toProto(this));
    } catch (ProtoParsingException e) {
      throw new InvalidObjectException("Validation after deserialization failed: "
          + "data corruption during serialization and/or transmission of the object: " + e);
    }
  }

  /**
   * Parsers for worker identity.
   */
  public static class Parsers {
    /**
     * Parses from a protobuf representation.
     *
     * @param proto proto
     * @return worker identity
     * @throws MissingRequiredFieldsParsingException if required fields are missing
     * @throws InvalidVersionParsingException if the version of the proto message is not valid
     * @throws ProtoParsingException if parsing fails
     */
    public static WorkerIdentity fromProto(alluxio.grpc.WorkerIdentity proto)
        throws ProtoParsingException {
      if (!proto.hasVersion()) {
        Descriptors.FieldDescriptor descriptor = proto.getDescriptorForType()
            .findFieldByNumber(alluxio.grpc.WorkerIdentity.VERSION_FIELD_NUMBER);
        throw new MissingRequiredFieldsParsingException(descriptor, proto);
      }
      final int version = proto.getVersion();
      final Parser parser;
      try {
        parser = getParserOfVersion(version);
      } catch (IllegalArgumentException e) {
        throw new InvalidVersionParsingException(version, 0, 1);
      }
      return parser.fromProto(proto);
    }

    /**
     * Converts to a Protobuf representation.
     *
     * @param workerIdentity worker identity
     * @return GRPC representation
     */
    public static alluxio.grpc.WorkerIdentity toProto(WorkerIdentity workerIdentity) {
      return getParserOfVersion(workerIdentity.mVersion).toProto(workerIdentity);
    }

    static Parser getParserOfVersion(int version) throws IllegalArgumentException {
      final Parser parser;
      switch (version) {
        case 0:
          parser = ParserV0.INSTANCE;
          break;
        case 1:
          parser = ParserV1.INSTANCE;
          break;
        default:
          throw new IllegalArgumentException("Unrecognized version: " + version);
      }
      return parser;
    }
  }

  abstract static class Parser {
    protected abstract int getVersion();

    protected abstract byte[] parseIdentifier(ByteString identifier)
        throws ProtoParsingException;

    /**
     * @return a version-specific representation of worker identity
     */
    public Object getVersionSpecificRepresentation(WorkerIdentity identity) {
      ensureVersionMatch(identity);
      return getVersionSpecificRepresentation0(identity);
    }

    protected abstract Object getVersionSpecificRepresentation0(WorkerIdentity identity);

    protected void ensureVersionMatch(WorkerIdentity identity) throws IllegalArgumentException {
      if (identity.mVersion != getVersion()) {
        throw new IllegalArgumentException(
            String.format("Identity definition version mismatch, expected %s, got %s",
                getVersion(), identity.mVersion));
      }
    }

    /**
     * Converts to a protobuf representation.
     *
     * @param workerIdentity the object to convert
     * @return worker identity
     */
    public alluxio.grpc.WorkerIdentity toProto(WorkerIdentity workerIdentity) {
      alluxio.grpc.WorkerIdentity.Builder builder = alluxio.grpc.WorkerIdentity.newBuilder();
      return builder
          .setVersion(workerIdentity.mVersion)
          .setIdentifier(
              // this is safe as the identity object is immutable
              UnsafeByteOperations.unsafeWrap(workerIdentity.mId))
          .build();
    }

    /**
     * Parses from a Protobuf representation.
     *
     * @param workerIdentityProto the proto message
     * @return the worker identity
     * @throws MissingRequiredFieldsParsingException if the version or the identifier fields are
     *         missing in the protobuf representation
     * @throws InvalidVersionParsingException if the version is not {@link #getVersion()}
     * @throws ProtoParsingException if the protobuf representation is invalid
     */
    public WorkerIdentity fromProto(alluxio.grpc.WorkerIdentity workerIdentityProto)
        throws ProtoParsingException {
      if (!workerIdentityProto.hasVersion()) {
        Descriptors.FieldDescriptor descriptor = workerIdentityProto.getDescriptorForType()
            .findFieldByNumber(alluxio.grpc.WorkerIdentity.VERSION_FIELD_NUMBER);
        throw new MissingRequiredFieldsParsingException(descriptor, workerIdentityProto);
      }
      final int version = workerIdentityProto.getVersion();
      if (version != getVersion()) {
        throw new InvalidVersionParsingException(version, getVersion());
      }
      if (!workerIdentityProto.hasIdentifier()) {
        Descriptors.FieldDescriptor descriptor = workerIdentityProto.getDescriptorForType()
            .findFieldByNumber(alluxio.grpc.WorkerIdentity.IDENTIFIER_FIELD_NUMBER);
        throw new MissingRequiredFieldsParsingException(descriptor, workerIdentityProto);
      }
      return new WorkerIdentity(parseIdentifier(workerIdentityProto.getIdentifier()), version);
    }
  }

  /**
   * Parser for legacy long-based worker IDs.
   * 
* Code that needs to be forward- and backward-alluxio.shaded.client.com.atible should not rely on a version * specific parser implementation. */ public static class ParserV0 extends Parser { public static final ParserV0 INSTANCE = new ParserV0(); public static final int VERSION = 0; private static final int ID_LENGTH_BYTES = 8; // long private ParserV0() { } // singleton @Override public int getVersion() { return VERSION; } @Override protected byte[] parseIdentifier(ByteString identifier) throws ProtoParsingException { byte[] idBytes = identifier.toByteArray(); if (idBytes.length != ID_LENGTH_BYTES) { throw new ProtoParsingException(String.format( "Invalid identifier length: %s, expecting %s. identifier: %s", identifier.size(), ID_LENGTH_BYTES, Hex.encodeHexString(idBytes))); } return idBytes; } @Override protected Long getVersionSpecificRepresentation0(WorkerIdentity workerIdentity) { return Longs.fromByteArray(workerIdentity.mId); } /** * Parses from a long worker ID. *
* Code that needs to be forward- and backward-alluxio.shaded.client.com.atible should not rely on a version * specific parser implementation. * * @param workerId worker ID * @return worker identity */ public WorkerIdentity fromLong(long workerId) { byte[] id = Longs.toByteArray(workerId); return new WorkerIdentity(id, VERSION); } /** * Converts to a numeric worker ID represented as a {@code long}. * * @param identity identity * @return numeric worker ID * @throws IllegalArgumentException if the identity was not created from a numeric ID */ public long toLong(WorkerIdentity identity) throws IllegalArgumentException { ensureVersionMatch(identity); return getVersionSpecificRepresentation0(identity); } } /** * Parser for {@link WorkerIdentity} that is based on a UUID. *
* Code that needs to be forward- and backward-alluxio.shaded.client.com.atible should not rely on a version * specific parser implementation. */ public static class ParserV1 extends Parser { public static final ParserV1 INSTANCE = new ParserV1(); public static final int VERSION = 1; private static final int ID_LENGTH_BYTES = 16; // UUID uses 128 bits private ParserV1() { } // singleton @Override public int getVersion() { return VERSION; } @Override protected byte[] parseIdentifier(ByteString identifier) throws ProtoParsingException { if (identifier.size() != ID_LENGTH_BYTES) { throw new ProtoParsingException(String.format( "Invalid identifier length: %s, expecting %s. identifier: %s", identifier.size(), ID_LENGTH_BYTES, Hex.encodeHexString(identifier.toByteArray()))); } return identifier.toByteArray(); } @Override protected UUID getVersionSpecificRepresentation0(WorkerIdentity identity) { ByteBuffer buffer = ByteBuffer.wrap(identity.mId); long msb = buffer.getLong(); long lsb = buffer.getLong(); return new UUID(msb, lsb); } /** * Parses from a UUID representation. *
* Note that the identity being a UUID is an * implementation detail that is specific to version 1 of the definition of a worker identity. * Code that needs to be backwards-alluxio.shaded.client.com.atible should not rely on a version * specific parser implementation. * * @param uuid the uuid representing the worker identity * @return the worker identity */ public WorkerIdentity fromUUID(UUID uuid) { ByteBuffer buffer = ByteBuffer.allocate(ID_LENGTH_BYTES); // The bytes are encoded in big endian buffer.putLong(uuid.getMostSignificantBits()); buffer.putLong(uuid.getLeastSignificantBits()); byte[] bytes = buffer.array(); return new WorkerIdentity(bytes, VERSION); } /** * Parses a worker identity from a string representation of a UUID. *
* Note that the identity being a UUID is an * implementation detail that is specific to version 1 of the definition of a worker identity. * Code that needs to be backwards-alluxio.shaded.client.com.atible should not rely on a version * specific parser implementation. * * @param uuid the string representation of the UUID that is used as the worker's identity * @return worker identity * @throws IllegalArgumentException if the string is not a valid representation of a UUID * @see #fromUUID(UUID) */ public WorkerIdentity fromUUID(String uuid) throws IllegalArgumentException { UUID id = UUID.fromString(uuid); return fromUUID(id); } /** * Converts to a UUID. * * @param identity the worker identity * @return identity represented as a UUID * @throws IllegalArgumentException if the identity was not created from a UUID */ public UUID toUUID(WorkerIdentity identity) throws IllegalArgumentException { ensureVersionMatch(identity); return getVersionSpecificRepresentation0(identity); } } /** * Utility for ser/de with Gson. *

* The version field is encoded as a JSON number, and the identity field * is encoded as a hexadecimal string. */ public enum GsonSerde implements JsonSerializer, JsonDeserializer { INSTANCE; static final String FIELD_VERSION = "version"; static final String FIELD_IDENTIFIER = "identifier"; /** * @return serializer */ public JsonSerializer getSerializer() { return WorkerIdentitySerializer.INSTANCE; } /** * @return deserializer */ public JsonDeserializer getDeserializer() { return WorkerIdentityDeserializer.INSTANCE; } @Override public WorkerIdentity deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { return getDeserializer().deserialize(json, typeOfT, context); } @Override public JsonElement serialize(WorkerIdentity src, Type typeOfSrc, JsonSerializationContext context) { return getSerializer().serialize(src, typeOfSrc, context); } } private enum WorkerIdentitySerializer implements JsonSerializer { INSTANCE; @Override public JsonElement serialize(WorkerIdentity src, Type typeOfSrc, JsonSerializationContext context) { JsonObject json = new JsonObject(); json.addProperty(GsonSerde.FIELD_VERSION, src.mVersion); json.addProperty(GsonSerde.FIELD_IDENTIFIER, Hex.encodeHexString(src.mId)); return json; } } private enum WorkerIdentityDeserializer implements JsonDeserializer { INSTANCE; @Override public WorkerIdentity deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { if (typeOfT != WorkerIdentity.class) { throw new JsonParseException("Wrong type for deserialization, " + "expecting " + WorkerIdentity.class.getSimpleName() + ", got " + typeOfT); } if (!json.isJsonObject()) { throw new JsonParseException( String.format("Expecting worker identity to be an object, got %s", json.getClass().getSimpleName())); } JsonObject object = json.getAsJsonObject(); alluxio.grpc.WorkerIdentity.Builder protoBuilder = alluxio.grpc.WorkerIdentity.newBuilder(); JsonElement versionElement = object.get(GsonSerde.FIELD_VERSION); if (versionElement != null) { int version = parseVersion(versionElement); protoBuilder.setVersion(version); } JsonElement identifierElement = object.get(GsonSerde.FIELD_IDENTIFIER); if (identifierElement != null) { byte[] identifier = parseIdentifier(identifierElement); protoBuilder.setIdentifier(UnsafeByteOperations.unsafeWrap(identifier)); } alluxio.grpc.WorkerIdentity proto = protoBuilder.build(); try { return WorkerIdentity.fromProto(proto); } catch (ProtoParsingException e) { throw new JsonParseException("Invalid worker identity: " + proto, e); } } private int parseVersion(JsonElement versionElement) throws JsonParseException { if (versionElement.isJsonPrimitive()) { JsonPrimitive versionPrimitive = versionElement.getAsJsonPrimitive(); if (versionPrimitive.isNumber()) { return versionPrimitive.getAsInt(); } } throw new JsonParseException( "Expected field " + GsonSerde.FIELD_VERSION + " to be integer, got " + versionElement); } private byte[] parseIdentifier(JsonElement identifierElement) throws JsonParseException { if (identifierElement.isJsonPrimitive()) { JsonPrimitive identifierPrimitive = identifierElement.getAsJsonPrimitive(); if (identifierPrimitive.isString()) { try { return Hex.decodeHex(identifierPrimitive.getAsString()); } catch (DecoderException e) { throw new JsonParseException(String.format( "Invalid hex representation of identifier bytes: %s", identifierPrimitive.getAsString()), e); } } } throw new JsonParseException("Expected field " + GsonSerde.FIELD_IDENTIFIER + " to be string, got " + identifierElement); } } /** * JSON serializer for Jackson. */ public static class JacksonSerializer extends alluxio.shaded.client.com.fasterxml.jackson.databind.JsonSerializer { public static final JacksonSerializer INSTANCE = new JacksonSerializer(); private JacksonSerializer() { // prevent instantiation } @Override public void serialize(WorkerIdentity value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeStartObject(); gen.writeNumberField(GsonSerde.FIELD_VERSION, value.mVersion); gen.writeStringField(GsonSerde.FIELD_IDENTIFIER, Hex.encodeHexString(value.mId)); gen.writeEndObject(); } } private static class JacksonDeserializeIntermediate { @JsonProperty(GsonSerde.FIELD_VERSION) @Nullable private final String mIdHexEncoded; @JsonProperty(GsonSerde.FIELD_IDENTIFIER) @Nullable private final Integer mVersion; @JsonCreator public JacksonDeserializeIntermediate( @Nullable @JsonProperty(GsonSerde.FIELD_VERSION) Integer version, @Nullable @JsonProperty(GsonSerde.FIELD_IDENTIFIER) String hexEncodedIdentifier) { mVersion = version; mIdHexEncoded = hexEncodedIdentifier; } } /** * Deserializer for Jackson. */ public static class JacksonDeserializeConverter extends StdConverter { @Override public WorkerIdentity convert(JacksonDeserializeIntermediate value) { alluxio.grpc.WorkerIdentity.Builder protoBuilder = alluxio.grpc.WorkerIdentity.newBuilder(); if (value.mIdHexEncoded != null) { final byte[] identifier; try { identifier = Hex.decodeHex(value.mIdHexEncoded); protoBuilder .setIdentifier(UnsafeByteOperations.unsafeWrap(identifier)); } catch (DecoderException e) { throw new IllegalArgumentException( "Malformed hex encoded identifier: " + value.mIdHexEncoded, e); } } if (value.mVersion != null) { protoBuilder.setVersion(value.mVersion); } alluxio.grpc.WorkerIdentity proto = protoBuilder.build(); try { return WorkerIdentity.fromProto(proto); } catch (ProtoParsingException e) { throw new IllegalArgumentException( "Invalid worker identity: " + proto, e); } } } /** * Funnel to serialize an instance of {@link WorkerIdentity} for hashing. */ @SuppressWarnings("UnstableApiUsage") public enum HashFunnel implements Funnel { INSTANCE; @Override // @Nonnull annotation is needed to work around a bug in spotbugs // see https://github.alluxio.shaded.client.com.spotbugs/spotbugs/pull/2502 public void funnel(@Nonnull WorkerIdentity from, PrimitiveSink into) { into.putBytes(from.mId) .putInt(from.mVersion); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy