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 .
- プロトコルファイル - a.proto
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); }