Initial commit of sample java grpc server
diff --git a/BookingService.java b/BookingService.java
new file mode 100644
index 0000000..5124027
--- /dev/null
+++ b/BookingService.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2018, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package ext.maps.booking.partner.v2;
+
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.AvailabilityUpdate;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.Booking;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.BookingStatus;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.CheckAvailabilityRequest;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.CheckAvailabilityResponse;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.CheckAvailabilityResponse.DurationRequirement;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.CreateBookingRequest;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.CreateBookingResponse;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.GetBookingStatusRequest;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.GetBookingStatusResponse;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.ListBookingsRequest;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.ListBookingsResponse;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.PrepaymentStatus;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.Slot;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.UpdateBookingRequest;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.UpdateBookingResponse;
+import ext.maps.booking.partner.v2.ApitemplateV2BookingService.UserPaymentOption;
+import grpc.health.v1.Health.HealthImpl;
+import io.grpc.Grpc;
+import io.grpc.Metadata;
+import io.grpc.Server;
+import io.grpc.ServerCall;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import io.grpc.ServerInterceptors;
+import io.grpc.Status;
+import io.grpc.netty.GrpcSslContexts;
+import io.grpc.netty.NettyServerBuilder;
+import io.grpc.stub.StreamObserver;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslProvider;
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import org.bouncycastle.asn1.x500.RDN;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x500.style.IETFUtils;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+
+/** Booking Server with TLS enabled */
+public class BookingService {
+ private static final Logger logger = Logger.getLogger(BookingService.class.getName());
+
+ private Server server;
+
+ private final String host;
+ private final int port;
+ private final String certChainFilePath;
+ private final String privateKeyFilePath;
+ private final String trustedrootsFilePath;
+ private final Set<String> acceptedCNs;
+
+ public BookingService(
+ String host,
+ int port,
+ String certChainFilePath,
+ String privateKeyFilePath,
+ String trustedrootsFilePath,
+ Set<String> acceptedCNs) {
+ this.host = host;
+ this.port = port;
+ this.certChainFilePath = certChainFilePath;
+ this.privateKeyFilePath = privateKeyFilePath;
+ this.trustedrootsFilePath = trustedrootsFilePath;
+ this.acceptedCNs = acceptedCNs;
+ }
+
+ private SslContextBuilder getSslContextBuilder() {
+ SslContextBuilder sslClientContextBuilder =
+ SslContextBuilder.forServer(new File(certChainFilePath), new File(privateKeyFilePath));
+ if (trustedrootsFilePath != null) {
+ sslClientContextBuilder.trustManager(new File(trustedrootsFilePath));
+ sslClientContextBuilder.clientAuth(ClientAuth.OPTIONAL);
+ }
+ return GrpcSslContexts.configure(sslClientContextBuilder, SslProvider.OPENSSL);
+ }
+
+ private void start() throws IOException {
+
+ server =
+ NettyServerBuilder.forAddress(new InetSocketAddress(host, port))
+ .addService(ServerInterceptors.intercept(new BookingServiceImpl(), new MyInterceptor()))
+ .addService(new HealthImpl())
+ .sslContext(getSslContextBuilder().build())
+ .build()
+ .start();
+
+ logger.info("Booking 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");
+ BookingService.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. */
+ public static void main(String[] args) throws IOException, InterruptedException {
+ // Set the accepted cns of the server
+ Set<String> acceptedCNs = new HashSet<String>();
+ acceptedCNs.add("mapsbooking.businesslink-3.net");
+
+ // TODO(partner): override your host name, port, server_cert_chain file path,
+ // private_key file path and trusted_client_roots file path here to build the server
+ final BookingService server =
+ new BookingService(
+ "localhost",
+ 8443,
+ "src/main/certificates/server_cert_chain.pem",
+ "src/main/certificates/server_private_key.pem",
+ "src/main/certificates/trusted_client_roots.pem",
+ acceptedCNs);
+ server.start();
+ server.blockUntilShutdown();
+ }
+
+ /**
+ * Implement the server interceptor to do peer certificate validation that checks for a specific
+ * CN in the subject name, if the CN check fails, the connection will be closed with
+ * "PERMISSION_DENIED" status message.
+ */
+ private class MyInterceptor implements ServerInterceptor {
+ @Override
+ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
+ ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
+ SSLSession sslSession = call.getAttributes().get(Grpc.TRANSPORT_ATTR_SSL_SESSION);
+ try {
+ Certificate[] peerCerts = sslSession.getPeerCertificates();
+ if (peerCerts[0] instanceof java.security.cert.X509Certificate) {
+ X509Certificate peerCert = (X509Certificate) peerCerts[0];
+ X500Name x500Name = new JcaX509CertificateHolder(peerCert).getSubject();
+ RDN cn = x500Name.getRDNs(BCStyle.CN)[0];
+ String peerCN = IETFUtils.valueToString(cn.getFirst().getValue());
+ if (!acceptedCNs.contains(peerCN)) {
+ throw new SSLPeerUnverifiedException("Invalid CN");
+ }
+ } else {
+ throw new SSLPeerUnverifiedException("Invalid certificate type");
+ }
+ } catch (SSLPeerUnverifiedException se) {
+ System.err.println(se.getMessage());
+ call.close(Status.PERMISSION_DENIED, headers);
+ return new ServerCall.Listener<ReqT>() {};
+ } catch (CertificateEncodingException ce) {
+ System.err.println(ce.getMessage());
+ call.close(Status.PERMISSION_DENIED, headers);
+ return new ServerCall.Listener<ReqT>() {};
+ }
+ return next.startCall(call, headers);
+ }
+ }
+
+ /** The implementation of Booking Service. */
+ static class BookingServiceImpl extends BookingServiceGrpc.BookingServiceImplBase {
+ @Override
+ public void checkAvailability(
+ CheckAvailabilityRequest request,
+ StreamObserver<CheckAvailabilityResponse> responseObserver) {
+
+ Slot requestedSlot = request.getSlot();
+
+ // TODO(partner): perform availability check
+ //
+ // Error conditions: response with corresponding canonical gRPC error code
+ // example:
+ // responseObserver.onError(
+ // new StatusException(
+ // io.grpc.Status.INVALID_ARGUMENT.withDescription("Invalid merchant id")
+ // )
+ // );
+ //
+ // Happy path: implement the helper functions, populate the response
+
+ CheckAvailabilityResponse response =
+ CheckAvailabilityResponse.newBuilder()
+ .setSlot(requestedSlot)
+ .setCountAvailable(getAvailableSpots(requestedSlot))
+ .setDurationRequirement(getDurationRequirement(requestedSlot)) // optional
+ .setAvailabilityUpdate(getAvailabilityUpdate(requestedSlot)) // optional
+ .build();
+
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+
+ private int getAvailableSpots(Slot slot) {
+ // TODO(partner): get the available spots for the given slot
+ //
+ // int available_spots = ...
+ // return available_spots;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ private DurationRequirement getDurationRequirement(Slot slot) {
+ // TODO(partner): optional, get the duration requirment for the given slot
+ //
+ // DurationRequirement durationRequirement = ...
+ // return durationRequirement;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ private AvailabilityUpdate getAvailabilityUpdate(Slot slot) {
+ // TODO(partner): optional, get the availability update for the given slot
+ //
+ // AvailabilityUpdate availabilityUpdate = ...
+ // return availabilityUpdate;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ @Override
+ public void createBooking(
+ CreateBookingRequest request, StreamObserver<CreateBookingResponse> responseObserver) {
+
+ // TODO(partner): create a booking based on the request
+ //
+ // Error conditions:
+ // unexpected error -> response with corresponding canonical gRPC error code
+ // example:
+ // responseObserver.onError(
+ // new StatusException(
+ // io.grpc.Status.INVALID_ARGUMENT.withDescription("Invalid merchant id")
+ // )
+ // );
+ // business logic error -> set the BookingFailure in the response
+ // example:
+ // BookingFailure bookingFailure = BookingFailure.newBuilder()
+ // .setCause(Cause.SLOT_UNAVAILABLE)
+ // .setDescription("slot is not available now")
+ // .build();
+ // CreateBookingResponse response = CreateBookingResponse.newBuilder()
+ // .setBookingFailure(bookingFailure)...
+ //
+ // Happy path: implement the helper functions, populate the response
+
+ Booking booking = createSuccessfulBooking(request);
+
+ CreateBookingResponse response =
+ CreateBookingResponse.newBuilder()
+ .setBooking(booking)
+ .setUserPaymentOption(getUserPaymentOption(booking)) // optional
+ .build();
+
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+
+ private Booking createSuccessfulBooking(CreateBookingRequest request) {
+ // TODO(partner): create a booking based on the create booking request and return the
+ // created booking instance
+ //
+ // Booking created = ...(e.g.
+ // Booking.newBuilder()
+ // .setBookingId("sampleBooking1")
+ // .setSlot(request.getSlot())
+ // .setUserInformation(request.getUserInformation())
+ // .setStatus(BookingStatus.CONFIRMED)
+ // .setPaymentInformation(request.getPaymentInformation())
+ // .build()
+ // )
+ // return created;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ private UserPaymentOption getUserPaymentOption(Booking booking) {
+ // TODO(partner): optional, get the user payment option for the given booking
+ //
+ // UserPaymentOption paymentOption = ...
+ // return paymentOption;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ @Override
+ public void updateBooking(
+ UpdateBookingRequest request, StreamObserver<UpdateBookingResponse> responseObserver) {
+
+ // TODO(partner): update the booking based on the request
+ //
+ // Error conditions:
+ // unexpected error -> response with corresponding canonical gRPC error code
+ // example:
+ // responseObserver.onError(
+ // new StatusException(
+ // io.grpc.Status.NOT_FOUND.withDescription("Unknown booking id")
+ // )
+ // );
+ // business logic error -> set the BookingFailure in the response
+ // example:
+ // BookingFailure bookingFailure = BookingFailure.newBuilder()
+ // .setCause(Cause.SLOT_UNAVAILABLE)
+ // .setDescription("slot is not available now")
+ // .build();
+ // CreateBookingResponse response = CreateBookingResponse.newBuilder()
+ // .setBookingFailure(bookingFailure)...
+ //
+ // Happy path: implement the helper functions, populate the response
+
+ Booking booking = updateSuccessfulBooking(request);
+
+ UpdateBookingResponse response =
+ UpdateBookingResponse.newBuilder()
+ .setBooking(booking)
+ .setUserPaymentOption(getUserPaymentOption(booking)) // option
+ .build();
+
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+
+ private Booking updateSuccessfulBooking(UpdateBookingRequest request) {
+ // TODO(partner): look up the booking with bookingId, update fields according to
+ // updateMask and return the updated booking instance
+ //
+ // String bookingId = request.getBooking().getBookingId();
+ // FieldMask updateMask = request.getUpdateMask();
+ // Booking updated = ...
+ // return updated;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ @Override
+ public void getBookingStatus(
+ GetBookingStatusRequest request,
+ StreamObserver<GetBookingStatusResponse> responseObserver) {
+
+ String bookingId = request.getBookingId();
+
+ // TODO(partner): look up the booking status and prepayment status with bookingId
+ //
+ // Error conditions:
+ // unexpected error -> response with corresponding canonical gRPC error code
+ // example:
+ // responseObserver.onError(
+ // new StatusException(
+ // io.grpc.Status.NOT_FOUND.withDescription("Unknown booking id")
+ // )
+ // );
+ //
+ // Happy path: implement the helper functions, populate the response
+
+ GetBookingStatusResponse response =
+ GetBookingStatusResponse.newBuilder()
+ .setBookingId(bookingId)
+ .setBookingStatus(getBookingStatus(bookingId))
+ .setPrepaymentStatus(getPrepaymentStatus(bookingId))
+ .build();
+
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+
+ private BookingStatus getBookingStatus(String bookingId) {
+ // TODO(partner): get the booking status for the given bookingId
+ //
+ // BookingStatus bookingStatus = ...
+ // return bookingStatus;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ private PrepaymentStatus getPrepaymentStatus(String bookingId) {
+ // TODO(partner): get the prepayment status for the given bookingId
+ //
+ // Prepayment prepaymentStatus = ...
+ // return prepaymentStatus;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ @Override
+ public void listBookings(
+ ListBookingsRequest request, StreamObserver<ListBookingsResponse> responseObserver) {
+
+ String userId = request.getUserId();
+
+ // TODO(partner): look up all bookings with the given userId
+ //
+ // Error conditions:
+ // unexpected error -> response with corresponding canonical gRPC error code
+ // example:
+ // responseObserver.onError(
+ // new StatusException(
+ // io.grpc.Status.NOT_FOUND.withDescription("Unknown user id")
+ // )
+ // );
+ //
+ // Happy path: implement the helper function, populate the response
+
+ ListBookingsResponse response =
+ ListBookingsResponse.newBuilder().addAllBookings(getListBooking(userId)).build();
+
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+
+ private List<Booking> getListBooking(String userId) {
+ // TODO(partner): get the bookings list for the given userId
+ //
+ // List<Booking> bookings = ...
+ // return bookings;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+ }
+}
diff --git a/Health.java b/Health.java
new file mode 100644
index 0000000..81788b6
--- /dev/null
+++ b/Health.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2018, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package grpc.health.v1;
+
+import grpc.health.v1.HealthOuterClass.HealthCheckRequest;
+import grpc.health.v1.HealthOuterClass.HealthCheckResponse;
+import grpc.health.v1.HealthOuterClass.HealthCheckResponse.ServingStatus;
+import io.grpc.Status;
+import io.grpc.StatusException;
+import io.grpc.stub.StreamObserver;
+import java.util.logging.Logger;
+
+/** gRPC Health Checking Protocol */
+public class Health {
+ private static final Logger logger = Logger.getLogger(Health.class.getName());
+ private static final String ACCEPTED_SERVICE = "ext.maps.booking.partner.v2.BookingService";
+
+ /** The implementation of Health Checking Service. */
+ public static class HealthImpl extends HealthGrpc.HealthImplBase {
+ @Override
+ public void check(
+ HealthCheckRequest request, StreamObserver<HealthCheckResponse> responseObserver) {
+ String service = request.getService();
+ if (!service.equals(ACCEPTED_SERVICE)) {
+ responseObserver.onError(
+ new StatusException(Status.NOT_FOUND.withDescription("Unknown service")));
+ } else {
+ HealthCheckResponse response =
+ HealthCheckResponse.newBuilder().setStatus(getServingStatus()).build();
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+ }
+
+ private ServingStatus getServingStatus() {
+ // TODO(partner): check the health status of the service
+ //
+ // ServingStatus status = ... (e.g. ServingStatus.SERVING)
+ // return status;
+
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+ }
+}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6fefbd1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,163 @@
+# gRPC Server Skeleton for Java
+
+### Get Started
+
+1. Create a java gradle project (grpc-booking-service), under the src/main,
+ create a 'proto' directory.
+
+2. Download the [booking service
+ definition](https://developers.google.com/maps-booking/download/apitemplate.v2.booking_service.proto)
+ and [health checking
+ protocol](https://github.com/grpc/grpc/blob/master/src/proto/grpc/health/v1/health.proto),
+ place them under src/main/proto. These files define the gRPC methods and
+ messages for the Reserve with Google API and Health Check.
+
+3. Update the ***build.gradle*** file, add dependencies and the protobuf plugin
+ for Gradle. The introduction and guide for protobuf-gradle-plugin can be
+ found [here](https://github.com/google/protobuf-gradle-plugin).
+
+ apply plugin: 'java'
+ apply plugin: 'com.google.protobuf'
+
+ repositories {
+ mavenCentral()
+ }
+
+ // updating the version in our release process.
+ def grpcVersion = '1.8.0' // CURRENT_GRPC_VERSION
+ def nettyTcNativeVersion = '2.0.7.Final'
+
+ dependencies {
+ compile "com.google.api.grpc:proto-google-common-protos:0.1.9"
+ compile "io.grpc:grpc-netty:${grpcVersion}"
+ compile "io.grpc:grpc-protobuf:${grpcVersion}"
+ compile "io.grpc:grpc-stub:${grpcVersion}"
+ compile "io.netty:netty-tcnative-boringssl-static:${nettyTcNativeVersion}"
+ compile "org.bouncycastle:bcmail-jdk15:1.46"
+
+ testCompile "io.grpc:grpc-testing:${grpcVersion}"
+ testCompile "junit:junit:4.12"
+ testCompile "org.mockito:mockito-core:1.9.5"
+ }
+
+ buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ // ASSUMES GRADLE 2.12 OR HIGHER. Use plugin version 0.7.5 with earlier
+ // gradle versions
+ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
+ }
+ }
+
+ protobuf {
+ protoc {
+ artifact = 'com.google.protobuf:protoc:3.4.0'
+ }
+ plugins {
+ grpc {
+ artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
+ }
+ }
+ generateProtoTasks {
+ all()*.plugins {
+ grpc {}
+ }
+ }
+ }
+
+ // Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code.
+ sourceSets {
+ main {
+ java {
+ srcDirs 'build/generated/source/proto/main/grpc'
+ srcDirs 'build/generated/source/proto/main/java'
+ }
+ }
+ }
+
+ // Generate IntelliJ IDEA's .idea & .iml project files
+ apply plugin: 'idea'
+
+ // Provide convenience executables for trying out the examples.
+ apply plugin: 'application'
+
+ startScripts.enabled = false
+
+4. Run the following command to build the library and auto-generate code from
+ protoc build plugin:
+
+ ./gradlew build
+
+5. To enable TLS on the server, under ***src/main/certificates/*** the
+ following files are required:
+
+ * ***server_cert_chain.pem*** your server certificate chain in PEM format
+ * ***server_private_key.pem*** your private key for the server certificate
+ chain, needs to be the PKCS#8 private key
+ * ***trusted_client_roots.pem*** the root certificates that are trusted
+ when authenticating clients, you can choose to obtain this set of
+ trusted roots from an authority like
+ [Mozilla](https://wiki.mozilla.org/CA:IncludedCAs), or install the [set
+ of roots currently recommended by the Google Internet Authority
+ G2](https://pki.google.com/roots.pem). In the latter case, you may have
+ to manually update the root certificate at times
+
+6. Retrieve the sample code, place the **BookingService.java** under
+ *src/main/java/ext/maps/booking/partner/v2*, place **Health.java** under
+ *src/main/java/grpc/health/v1*. In both files, follow the ***TODOs*** to
+ complete your implementations.
+
+7. Update the gradle.build file to specify the generation of server executable
+ by adding the following code:
+
+ task bookingService(type: CreateStartScripts) {
+ mainClassName = 'ext.maps.booking.partner.v2.BookingService'
+ applicationName = 'booking-service'
+ outputDir = new File(project.buildDir, 'tmp')
+ classpath = jar.outputs.files + project.configurations.runtime
+ }
+
+ applicationDistribution.into('bin') {
+ from(bookingService)
+ fileMode = 0755
+ }
+
+8. Compile the server:
+
+ ./gradlew installDist
+
+9. Run the booking server:
+
+ ./build/install/grpc-booking-service/bin/booking-service
+
+### Final Directory Structure
+
+ src
+ |---main
+ |---certificates
+ |---server_cert_chain.pem
+ |---server_private_key.pem
+ |---trusted_client_roots.pem
+ |---java
+ |---ext.maps.booking.partner.v2.BookingService.java
+ |---grpc.health.v1.Health.java
+ |---proto
+ |---booking_service.proto
+ |---health.proto
+ |---test
+
+### Other Reference
+
+* For other building tools, visit the
+ [gRPC-java](https://grpc.io/docs/quickstart/java.html) and download the
+ example, check grpc-java/examples.
+
+ git clone -b v1.9.0 https://github.com/grpc/grpc-java
+
+* [gRPC java Transport Security
+ (TLS)](https://github.com/grpc/grpc-java/blob/master/SECURITY.md).
+
+* [gRPC API
+ V2](https://developers.google.com/maps-booking/reference/grpc-api-v2/slot-specification).