Protocol Buffers by Google

Protocol Buffers  |  Google Developers

Protocol Buffersは、XMLデータに比べて1/3〜1/10に小さくなり、20〜100倍高速に処理できるという。
とのこと。


これは、いいです。

今やっている実装の多くは、WSDL,XSDでメソッドとデータ構造を定義して、そこからWSDL2JAVAで実装コードを起こす形に落ち着いているんですが、このProtocol Bufferはそれと比較してもシンプルでよいです。

.protoを用意するだけで、JAVAコードを生成してくれ、さらにそのフィールドに対するgetter/setterも生成してくれました。
WSDLをWSDL2JAVAでコード化する場合とほとんど同じです。
細かいWSDL定義も知らなくてもよいし、XMLスキーマをいちいち定義しなくてもよいので、かなり楽です。
複雑なデータ構造でなければ、これを使うのがよいです。XMLを用いた階層的なデータでは、従来どおりSOAP/XMLを用いるほうがよさそうですが。
.protoでデータモデルを定義するだけで後はロジックを考えればよくなるので、相当楽になりそうです。
生成されたコードはPOJO(ただのJAVAオブジェクト)になるので、これとDIコンテナを組み合わせるとデータモデルとロジックを分離泥臭いコーディング部分はかなり減らせます。

ちなみに、GoogleもDIコンテナを出しています。
Google Guice - 第1回[前編] Guice 1.0 - GoogleからリリースされたDIフレームワーク:気になる開発プロダクツ|gihyo.jp … 技術評論社
これもかなりよさそうです。

以下に、Protocol Bufferを実行した結果を記します。

  • 準備

http://code.google.com/p/protobuf/downloads/list
から
protoc-2.0.0beta-win32.zip
をダウンロードし、protoc.exeをゲットします。
その後、以下のプロトコルファイルを用意し、コンパイルすればJAVAファイルが生成されます。

  • 実行したコマンド
protoc.exe a.proto --java_out .
message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}
  • 出力ファイル A.java
// Generated by the protocol buffer compiler.  DO NOT EDIT!

