Gradle で Protocol Buffers を使う - Java
Gradle を使って Protocol Buffers の protoc で Java ソースコードを生成し、ビルドしてみます。
- Gradle 3.0
- protoc-jar 3.0.0
- protobuf-java 3.0.0
Gradle から protoc コマンドを呼び出す方法もありますが、今回は protoc-jar を使いました。
protoc-jar を使うと、プラットフォームの環境に応じた protoc コマンドを TEMP ディレクトリへ一時的に生成して実行してくれます。
今回作成したソースは http://github.com/fits/try_samples/tree/master/blog/20160829/
proto ファイル
今回は以下の proto ファイル(version 3)を使います。
Protocol Buffers では、proto ファイルを protoc コマンドで処理する事で任意のプログラム言語のソースコードを自動生成します。
java_package
と java_outer_classname
オプションで Java ソースコードを生成した際のパッケージ名とクラス名をそれぞれ指定できます。
proto/address-book.proto (.proto ファイル)
syntax = "proto3"; package sample; option java_package = "sample.model"; option java_outer_classname = "AddressBookProtos"; message Person { string name = 1; int32 id = 2; string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phone = 4; } message AddressBook { repeated Person person = 1; }
Gradle ビルド定義
compileJava タスクの実行前に com.github.os72.protocjar.Protoc
を実行して src/main/protoc-generated へソースを自動生成する protoc タスクを定義しました。
protoc-jar モジュールをクラスパスへ指定するため protoc 用の configurations
を定義しています。
なお、今回は Java 用のソースコードを生成するため --java_out
オプションを使っています。
build.gradle
apply plugin: 'application' // protoc によるソースの自動生成先 def protoDestDir = 'src/main/protoc-generated' // proto ファイル名 def protoFile = 'proto/address-book.proto' mainClassName = 'SampleApp' repositories { jcenter() } configurations { protoc } dependencies { protoc 'com.github.os72:protoc-jar:3.0.0' compileOnly 'org.projectlombok:lombok:1.16.10' compile 'com.google.protobuf:protobuf-java:3.0.0' } // protoc の実行タスク task protoc << { mkdir(protoDestDir) // protoc の実行 javaexec { main = 'com.github.os72.protocjar.Protoc' classpath = configurations.protoc args = [ protoFile, "--java_out=${protoDestDir}" ] } } compileJava { dependsOn protoc source protoDestDir } clean { delete protoDestDir }
サンプルアプリケーション
protoc で自動生成したクラスを動作確認するための簡単なサンプルを用意しました。
src/main/java/SampleApp.java
import static sample.model.AddressBookProtos.Person.PhoneType.*; import lombok.val; import java.io.ByteArrayOutputStream; import sample.model.AddressBookProtos.Person; import sample.model.AddressBookProtos.Person.PhoneNumber; class SampleApp { public static void main(String... args) throws Exception { val phone = PhoneNumber.newBuilder() .setNumber("000-1234-5678") .setType(HOME) .build(); val person = Person.newBuilder() .setName("sample1") .addPhone(phone) .build(); System.out.println(person); try (val output = new ByteArrayOutputStream()) { // シリアライズ処理(バイト配列化) person.writeTo(output); System.out.println("----------"); // デシリアライズ処理(バイト配列から復元) val restoredPerson = Person.newBuilder() .mergeFrom(output.toByteArray()) .build(); System.out.println(restoredPerson); } } }
ビルドと実行
ビルドと実行の結果は以下の通りです。
実行結果
> gradle run :protoc protoc-jar: protoc version: 300, detected platform: windows 10/amd64 protoc-jar: executing: [・・・\Local\Temp\protoc3178938487369694690.exe, proto/address-book.proto, --java_out=src/main/protoc-generated] :compileJava ・・・ :run name: "sample1" phone { number: "000-1234-5678" type: HOME } ---------- name: "sample1" phone { number: "000-1234-5678" type: HOME } BUILD SUCCESSFUL
なお、protoc で以下のようなコードが生成されました。
src/main/protoc-generated/sample/model/AddressBookProtos.java
package sample.model; public final class AddressBookProtos { private AddressBookProtos() {} ・・・ public static final class Person extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:sample.Person) PersonOrBuilder { ・・・ public enum PhoneType implements com.google.protobuf.ProtocolMessageEnum { /** * <code>MOBILE = 0;</code> */ MOBILE(0), /** * <code>HOME = 1;</code> */ HOME(1), /** * <code>WORK = 2;</code> */ WORK(2), UNRECOGNIZED(-1), ; ・・・ } ・・・ public static final class PhoneNumber extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:sample.Person.PhoneNumber) PhoneNumberOrBuilder { ・・・ } ・・・ } ・・・ }