Windows + Java 7 + Maven 2系でgRPCを開発する

## Windows + Java 7 + Maven 2系

世の中には最新だけじゃダメなこともあるんです。例えばIEとかIEとかIEとか。
そんなわけで、古い環境で頑張って開発しなければならない境遇の方への参考になればと思います。
[gRPC](https://grpc.io/)自体の説明は他の方が詳しく書いてくれているので探してみて下さい。

ざっと今回の環境。いろいろ古い。
* Windows 10 64bit
* Oracle JDK 1.7.0_75 32bit
* Apache Maven 2.2.1
* Eclipse 4.3.2 Kepler SR2

## まずはイマドキのやり方でおさらい

以下を参考にしながら。
https://grpc.io/docs/tutorials/basic/java/
https://github.com/grpc/grpc-java/blob/master/examples/README.md

そんな余裕無いって方はここは読み飛ばしてください。

いつも通りDockerで環境をサクッと作りましょう。

```console
$ docker run --rm -it --name grpc-java -p 50051:50051 ubuntu
root@e7b3b2c13c0b:/# apt-get update
root@e7b3b2c13c0b:/# apt-get install -y git openjdk-8-jdk maven
root@e7b3b2c13c0b:/# git clone -b v1.22.1 https://github.com/grpc/grpc-java
root@e7b3b2c13c0b:/# cd grpc-java/examples
root@e7b3b2c13c0b:/grpc-java/examples# ./gradlew installDist
```

ちなみに`tools.jar`を使うため、`jre`じゃダメよ。
jreでやった場合は下記のエラーになります。

```console
> Task :compileJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Could not find tools.jar. Please check that /usr/lib/jvm/java-8-openjdk-amd64 contains a valid JDK installation.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 43s
```

ここまでで環境の準備は出来ています。
HelloWorldのサンプルは以下にあります。

> /grpc-java/examples/src/main/proto/helloworld.proto
/grpc-java/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java
/grpc-java/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java

`/grpc-java/examples`配下でビルドして実行してみましょう。

```console
root@e7b3b2c13c0b:~# cd /grpc-java/examples
root@e7b3b2c13c0b:/grpc-java/examples# mvn clean compile
root@e7b3b2c13c0b:/grpc-java/examples# mvn exec:java -Dexec.mainClass=io.grpc.examples.helloworld.HelloWorldServer &
[1] 5817
root@e7b3b2c13c0b:/grpc-java/examples# 
root@e7b3b2c13c0b:/grpc-java/examples# mvn exec:java -Dexec.mainClass=io.grpc.examples.helloworld.HelloWorldClient
```

`Greeting: Hello world`が表示されれば完了ですね。
今回はサーバーとクライアントを同じ端末で実行しています。
サーバーを終了したいときは下記。(`fg` -> `Ctrl + C`)

```console
root@e7b3b2c13c0b:/grpc-java/examples# fg
mvn exec:java -Dexec.mainClass=io.grpc.examples.helloworld.HelloWorldServer
^C*** shutting down gRPC server since JVM is shutting down
*** server shut down
root@e7b3b2c13c0b:/grpc-java/examples#
```

protoファイルを変更した場合は、`./gradlew installDist`を実行すればJavaコードが生成されます。
Javaの実行オプションを変えたい場合は、`MAVEN_OPTS`を設定すればOKです。

```console
MAVEN_OPTS="-server -Xmx1024m -Xms1024m -Xloggc:/grpc-java/examples/GC.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xverify:none -Djava.security.egd=file:/dev/./urandom -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSClassUnloadingEnabled -XX:+DisableExplicitGC -XX:+PrintTenuringDistribution -XX:-Inline"
export MAVEN_OPTS
```

## Windowsの場合

さて、いよいよ本題です。WindowsでDockerもGradleも使用禁止の縛りプレイです。
純粋なgRPCはJava 7以上となっていますが、サンプルではGradleを使うし、Maven3以上が必須（プラグインを使うため）になっています。

まず、Maven プロジェクトを作りましょう。今回必要な依存関係は下記。(ログはお好みで)

```xml:pom.xml
	<dependencies>
		<dependency>
			<groupId>io.grpc</groupId>
			<artifactId>grpc-all</artifactId>
			<version>1.22.1</version>
		</dependency>
		<dependency>
			<groupId>com.google.protobuf</groupId>
			<artifactId>protobuf-java</artifactId>
			<version>3.9.0</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.12</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.1</version>
		</dependency>
	</dependencies>
```

プロジェクト直下に`proto`フォルダを作成し、protoファイルを作成します。

```proto:grpc.proto
syntax = "proto3";

option java_multiple_files = true;
option java_package = "org.mygrpc.helloworld";
option java_outer_classname = "GrpcProto";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
```

protoファイルからJavaソースを生成します。
[protobuf](https://github.com/protocolbuffers/protobuf/)のWindows用ファイルをダウンロードします。
https://github.com/protocolbuffers/protobuf/releases/download/v3.9.0/protoc-3.9.0-win64.zip

解凍し、`bin`フォルダ配下にある`protoc.exe`をパスが通った場所に置きます。新たにどこかに置いてパスを通しても良いです。
今後フルパスで実行するなら、別にパスは通さなくても良いです。

コマンドプロンプトで先ほど作成した、`grpc.proto`があるフォルダで下記を実行。

```console
protoc --java_out=..\src\main\java grpc.proto
```

`org.mygrpc.helloworld`配下にソースが生成されました。

```
.
│  pom.xml
│
├─proto
│      grpc.proto
│
└─src
   ├─main
   │  ├─java
   │  │  └─org
   │  │      └─mygrpc
   │  │          └─helloworld
   │  │                  GrpcProto.java
   │  │                  HelloReply.java
   │  │                  HelloReplyOrBuilder.java
   │  │                  HelloRequest.java
   │  │                  HelloRequestOrBuilder.java
   │  │
   │  └─resources
   │          log4j.dtd
   │          log4j.xml
   │
   └─test
       ├─java
       └─resources
```

実はこれだけでは足りません。もう１つ生成するためにツールをダウンロードします。
https://search.maven.org/search?q=g:io.grpc%20a:protoc-gen-grpc-java
※Downloadのリンクからexeを落とすべし。

ダウンロードしたexeを任意のフォルダに配置し、以下を実行(パスは上記のexeを配置したフォルダに変更してください)

```
protoc --plugin=protoc-gen-grpc-java=C:\protoc\protoc-gen-grpc-java-1.22.1-windows-x86_64.exe --grpc-java_out=..\src\main\java grpc.proto
```

`GreeterGrpc.java`が作成されたと思います。

サンプルを参考にサーバーとクライアントの処理を作成します。

```java:HelloWorldServer.java
package org.mygrpc.helloworld;

import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;

import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class HelloWorldServer {
    /** Log. */
    protected static Log log = LogFactory.getLog(HelloWorldServer.class);

    private Server server;

    private void start() throws IOException {
      int port = 50051;
      server = ServerBuilder.forPort(port)
          .addService(new GreeterImpl())
          .build()
          .start();
      log.info("Server started, listening on " + port);
      Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
          // Use stderr here since the logger may have been reset by its JVM shutdown hook.
          System.err.println("*** shutting down gRPC server since JVM is shutting down");
          HelloWorldServer.this.stop();
          System.err.println("*** server shut down");
        }
      });
    }

    private void stop() {
      if (server != null) {
        server.shutdown();
      }
    }

    /**
     * Await termination on the main thread since the grpc library uses daemon threads.
     */
    private void blockUntilShutdown() throws InterruptedException {
      if (server != null) {
        server.awaitTermination();
      }
    }

    /**
     * Main launches the server from the command line.
     */
    public static void main(String[] args) throws IOException, InterruptedException {
      final HelloWorldServer server = new HelloWorldServer();
      server.start();
      server.blockUntilShutdown();
    }

    static class GreeterImpl extends GreeterGrpc.GreeterImplBase {

      @Override
      public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
        HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
      }
    }
}
```


```java:HelloWorldClient.java
package org.mygrpc.helloworld;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;

import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class HelloWorldClient {
    /** Log. */
    protected static Log log = LogFactory.getLog(HelloWorldClient.class);

    public static void main(String[] args) throws Exception {
        ManagedChannel channel = ManagedChannelBuilder
                .forAddress("localhost", 50051).usePlaintext().build();
        GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc
                .newBlockingStub(channel);

        try {
            HelloRequest request = HelloRequest.newBuilder().setName("World").build();
            try {
                HelloReply resp = blockingStub.sayHello(request);
                log.info(resp.getMessage());
            } catch (StatusRuntimeException e) {
                log.warn("RPC failed: " + e.getStatus());
                return;
            }
        } finally {
            channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
        }
    }
}
```

サーバーとクライアントをそれぞれ実行して`Hello World`と出力されればOK。
これでやっと開発できるようになりました。

ちなみに、protoファイル編集用のプラグインとして以下があるようですが、バージョン2.3.0以上はEclipse 4.6 Neon以上が必要とのことで諦めました。
https://github.com/google/protobuf-dt