public final class A {
  private A() {}
  public static com.google.protobuf.Descriptors.FileDescriptor
      getDescriptor() {
    return descriptor;
  }
  private static final com.google.protobuf.Descriptors.FileDescriptor
      descriptor = buildDescriptor();
  private static
      com.google.protobuf.Descriptors.FileDescriptor
      buildDescriptor() {
    java.lang.String descriptorData =
      "\n\007a.proto\"\310\001\n\006Person\022\014\n\004name\030\001
\002(\t\022\n\n\002id" +
      "\030\002 \002(\005\022\r\n\005email\030\003
\001(\t\022\"\n\005phone\030\004 \003(\0132\023.P" +
      "erson.PhoneNumber\032D\n\013PhoneNumber\022\016\n\006numb" +
      "er\030\001 \002(\t\022%\n\004type\030\002
\001(\0162\021.Person.PhoneTyp" +

"e:\004HOME\"+\n\tPhoneType\022\n\n\006MOBILE\020\000\022\010\n\004HOME" +
      "\020\001\022\010\n\004WORK\020\002";
    try {
      return com.google.protobuf.Descriptors.FileDescriptor
        .internalBuildGeneratedFileFrom(descriptorData,
          new com.google.protobuf.Descriptors.FileDescriptor[] {
          });
    } catch (Exception e) {
      throw new RuntimeException(
        "Failed to parse protocol buffer descriptor for " +
        "\"a.proto\".", e);
    }
  }

  public static final class Person extends
      com.google.protobuf.GeneratedMessage {
    // Use Person.newBuilder() to construct.
    private Person() {}

    private static final Person defaultInstance = new Person();
    public static Person getDefaultInstance() {
      return defaultInstance;
    }

    public Person getDefaultInstanceForType() {
      return defaultInstance;
    }

    public static final com.google.protobuf.Descriptors.Descriptor
        getDescriptor() {
      return A.internal_static_Person_descriptor;
    }

    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
        internalGetFieldAccessorTable() {
      return A.internal_static_Person_fieldAccessorTable;
    }

    public static enum PhoneType {
      MOBILE(0, 0),
      HOME(1, 1),
      WORK(2, 2),
      ;


      public final int getNumber() { return value; }

      public static PhoneType valueOf(int value) {
        switch (value) {
          case 0: return MOBILE;
          case 1: return HOME;
          case 2: return WORK;
          default: return null;
        }
      }

      public final com.google.protobuf.Descriptors.EnumValueDescriptor
          getValueDescriptor() {
        return getDescriptor().getValues().get(index);
      }
      public final com.google.protobuf.Descriptors.EnumDescriptor
          getDescriptorForType() {
        return getDescriptor();
      }
      public static final com.google.protobuf.Descriptors.EnumDescriptor
          getDescriptor() {
        return A.Person.getDescriptor().getEnumTypes().get(0);
      }

      private static final PhoneType[] VALUES = {
        MOBILE, HOME, WORK,
      };
      public static PhoneType valueOf(
          com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
        if (desc.getType() != getDescriptor()) {
          throw new java.lang.IllegalArgumentException(
            "EnumValueDescriptor is not for this type.");
        }
        return VALUES[desc.getIndex()];
      }
      private final int index;
      private final int value;
      private PhoneType(int index, int value) {
        this.index = index;
        this.value = value;
      }
    }

    public static final class PhoneNumber extends
        com.google.protobuf.GeneratedMessage {
      // Use PhoneNumber.newBuilder() to construct.
      private PhoneNumber() {}

      private static final PhoneNumber defaultInstance = new PhoneNumber();
      public static PhoneNumber getDefaultInstance() {
        return defaultInstance;
      }

      public PhoneNumber getDefaultInstanceForType() {
        return defaultInstance;
      }

      public static final com.google.protobuf.Descriptors.Descriptor
          getDescriptor() {
        return A.internal_static_Person_PhoneNumber_descriptor;
      }

      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
          internalGetFieldAccessorTable() {
        return A.internal_static_Person_PhoneNumber_fieldAccessorTable;
      }

      // required string number = 1;
      private boolean hasNumber;
      private java.lang.String number_ = "";
      public boolean hasNumber() { return hasNumber; }
      public java.lang.String getNumber() { return number_; }

      // optional .Person.PhoneType type = 2 [default = HOME];
      private boolean hasType;
      private A.Person.PhoneType type_ = A.Person.PhoneType.HOME;
      public boolean hasType() { return hasType; }
      public A.Person.PhoneType getType() { return type_; }

      public static A.Person.PhoneNumber parseFrom(
          com.google.protobuf.ByteString data)
          throws com.google.protobuf.InvalidProtocolBufferException {
        return newBuilder().mergeFrom(data).buildParsed();
      }
      public static A.Person.PhoneNumber parseFrom(
          com.google.protobuf.ByteString data,
          com.google.protobuf.ExtensionRegistry extensionRegistry)
          throws com.google.protobuf.InvalidProtocolBufferException {
        return newBuilder().mergeFrom(data, extensionRegistry)
                 .buildParsed();
      }
      public static A.Person.PhoneNumber parseFrom(byte[] data)
          throws com.google.protobuf.InvalidProtocolBufferException {
        return newBuilder().mergeFrom(data).buildParsed();
      }
      public static A.Person.PhoneNumber parseFrom(
          byte[] data,
          com.google.protobuf.ExtensionRegistry extensionRegistry)
          throws com.google.protobuf.InvalidProtocolBufferException {
        return newBuilder().mergeFrom(data, extensionRegistry)
                 .buildParsed();
      }
      public static A.Person.PhoneNumber parseFrom(java.io.InputStream
input)
          throws java.io.IOException {
        return newBuilder().mergeFrom(input).buildParsed();
      }
      public static A.Person.PhoneNumber parseFrom(
          java.io.InputStream input,
          com.google.protobuf.ExtensionRegistry extensionRegistry)
          throws java.io.IOException {
        return newBuilder().mergeFrom(input, extensionRegistry)
                 .buildParsed();
      }
      public static A.Person.PhoneNumber parseFrom(
          com.google.protobuf.CodedInputStream input)
          throws java.io.IOException {
        return newBuilder().mergeFrom(input).buildParsed();
      }
      public static A.Person.PhoneNumber parseFrom(
          com.google.protobuf.CodedInputStream input,
          com.google.protobuf.ExtensionRegistry extensionRegistry)
          throws java.io.IOException {
        return newBuilder().mergeFrom(input, extensionRegistry)
                 .buildParsed();
      }

      public static Builder newBuilder() { return new Builder(); }
      public Builder newBuilderForType() { return new Builder(); }
      public static Builder newBuilder(A.Person.PhoneNumber prototype) {
        return new Builder().mergeFrom(prototype);
      }

      public static final class Builder extends
          com.google.protobuf.GeneratedMessage.Builder<Builder> {
        // Construct using A.Person.PhoneNumber.newBuilder()
        private Builder() {}

        A.Person.PhoneNumber result = new A.Person.PhoneNumber();

        protected A.Person.PhoneNumber internalGetResult() {
          return result;
        }

        public Builder clear() {
          result = new A.Person.PhoneNumber();
          return this;
        }

        public Builder clone() {
          return new Builder().mergeFrom(result);
        }

        public com.google.protobuf.Descriptors.Descriptor
            getDescriptorForType() {
          return A.Person.PhoneNumber.getDescriptor();
        }

        public A.Person.PhoneNumber getDefaultInstanceForType() {
          return A.Person.PhoneNumber.getDefaultInstance();
        }

        public A.Person.PhoneNumber build() {
          if (!isInitialized()) {
            throw new com.google.protobuf.UninitializedMessageException(
              result);
          }
          return buildPartial();
        }

        private A.Person.PhoneNumber buildParsed()
            throws com.google.protobuf.InvalidProtocolBufferException {
          if (!isInitialized()) {
            throw new com.google.protobuf.UninitializedMessageException(
              result).asInvalidProtocolBufferException();
          }
          return buildPartial();
        }

        public A.Person.PhoneNumber buildPartial() {
          A.Person.PhoneNumber returnMe = result;
          result = null;
          return returnMe;
        }


        // required string number = 1;
        public boolean hasNumber() {
          return result.hasNumber();
        }
        public java.lang.String getNumber() {
          return result.getNumber();
        }
        public Builder setNumber(java.lang.String value) {
          result.hasNumber = true;
          result.number_ = value;
          return this;
        }
        public Builder clearNumber() {
          result.hasNumber = false;
          result.number_ = "";
          return this;
        }

        // optional .Person.PhoneType type = 2 [default = HOME];
        public boolean hasType() {
          return result.hasType();
        }
        public A.Person.PhoneType getType() {
          return result.getType();
        }
        public Builder setType(A.Person.PhoneType value) {
          result.hasType = true;
          result.type_ = value;
          return this;
        }
        public Builder clearType() {
          result.hasType = false;
          result.type_ = A.Person.PhoneType.HOME;
          return this;
        }
      }
    }

    // required string name = 1;
    private boolean hasName;
    private java.lang.String name_ = "";
    public boolean hasName() { return hasName; }
    public java.lang.String getName() { return name_; }

    // required int32 id = 2;
    private boolean hasId;
    private int id_ = 0;
    public boolean hasId() { return hasId; }
    public int getId() { return id_; }

    // optional string email = 3;
    private boolean hasEmail;
    private java.lang.String email_ = "";
    public boolean hasEmail() { return hasEmail; }
    public java.lang.String getEmail() { return email_; }

    // repeated .Person.PhoneNumber phone = 4;
    private java.util.List<A.Person.PhoneNumber> phone_ =
      java.util.Collections.emptyList();
    public java.util.List<A.Person.PhoneNumber> getPhoneList() {
      return phone_;
    }
    public int getPhoneCount() { return phone_.size(); }
    public A.Person.PhoneNumber getPhone(int index) {
      return phone_.get(index);
    }

    public static A.Person parseFrom(
        com.google.protobuf.ByteString data)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return newBuilder().mergeFrom(data).buildParsed();
    }
    public static A.Person parseFrom(
        com.google.protobuf.ByteString data,
        com.google.protobuf.ExtensionRegistry extensionRegistry)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return newBuilder().mergeFrom(data, extensionRegistry)
               .buildParsed();
    }
    public static A.Person parseFrom(byte[] data)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return newBuilder().mergeFrom(data).buildParsed();
    }
    public static A.Person parseFrom(
        byte[] data,
        com.google.protobuf.ExtensionRegistry extensionRegistry)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return newBuilder().mergeFrom(data, extensionRegistry)
               .buildParsed();
    }
    public static A.Person parseFrom(java.io.InputStream input)
        throws java.io.IOException {
      return newBuilder().mergeFrom(input).buildParsed();
    }
    public static A.Person parseFrom(
        java.io.InputStream input,
        com.google.protobuf.ExtensionRegistry extensionRegistry)
        throws java.io.IOException {
      return newBuilder().mergeFrom(input, extensionRegistry)
               .buildParsed();
    }
    public static A.Person parseFrom(
        com.google.protobuf.CodedInputStream input)
        throws java.io.IOException {
      return newBuilder().mergeFrom(input).buildParsed();
    }
    public static A.Person parseFrom(
        com.google.protobuf.CodedInputStream input,
        com.google.protobuf.ExtensionRegistry extensionRegistry)
        throws java.io.IOException {
      return newBuilder().mergeFrom(input, extensionRegistry)
               .buildParsed();
    }

    public static Builder newBuilder() { return new Builder(); }
    public Builder newBuilderForType() { return new Builder(); }
    public static Builder newBuilder(A.Person prototype) {
      return new Builder().mergeFrom(prototype);
    }

    public static final class Builder extends
        com.google.protobuf.GeneratedMessage.Builder<Builder> {
      // Construct using A.Person.newBuilder()
      private Builder() {}

      A.Person result = new A.Person();

      protected A.Person internalGetResult() {
        return result;
      }

      public Builder clear() {
        result = new A.Person();
        return this;
      }

      public Builder clone() {
        return new Builder().mergeFrom(result);
      }

      public com.google.protobuf.Descriptors.Descriptor
          getDescriptorForType() {
        return A.Person.getDescriptor();
      }

      public A.Person getDefaultInstanceForType() {
        return A.Person.getDefaultInstance();
      }

      public A.Person build() {
        if (!isInitialized()) {
          throw new com.google.protobuf.UninitializedMessageException(
            result);
        }
        return buildPartial();
      }

      private A.Person buildParsed()
          throws com.google.protobuf.InvalidProtocolBufferException {
        if (!isInitialized()) {
          throw new com.google.protobuf.UninitializedMessageException(
            result).asInvalidProtocolBufferException();
        }
        return buildPartial();
      }

      public A.Person buildPartial() {
        if (result.phone_ != java.util.Collections.EMPTY_LIST) {
          result.phone_ =
            java.util.Collections.unmodifiableList(result.phone_);
        }
        A.Person returnMe = result;
        result = null;
        return returnMe;
      }


      // required string name = 1;
      public boolean hasName() {
        return result.hasName();
      }
      public java.lang.String getName() {
        return result.getName();
      }
      public Builder setName(java.lang.String value) {
        result.hasName = true;
        result.name_ = value;
        return this;
      }
      public Builder clearName() {
        result.hasName = false;
        result.name_ = "";
        return this;
      }

      // required int32 id = 2;
      public boolean hasId() {
        return result.hasId();
      }
      public int getId() {
        return result.getId();
      }
      public Builder setId(int value) {
        result.hasId = true;
        result.id_ = value;
        return this;
      }
      public Builder clearId() {
        result.hasId = false;
        result.id_ = 0;
        return this;
      }

      // optional string email = 3;
      public boolean hasEmail() {
        return result.hasEmail();
      }
      public java.lang.String getEmail() {
        return result.getEmail();
      }
      public Builder setEmail(java.lang.String value) {
        result.hasEmail = true;
        result.email_ = value;
        return this;
      }
      public Builder clearEmail() {
        result.hasEmail = false;
        result.email_ = "";
        return this;
      }

      // repeated .Person.PhoneNumber phone = 4;
      public java.util.List<A.Person.PhoneNumber> getPhoneList() {
        return java.util.Collections.unmodifiableList(result.phone_);
      }
      public int getPhoneCount() {
        return result.getPhoneCount();
      }
      public A.Person.PhoneNumber getPhone(int index) {
        return result.getPhone(index);
      }
      public Builder setPhone(int index, A.Person.PhoneNumber value) {
        result.phone_.set(index, value);
        return this;
      }
      public Builder setPhone(int index, A.Person.PhoneNumber.Builder
builderForValue) {
        result.phone_.set(index, builderForValue.build());
        return this;
      }
      public Builder addPhone(A.Person.PhoneNumber value) {
        if (result.phone_.isEmpty()) {
          result.phone_ = new java.util.ArrayList<A.Person.PhoneNumber>();
        }
        result.phone_.add(value);
        return this;
      }
      public Builder addPhone(A.Person.PhoneNumber.Builder
builderForValue) {
        if (result.phone_.isEmpty()) {
          result.phone_ = new java.util.ArrayList<A.Person.PhoneNumber>();
        }
        result.phone_.add(builderForValue.build());
        return this;
      }
      public Builder addAllPhone(
          java.lang.Iterable<? extends A.Person.PhoneNumber> values) {
        if (result.phone_.isEmpty()) {
          result.phone_ = new java.util.ArrayList<A.Person.PhoneNumber>();
        }
        super.addAll(values, result.phone_);
        return this;
      }
      public Builder clearPhone() {
        result.phone_ = java.util.Collections.emptyList();
        return this;
      }
    }
  }

  private static final com.google.protobuf.Descriptors.Descriptor
    internal_static_Person_descriptor =
      getDescriptor().getMessageTypes().get(0);
  private static
    com.google.protobuf.GeneratedMessage.FieldAccessorTable
      internal_static_Person_fieldAccessorTable = new
        com.google.protobuf.GeneratedMessage.FieldAccessorTable(
          internal_static_Person_descriptor,
          new java.lang.String[] { "Name", "Id", "Email", "Phone", },
          A.Person.class,
          A.Person.Builder.class);
  private static final com.google.protobuf.Descriptors.Descriptor
    internal_static_Person_PhoneNumber_descriptor =
      internal_static_Person_descriptor.getNestedTypes().get(0);
  private static
    com.google.protobuf.GeneratedMessage.FieldAccessorTable
      internal_static_Person_PhoneNumber_fieldAccessorTable = new
        com.google.protobuf.GeneratedMessage.FieldAccessorTable(
          internal_static_Person_PhoneNumber_descriptor,
          new java.lang.String[] { "Number", "Type", },
          A.Person.PhoneNumber.class,
          A.Person.PhoneNumber.Builder.class);
}