Flatbuffers library added to the list of third party libraries.
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
||||
GRPC implementation and test
|
||||
============================
|
||||
|
||||
NOTE: files in `src/` are shared with the GRPC project, and maintained there
|
||||
(any changes should be submitted to GRPC instead). These files are copied
|
||||
from GRPC, and work with both the Protobuf and FlatBuffers code generator.
|
||||
|
||||
`tests/` contains a GRPC specific test, you need to have built and installed
|
||||
the GRPC libraries for this to compile. This test will build using the
|
||||
`FLATBUFFERS_BUILD_GRPCTEST` option to the main FlatBuffers CMake project.
|
||||
|
||||
## Building Flatbuffers with gRPC
|
||||
|
||||
### Linux
|
||||
|
||||
1. Download, build and install gRPC. See [instructions](https://github.com/grpc/grpc/tree/master/src/cpp).
|
||||
* Lets say your gRPC clone is at `/your/path/to/grpc_repo`.
|
||||
* Install gRPC in a custom directory by running `make install prefix=/your/path/to/grpc_repo/install`.
|
||||
2. `export GRPC_INSTALL_PATH=/your/path/to/grpc_repo/install`
|
||||
3. `export PROTOBUF_DOWNLOAD_PATH=/your/path/to/grpc_repo/third_party/protobuf`
|
||||
4. `mkdir build ; cd build`
|
||||
5. `cmake -DFLATBUFFERS_BUILD_GRPCTEST=ON -DGRPC_INSTALL_PATH=${GRPC_INSTALL_PATH} -DPROTOBUF_DOWNLOAD_PATH=${PROTOBUF_DOWNLOAD_PATH} ..`
|
||||
6. `make`
|
||||
|
||||
## Running FlatBuffer gRPC tests
|
||||
|
||||
### Linux
|
||||
|
||||
1. `ln -s ${GRPC_INSTALL_PATH}/lib/libgrpc++_unsecure.so.6 ${GRPC_INSTALL_PATH}/lib/libgrpc++_unsecure.so.1`
|
||||
2. `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${GRPC_INSTALL_PATH}/lib`
|
||||
3. `make test ARGS=-V`
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
grpc_1_15_1_githash=1a60e6971f428323245a930031ad267bb3142ba4
|
||||
|
||||
function build_grpc () {
|
||||
git clone https://github.com/grpc/grpc.git google/grpc
|
||||
cd google/grpc
|
||||
git checkout ${grpc_1_15_1_githash}
|
||||
git submodule update --init
|
||||
make
|
||||
make install prefix=`pwd`/install
|
||||
if [ ! -f ${GRPC_INSTALL_PATH}/lib/libgrpc++_unsecure.so.1 ]; then
|
||||
ln -s ${GRPC_INSTALL_PATH}/lib/libgrpc++_unsecure.so.6 ${GRPC_INSTALL_PATH}/lib/libgrpc++_unsecure.so.1
|
||||
fi
|
||||
cd ../..
|
||||
}
|
||||
|
||||
GRPC_INSTALL_PATH=`pwd`/google/grpc/install
|
||||
PROTOBUF_DOWNLOAD_PATH=`pwd`/google/grpc/third_party/protobuf
|
||||
|
||||
build_grpc
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.google.flatbuffers</groupId>
|
||||
<artifactId>flatbuffers-parent</artifactId>
|
||||
<version>1.11.1</version>
|
||||
</parent>
|
||||
<artifactId>flatbuffers-java-grpc</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<packaging>bundle</packaging>
|
||||
<description>
|
||||
Utilities supporting generated code for GRPC
|
||||
</description>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Wouter van Oortmerssen</name>
|
||||
</developer>
|
||||
<developer>
|
||||
<name>Yuri Finkelstein</name>
|
||||
<url>https://github.com/yfinkelstein</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<properties>
|
||||
<gRPC.version>1.11.1</gRPC.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.flatbuffers</groupId>
|
||||
<artifactId>flatbuffers-java</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-core</artifactId>
|
||||
<version>${gRPC.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.flatbuffers.grpc;
|
||||
|
||||
import com.google.flatbuffers.Table;
|
||||
import io.grpc.Drainable;
|
||||
import io.grpc.KnownLength;
|
||||
import io.grpc.MethodDescriptor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class FlatbuffersUtils {
|
||||
abstract public static class FBExtactor <T extends Table> {
|
||||
T extract (InputStream stream) throws IOException {
|
||||
if (stream instanceof KnownLength) {
|
||||
int size = stream.available();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(size);
|
||||
stream.read(buffer.array());
|
||||
return extract(buffer);
|
||||
} else
|
||||
throw new RuntimeException("The class " + stream.getClass().getCanonicalName() + " does not extend from KnownLength ");
|
||||
}
|
||||
|
||||
public abstract T extract(ByteBuffer buffer);
|
||||
|
||||
}
|
||||
|
||||
static class FBInputStream extends InputStream implements Drainable, KnownLength {
|
||||
private final ByteBuffer buffer;
|
||||
private final int size;
|
||||
@Nullable private ByteArrayInputStream inputStream;
|
||||
|
||||
FBInputStream(ByteBuffer buffer) {
|
||||
this.buffer = buffer;
|
||||
this.size = buffer.remaining();
|
||||
}
|
||||
|
||||
private void makeStreamIfNotAlready() {
|
||||
if (inputStream == null)
|
||||
inputStream = new ByteArrayInputStream(buffer.array(), buffer.position(), size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int drainTo(OutputStream target) throws IOException {
|
||||
target.write(buffer.array(), buffer.position(), size);
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
makeStreamIfNotAlready();
|
||||
return inputStream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
makeStreamIfNotAlready();
|
||||
if (inputStream == null) {
|
||||
if (len >= size) {
|
||||
System.arraycopy(buffer.array(), buffer.position(), b, off, size);
|
||||
return size;
|
||||
} else {
|
||||
makeStreamIfNotAlready();
|
||||
return inputStream.read(b, off, len);
|
||||
}
|
||||
} else
|
||||
return inputStream.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return inputStream == null ? size : inputStream.available();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static <T extends Table> MethodDescriptor.Marshaller<T> marshaller(final Class<T> clazz, final FBExtactor<T> extractor) {
|
||||
return new MethodDescriptor.ReflectableMarshaller<T>() {
|
||||
@Override
|
||||
public Class<T> getMessageClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream stream(T value) {
|
||||
return new FBInputStream (value.getByteBuffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public T parse(InputStream stream) {
|
||||
try {
|
||||
return extractor.extract(stream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Vendored
+213
@@ -0,0 +1,213 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.google.flatbuffers</groupId>
|
||||
<artifactId>flatbuffers-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.11.1</version>
|
||||
<name>flatbuffers-parent</name>
|
||||
<description>parent pom for flatbuffers java artifacts</description>
|
||||
<properties>
|
||||
<scm.url>https://github.com/google/flatbuffers</scm.url>
|
||||
<scm.connection>scm:git:${scm.url}.git</scm.connection>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<issueManagement>
|
||||
<system>GitHub</system>
|
||||
<url>https://github.com/google/flatbuffers/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Wouter van Oortmerssen</name>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<url>${scm.url}</url>
|
||||
|
||||
<scm>
|
||||
<connection>${scm.connection}</connection>
|
||||
<developerConnection>${scm.connection}</developerConnection>
|
||||
<url>${scm.url}</url>
|
||||
<tag>HEAD</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>ossrh</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<extensions>
|
||||
<extension>
|
||||
<!--
|
||||
os-maven-plugin is a Maven extension/plugin that generates various useful platform-dependent
|
||||
project properties normalized from ${os.detected.name} and ${os.detected.arch}.
|
||||
-->
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>1.5.0.Final</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.6.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.19.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.4</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>1.12</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.8</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.5.3</version>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>1.5.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Test.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<extensions>true</extensions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>1.6.7</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<configuration>
|
||||
<autoVersionSubmodules>true</autoVersionSubmodules>
|
||||
<useReleaseProfile>false</useReleaseProfile>
|
||||
<releaseProfiles>release</releaseProfiles>
|
||||
<goals>deploy</goals>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<modules>
|
||||
<!-- consider the benefits of publishing all maven artifacts in this project
|
||||
|
||||
<module>flatbuffers-compiler</module>
|
||||
<module>flatbuffers-java</module>
|
||||
|
||||
-->
|
||||
<module>flatbuffers-java-grpc</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "greeter.grpc.fb.h"
|
||||
#include "greeter_generated.h"
|
||||
|
||||
#include <grpc++/grpc++.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class GreeterClient {
|
||||
public:
|
||||
GreeterClient(std::shared_ptr<grpc::Channel> channel)
|
||||
: stub_(Greeter::NewStub(channel)) {}
|
||||
|
||||
std::string SayHello(const std::string &name) {
|
||||
flatbuffers::grpc::MessageBuilder mb;
|
||||
auto name_offset = mb.CreateString(name);
|
||||
auto request_offset = CreateHelloRequest(mb, name_offset);
|
||||
mb.Finish(request_offset);
|
||||
auto request_msg = mb.ReleaseMessage<HelloRequest>();
|
||||
|
||||
flatbuffers::grpc::Message<HelloReply> response_msg;
|
||||
|
||||
grpc::ClientContext context;
|
||||
|
||||
auto status = stub_->SayHello(&context, request_msg, &response_msg);
|
||||
if (status.ok()) {
|
||||
const HelloReply *response = response_msg.GetRoot();
|
||||
return response->message()->str();
|
||||
} else {
|
||||
std::cerr << status.error_code() << ": " << status.error_message()
|
||||
<< std::endl;
|
||||
return "RPC failed";
|
||||
}
|
||||
}
|
||||
|
||||
void SayManyHellos(const std::string &name, int num_greetings,
|
||||
std::function<void(const std::string &)> callback) {
|
||||
flatbuffers::grpc::MessageBuilder mb;
|
||||
auto name_offset = mb.CreateString(name);
|
||||
auto request_offset =
|
||||
CreateManyHellosRequest(mb, name_offset, num_greetings);
|
||||
mb.Finish(request_offset);
|
||||
auto request_msg = mb.ReleaseMessage<ManyHellosRequest>();
|
||||
|
||||
flatbuffers::grpc::Message<HelloReply> response_msg;
|
||||
|
||||
grpc::ClientContext context;
|
||||
|
||||
auto stream = stub_->SayManyHellos(&context, request_msg);
|
||||
while (stream->Read(&response_msg)) {
|
||||
const HelloReply *response = response_msg.GetRoot();
|
||||
callback(response->message()->str());
|
||||
}
|
||||
auto status = stream->Finish();
|
||||
if (!status.ok()) {
|
||||
std::cerr << status.error_code() << ": " << status.error_message()
|
||||
<< std::endl;
|
||||
callback("RPC failed");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Greeter::Stub> stub_;
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::string server_address("localhost:50051");
|
||||
|
||||
auto channel =
|
||||
grpc::CreateChannel(server_address, grpc::InsecureChannelCredentials());
|
||||
GreeterClient greeter(channel);
|
||||
|
||||
std::string name("world");
|
||||
|
||||
std::string message = greeter.SayHello(name);
|
||||
std::cerr << "Greeter received: " << message << std::endl;
|
||||
|
||||
int num_greetings = 10;
|
||||
greeter.SayManyHellos(name, num_greetings, [](const std::string &message) {
|
||||
std::cerr << "Greeter received: " << message << std::endl;
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
table HelloReply {
|
||||
message:string;
|
||||
}
|
||||
|
||||
table HelloRequest {
|
||||
name:string;
|
||||
}
|
||||
|
||||
table ManyHellosRequest {
|
||||
name:string;
|
||||
num_greetings:int;
|
||||
}
|
||||
|
||||
rpc_service Greeter {
|
||||
SayHello(HelloRequest):HelloReply;
|
||||
SayManyHellos(ManyHellosRequest):HelloReply (streaming: "server");
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "greeter.grpc.fb.h"
|
||||
#include "greeter_generated.h"
|
||||
|
||||
#include <grpc++/grpc++.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class GreeterServiceImpl final : public Greeter::Service {
|
||||
virtual grpc::Status SayHello(
|
||||
grpc::ServerContext *context,
|
||||
const flatbuffers::grpc::Message<HelloRequest> *request_msg,
|
||||
flatbuffers::grpc::Message<HelloReply> *response_msg) override {
|
||||
// flatbuffers::grpc::MessageBuilder mb_;
|
||||
// We call GetRoot to "parse" the message. Verification is already
|
||||
// performed by default. See the notes below for more details.
|
||||
const HelloRequest *request = request_msg->GetRoot();
|
||||
|
||||
// Fields are retrieved as usual with FlatBuffers
|
||||
const std::string &name = request->name()->str();
|
||||
|
||||
// `flatbuffers::grpc::MessageBuilder` is a `FlatBufferBuilder` with a
|
||||
// special allocator for efficient gRPC buffer transfer, but otherwise
|
||||
// usage is the same as usual.
|
||||
auto msg_offset = mb_.CreateString("Hello, " + name);
|
||||
auto hello_offset = CreateHelloReply(mb_, msg_offset);
|
||||
mb_.Finish(hello_offset);
|
||||
|
||||
// The `ReleaseMessage<T>()` function detaches the message from the
|
||||
// builder, so we can transfer the resopnse to gRPC while simultaneously
|
||||
// detaching that memory buffer from the builer.
|
||||
*response_msg = mb_.ReleaseMessage<HelloReply>();
|
||||
assert(response_msg->Verify());
|
||||
|
||||
// Return an OK status.
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
virtual grpc::Status SayManyHellos(
|
||||
grpc::ServerContext *context,
|
||||
const flatbuffers::grpc::Message<ManyHellosRequest> *request_msg,
|
||||
grpc::ServerWriter<flatbuffers::grpc::Message<HelloReply>> *writer)
|
||||
override {
|
||||
// The streaming usage below is simply a combination of standard gRPC
|
||||
// streaming with the FlatBuffers usage shown above.
|
||||
const ManyHellosRequest *request = request_msg->GetRoot();
|
||||
const std::string &name = request->name()->str();
|
||||
int num_greetings = request->num_greetings();
|
||||
|
||||
for (int i = 0; i < num_greetings; i++) {
|
||||
auto msg_offset = mb_.CreateString("Many hellos, " + name);
|
||||
auto hello_offset = CreateHelloReply(mb_, msg_offset);
|
||||
mb_.Finish(hello_offset);
|
||||
writer->Write(mb_.ReleaseMessage<HelloReply>());
|
||||
}
|
||||
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
flatbuffers::grpc::MessageBuilder mb_;
|
||||
};
|
||||
|
||||
void RunServer() {
|
||||
std::string server_address("0.0.0.0:50051");
|
||||
GreeterServiceImpl service;
|
||||
|
||||
grpc::ServerBuilder builder;
|
||||
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
|
||||
builder.RegisterService(&service);
|
||||
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
|
||||
std::cerr << "Server listening on " << server_address << std::endl;
|
||||
|
||||
server->Wait();
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
RunServer();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2015, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SRC_COMPILER_CONFIG_H
|
||||
#define SRC_COMPILER_CONFIG_H
|
||||
|
||||
// This file is here only because schema_interface.h, which is copied from gRPC,
|
||||
// includes it. There is nothing for Flatbuffers to configure.
|
||||
|
||||
#endif // SRC_COMPILER_CONFIG_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2015, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
|
||||
#define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
|
||||
|
||||
// cpp_generator.h/.cc do not directly depend on GRPC/ProtoBuf, such that they
|
||||
// can be used to generate code for other serialization systems, such as
|
||||
// FlatBuffers.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "src/compiler/config.h"
|
||||
#include "src/compiler/schema_interface.h"
|
||||
|
||||
#ifndef GRPC_CUSTOM_STRING
|
||||
#include <string>
|
||||
#define GRPC_CUSTOM_STRING std::string
|
||||
#endif
|
||||
|
||||
namespace grpc {
|
||||
|
||||
typedef GRPC_CUSTOM_STRING string;
|
||||
|
||||
} // namespace grpc
|
||||
|
||||
namespace grpc_cpp_generator {
|
||||
|
||||
// Contains all the parameters that are parsed from the command line.
|
||||
struct Parameters {
|
||||
// Puts the service into a namespace
|
||||
grpc::string services_namespace;
|
||||
// Use system includes (<>) or local includes ("")
|
||||
bool use_system_headers;
|
||||
// Prefix to any grpc include
|
||||
grpc::string grpc_search_path;
|
||||
// Generate GMOCK code to facilitate unit testing.
|
||||
bool generate_mock_code;
|
||||
};
|
||||
|
||||
// Return the prologue of the generated header file.
|
||||
grpc::string GetHeaderPrologue(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the includes needed for generated header file.
|
||||
grpc::string GetHeaderIncludes(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the includes needed for generated source file.
|
||||
grpc::string GetSourceIncludes(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the epilogue of the generated header file.
|
||||
grpc::string GetHeaderEpilogue(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the prologue of the generated source file.
|
||||
grpc::string GetSourcePrologue(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the services for generated header file.
|
||||
grpc::string GetHeaderServices(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the services for generated source file.
|
||||
grpc::string GetSourceServices(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the epilogue of the generated source file.
|
||||
grpc::string GetSourceEpilogue(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the prologue of the generated mock file.
|
||||
grpc::string GetMockPrologue(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the includes needed for generated mock file.
|
||||
grpc::string GetMockIncludes(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the services for generated mock file.
|
||||
grpc::string GetMockServices(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the epilogue of generated mock file.
|
||||
grpc::string GetMockEpilogue(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the prologue of the generated mock file.
|
||||
grpc::string GetMockPrologue(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the includes needed for generated mock file.
|
||||
grpc::string GetMockIncludes(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the services for generated mock file.
|
||||
grpc::string GetMockServices(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
// Return the epilogue of generated mock file.
|
||||
grpc::string GetMockEpilogue(grpc_generator::File *file,
|
||||
const Parameters ¶ms);
|
||||
|
||||
} // namespace grpc_cpp_generator
|
||||
|
||||
#endif // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H
|
||||
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2015, 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 AN/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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
#include "src/compiler/go_generator.h"
|
||||
|
||||
template <class T>
|
||||
grpc::string as_string(T x) {
|
||||
std::ostringstream out;
|
||||
out << x;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
inline bool ClientOnlyStreaming(const grpc_generator::Method *method) {
|
||||
return method->ClientStreaming() && !method->ServerStreaming();
|
||||
}
|
||||
|
||||
inline bool ServerOnlyStreaming(const grpc_generator::Method *method) {
|
||||
return !method->ClientStreaming() && method->ServerStreaming();
|
||||
}
|
||||
|
||||
namespace grpc_go_generator {
|
||||
|
||||
// Returns string with first letter to lowerCase
|
||||
grpc::string unexportName(grpc::string s) {
|
||||
if (s.empty())
|
||||
return s;
|
||||
s[0] = static_cast<char>(std::tolower(s[0]));
|
||||
return s;
|
||||
}
|
||||
|
||||
// Returns string with first letter to uppercase
|
||||
grpc::string exportName(grpc::string s) {
|
||||
if (s.empty())
|
||||
return s;
|
||||
s[0] = static_cast<char>(std::toupper(s[0]));
|
||||
return s;
|
||||
}
|
||||
|
||||
// Generates imports for the service
|
||||
void GenerateImports(grpc_generator::File *file, grpc_generator::Printer *printer,
|
||||
std::map<grpc::string, grpc::string> vars) {
|
||||
vars["filename"] = file->filename();
|
||||
printer->Print("//Generated by gRPC Go plugin\n");
|
||||
printer->Print("//If you make any local changes, they will be lost\n");
|
||||
printer->Print(vars, "//source: $filename$\n\n");
|
||||
printer->Print(vars, "package $Package$\n\n");
|
||||
if (file->additional_headers() != "") {
|
||||
printer->Print(file->additional_headers().c_str());
|
||||
printer->Print("\n\n");
|
||||
}
|
||||
printer->Print("import (\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "$context$ \"context\"\n");
|
||||
printer->Print(vars, "$grpc$ \"google.golang.org/grpc\"\n");
|
||||
printer->Outdent();
|
||||
printer->Print(")\n\n");
|
||||
}
|
||||
|
||||
// Generates Server method signature source
|
||||
void GenerateServerMethodSignature(const grpc_generator::Method *method, grpc_generator::Printer *printer,
|
||||
std::map<grpc::string, grpc::string> vars) {
|
||||
vars["Method"] = exportName(method->name());
|
||||
vars["Request"] = method->get_input_type_name();
|
||||
vars["Response"] = (vars["CustomMethodIO"] == "") ? method->get_output_type_name() : vars["CustomMethodIO"];
|
||||
if (method->NoStreaming()) {
|
||||
printer->Print(vars, "$Method$($context$.Context, *$Request$) (*$Response$, error)");
|
||||
} else if (ServerOnlyStreaming(method)) {
|
||||
printer->Print(vars, "$Method$(*$Request$, $Service$_$Method$Server) error");
|
||||
} else {
|
||||
printer->Print(vars, "$Method$($Service$_$Method$Server) error");
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateServerMethod(const grpc_generator::Method *method, grpc_generator::Printer *printer,
|
||||
std::map<grpc::string, grpc::string> vars) {
|
||||
vars["Method"] = exportName(method->name());
|
||||
vars["Request"] = method->get_input_type_name();
|
||||
vars["Response"] = (vars["CustomMethodIO"] == "") ? method->get_output_type_name() : vars["CustomMethodIO"];
|
||||
vars["FullMethodName"] = "/" + vars["ServicePrefix"] + "." + vars["Service"] + "/" + vars["Method"];
|
||||
vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler";
|
||||
if (method->NoStreaming()) {
|
||||
printer->Print(vars, "func $Handler$(srv interface{}, ctx $context$.Context,\n\tdec func(interface{}) error, interceptor $grpc$.UnaryServerInterceptor) (interface{}, error) {\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "in := new($Request$)\n");
|
||||
printer->Print("if err := dec(in); err != nil { return nil, err }\n");
|
||||
printer->Print(vars, "if interceptor == nil { return srv.($Service$Server).$Method$(ctx, in) }\n");
|
||||
printer->Print(vars, "info := &$grpc$.UnaryServerInfo{\n");
|
||||
printer->Indent();
|
||||
printer->Print("Server: srv,\n");
|
||||
printer->Print(vars, "FullMethod: \"$FullMethodName$\",\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
printer->Print(vars, "handler := func(ctx $context$.Context, req interface{}) (interface{}, error) {\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "return srv.($Service$Server).$Method$(ctx, req.(* $Request$))\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
printer->Print("return interceptor(ctx, in, info, handler)\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
return;
|
||||
}
|
||||
vars["StreamType"] = vars["ServiceUnexported"] + vars["Method"] + "Server";
|
||||
printer->Print(vars, "func $Handler$(srv interface{}, stream $grpc$.ServerStream) error {\n");
|
||||
printer->Indent();
|
||||
if (ServerOnlyStreaming(method)) {
|
||||
printer->Print(vars, "m := new($Request$)\n");
|
||||
printer->Print(vars, "if err := stream.RecvMsg(m); err != nil { return err }\n");
|
||||
printer->Print(vars, "return srv.($Service$Server).$Method$(m, &$StreamType${stream})\n");
|
||||
} else {
|
||||
printer->Print(vars, "return srv.($Service$Server).$Method$(&$StreamType${stream})\n");
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
bool genSend = method->BidiStreaming() || ServerOnlyStreaming(method);
|
||||
bool genRecv = method->BidiStreaming() || ClientOnlyStreaming(method);
|
||||
bool genSendAndClose = ClientOnlyStreaming(method);
|
||||
|
||||
printer->Print(vars, "type $Service$_$Method$Server interface { \n");
|
||||
printer->Indent();
|
||||
if (genSend) {
|
||||
printer->Print(vars, "Send(* $Response$) error\n");
|
||||
}
|
||||
if (genRecv) {
|
||||
printer->Print(vars, "Recv() (* $Request$, error)\n");
|
||||
}
|
||||
if (genSendAndClose) {
|
||||
printer->Print(vars, "SendAndClose(* $Response$) error\n");
|
||||
}
|
||||
printer->Print(vars, "$grpc$.ServerStream\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
printer->Print(vars, "type $StreamType$ struct {\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "$grpc$.ServerStream\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
if (genSend) {
|
||||
printer->Print(vars, "func (x *$StreamType$) Send(m *$Response$) error {\n");
|
||||
printer->Indent();
|
||||
printer->Print("return x.ServerStream.SendMsg(m)\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
}
|
||||
if (genRecv) {
|
||||
printer->Print(vars, "func (x *$StreamType$) Recv() (*$Request$, error) {\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "m := new($Request$)\n");
|
||||
printer->Print("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }\n");
|
||||
printer->Print("return m, nil\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
}
|
||||
if (genSendAndClose) {
|
||||
printer->Print(vars, "func (x *$StreamType$) SendAndClose(m *$Response$) error {\n");
|
||||
printer->Indent();
|
||||
printer->Print("return x.ServerStream.SendMsg(m)\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Generates Client method signature source
|
||||
void GenerateClientMethodSignature(const grpc_generator::Method *method, grpc_generator::Printer *printer,
|
||||
std::map<grpc::string, grpc::string> vars) {
|
||||
vars["Method"] = exportName(method->name());
|
||||
vars["Request"] = ", in *" + ((vars["CustomMethodIO"] == "") ? method->get_input_type_name() : vars["CustomMethodIO"]);
|
||||
if (ClientOnlyStreaming(method) || method->BidiStreaming()) {
|
||||
vars["Request"] = "";
|
||||
}
|
||||
vars["Response"] = "* " + method->get_output_type_name();
|
||||
if (ClientOnlyStreaming(method) || method->BidiStreaming() || ServerOnlyStreaming(method)) {
|
||||
vars["Response"] = vars["Service"] + "_" + vars["Method"] + "Client" ;
|
||||
}
|
||||
printer->Print(vars, "$Method$(ctx $context$.Context$Request$, \n\topts... $grpc$.CallOption) ($Response$, error)");
|
||||
}
|
||||
|
||||
// Generates Client method source
|
||||
void GenerateClientMethod(const grpc_generator::Method *method, grpc_generator::Printer *printer,
|
||||
std::map<grpc::string, grpc::string> vars) {
|
||||
printer->Print(vars, "func (c *$ServiceUnexported$Client) ");
|
||||
GenerateClientMethodSignature(method, printer, vars);
|
||||
printer->Print(" {\n");
|
||||
printer->Indent();
|
||||
vars["Method"] = exportName(method->name());
|
||||
vars["Request"] = (vars["CustomMethodIO"] == "") ? method->get_input_type_name() : vars["CustomMethodIO"];
|
||||
vars["Response"] = method->get_output_type_name();
|
||||
vars["FullMethodName"] = "/" + vars["ServicePrefix"] + "." + vars["Service"] + "/" + vars["Method"];
|
||||
if (method->NoStreaming()) {
|
||||
printer->Print(vars, "out := new($Response$)\n");
|
||||
printer->Print(vars, "err := $grpc$.Invoke(ctx, \"$FullMethodName$\", in, out, c.cc, opts...)\n");
|
||||
printer->Print("if err != nil { return nil, err }\n");
|
||||
printer->Print("return out, nil\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
return;
|
||||
}
|
||||
vars["StreamType"] = vars["ServiceUnexported"] + vars["Method"] + "Client";
|
||||
printer->Print(vars, "stream, err := $grpc$.NewClientStream(ctx, &$MethodDesc$, c.cc, \"$FullMethodName$\", opts...)\n");
|
||||
printer->Print("if err != nil { return nil, err }\n");
|
||||
|
||||
printer->Print(vars, "x := &$StreamType${stream}\n");
|
||||
if (ServerOnlyStreaming(method)) {
|
||||
printer->Print("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }\n");
|
||||
printer->Print("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }\n");
|
||||
}
|
||||
printer->Print("return x,nil\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
bool genSend = method->BidiStreaming() || ClientOnlyStreaming(method);
|
||||
bool genRecv = method->BidiStreaming() || ServerOnlyStreaming(method);
|
||||
bool genCloseAndRecv = ClientOnlyStreaming(method);
|
||||
|
||||
//Stream interface
|
||||
printer->Print(vars, "type $Service$_$Method$Client interface {\n");
|
||||
printer->Indent();
|
||||
if (genSend) {
|
||||
printer->Print(vars, "Send(*$Request$) error\n");
|
||||
}
|
||||
if (genRecv) {
|
||||
printer->Print(vars, "Recv() (*$Response$, error)\n");
|
||||
}
|
||||
if (genCloseAndRecv) {
|
||||
printer->Print(vars, "CloseAndRecv() (*$Response$, error)\n");
|
||||
}
|
||||
printer->Print(vars, "$grpc$.ClientStream\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
//Stream Client
|
||||
printer->Print(vars, "type $StreamType$ struct{\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "$grpc$.ClientStream\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
if (genSend) {
|
||||
printer->Print(vars, "func (x *$StreamType$) Send(m *$Request$) error {\n");
|
||||
printer->Indent();
|
||||
printer->Print("return x.ClientStream.SendMsg(m)\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
}
|
||||
|
||||
if (genRecv) {
|
||||
printer->Print(vars, "func (x *$StreamType$) Recv() (*$Response$, error) {\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "m := new($Response$)\n");
|
||||
printer->Print("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }\n");
|
||||
printer->Print("return m, nil\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
}
|
||||
|
||||
if (genCloseAndRecv) {
|
||||
printer->Print(vars, "func (x *$StreamType$) CloseAndRecv() (*$Response$, error) {\n");
|
||||
printer->Indent();
|
||||
printer->Print("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }\n");
|
||||
printer->Print(vars, "m := new ($Response$)\n");
|
||||
printer->Print("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }\n");
|
||||
printer->Print("return m, nil\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Generates client API for the service
|
||||
void GenerateService(const grpc_generator::Service *service, grpc_generator::Printer* printer,
|
||||
std::map<grpc::string, grpc::string> vars) {
|
||||
vars["Service"] = exportName(service->name());
|
||||
// Client Interface
|
||||
printer->Print(vars, "// Client API for $Service$ service\n");
|
||||
printer->Print(vars, "type $Service$Client interface{\n");
|
||||
printer->Indent();
|
||||
for (int i = 0; i < service->method_count(); i++) {
|
||||
GenerateClientMethodSignature(service->method(i).get(), printer, vars);
|
||||
printer->Print("\n");
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
// Client structure
|
||||
vars["ServiceUnexported"] = unexportName(vars["Service"]);
|
||||
printer->Print(vars, "type $ServiceUnexported$Client struct {\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "cc *$grpc$.ClientConn\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
// NewClient
|
||||
printer->Print(vars, "func New$Service$Client(cc *$grpc$.ClientConn) $Service$Client {\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "return &$ServiceUnexported$Client{cc}");
|
||||
printer->Outdent();
|
||||
printer->Print("\n}\n\n");
|
||||
|
||||
int unary_methods = 0, streaming_methods = 0;
|
||||
vars["ServiceDesc"] = "_" + vars["Service"] + "_serviceDesc";
|
||||
for (int i = 0; i < service->method_count(); i++) {
|
||||
auto method = service->method(i);
|
||||
if (method->NoStreaming()) {
|
||||
vars["MethodDesc"] = vars["ServiceDesc"] + ".Method[" + as_string(unary_methods) + "]";
|
||||
unary_methods++;
|
||||
} else {
|
||||
vars["MethodDesc"] = vars["ServiceDesc"] + ".Streams[" + as_string(streaming_methods) + "]";
|
||||
streaming_methods++;
|
||||
}
|
||||
GenerateClientMethod(method.get(), printer, vars);
|
||||
}
|
||||
|
||||
//Server Interface
|
||||
printer->Print(vars, "// Server API for $Service$ service\n");
|
||||
printer->Print(vars, "type $Service$Server interface {\n");
|
||||
printer->Indent();
|
||||
for (int i = 0; i < service->method_count(); i++) {
|
||||
GenerateServerMethodSignature(service->method(i).get(), printer, vars);
|
||||
printer->Print("\n");
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
// Server registration.
|
||||
printer->Print(vars, "func Register$Service$Server(s *$grpc$.Server, srv $Service$Server) {\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "s.RegisterService(&$ServiceDesc$, srv)\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
for (int i = 0; i < service->method_count(); i++) {
|
||||
GenerateServerMethod(service->method(i).get(), printer, vars);
|
||||
printer->Print("\n");
|
||||
}
|
||||
|
||||
|
||||
//Service Descriptor
|
||||
printer->Print(vars, "var $ServiceDesc$ = $grpc$.ServiceDesc{\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "ServiceName: \"$ServicePrefix$.$Service$\",\n");
|
||||
printer->Print(vars, "HandlerType: (*$Service$Server)(nil),\n");
|
||||
printer->Print(vars, "Methods: []$grpc$.MethodDesc{\n");
|
||||
printer->Indent();
|
||||
for (int i = 0; i < service->method_count(); i++) {
|
||||
auto method = service->method(i);
|
||||
vars["Method"] = method->name();
|
||||
vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler";
|
||||
if (method->NoStreaming()) {
|
||||
printer->Print("{\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "MethodName: \"$Method$\",\n");
|
||||
printer->Print(vars, "Handler: $Handler$, \n");
|
||||
printer->Outdent();
|
||||
printer->Print("},\n");
|
||||
}
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print("},\n");
|
||||
printer->Print(vars, "Streams: []$grpc$.StreamDesc{\n");
|
||||
printer->Indent();
|
||||
for (int i = 0; i < service->method_count(); i++) {
|
||||
auto method = service->method(i);
|
||||
vars["Method"] = method->name();
|
||||
vars["Handler"] = "_" + vars["Service"] + "_" + vars["Method"] + "_Handler";
|
||||
if (!method->NoStreaming()) {
|
||||
printer->Print("{\n");
|
||||
printer->Indent();
|
||||
printer->Print(vars, "StreamName: \"$Method$\",\n");
|
||||
printer->Print(vars, "Handler: $Handler$, \n");
|
||||
if (ClientOnlyStreaming(method.get())) {
|
||||
printer->Print("ClientStreams: true,\n");
|
||||
} else if (ServerOnlyStreaming(method.get())) {
|
||||
printer->Print("ServerStreams: true,\n");
|
||||
} else {
|
||||
printer->Print("ServerStreams: true,\n");
|
||||
printer->Print("ClientStreams: true,\n");
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print("},\n");
|
||||
}
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print("},\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Returns source for the service
|
||||
grpc::string GenerateServiceSource(grpc_generator::File *file,
|
||||
const grpc_generator::Service *service,
|
||||
grpc_go_generator::Parameters *parameters) {
|
||||
grpc::string out;
|
||||
auto p = file->CreatePrinter(&out);
|
||||
auto printer = p.get();
|
||||
std::map<grpc::string, grpc::string> vars;
|
||||
vars["Package"] = parameters->package_name;
|
||||
vars["ServicePrefix"] = parameters->service_prefix;
|
||||
vars["grpc"] = "grpc";
|
||||
vars["context"] = "context";
|
||||
GenerateImports(file, printer, vars);
|
||||
if (parameters->custom_method_io_type != "") {
|
||||
vars["CustomMethodIO"] = parameters->custom_method_io_type;
|
||||
}
|
||||
GenerateService(service, printer, vars);
|
||||
return out;
|
||||
}
|
||||
}// Namespace grpc_go_generator
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2015, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRPC_INTERNAL_COMPILER_GO_GENERATOR_H
|
||||
#define GRPC_INTERNAL_COMPILER_GO_GENERATOR_H
|
||||
|
||||
//go generator is used to generate GRPC code for serialization system, such as flatbuffers
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "src/compiler/schema_interface.h"
|
||||
|
||||
namespace grpc_go_generator {
|
||||
|
||||
struct Parameters {
|
||||
//Defines the custom parameter types for methods
|
||||
//eg: flatbuffers uses flatbuffers.Builder as input for the client and output for the server
|
||||
grpc::string custom_method_io_type;
|
||||
|
||||
//Package name for the service
|
||||
grpc::string package_name;
|
||||
|
||||
//Prefix for RPC Calls
|
||||
grpc::string service_prefix;
|
||||
};
|
||||
|
||||
// Return the source of the generated service file.
|
||||
grpc::string GenerateServiceSource(grpc_generator::File *file,
|
||||
const grpc_generator::Service *service,
|
||||
grpc_go_generator::Parameters *parameters);
|
||||
|
||||
}
|
||||
|
||||
#endif // GRPC_INTERNAL_COMPILER_GO_GENERATOR_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef NET_GRPC_COMPILER_JAVA_GENERATOR_H_
|
||||
#define NET_GRPC_COMPILER_JAVA_GENERATOR_H_
|
||||
|
||||
#include <stdlib.h> // for abort()
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "src/compiler/schema_interface.h"
|
||||
|
||||
class LogMessageVoidify {
|
||||
public:
|
||||
LogMessageVoidify() {}
|
||||
// This has to be an operator with a precedence lower than << but
|
||||
// higher than ?:
|
||||
void operator&(std::ostream&) {}
|
||||
};
|
||||
|
||||
class LogHelper {
|
||||
std::ostream* os_;
|
||||
|
||||
public:
|
||||
LogHelper(std::ostream* os) : os_(os) {}
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning( \
|
||||
disable : 4722) // the flow of control terminates in a destructor
|
||||
// (needed to compile ~LogHelper where destructor emits abort intentionally -
|
||||
// inherited from grpc/java code generator).
|
||||
#endif
|
||||
~LogHelper() {
|
||||
*os_ << std::endl;
|
||||
::abort();
|
||||
}
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
std::ostream& get_os() const { return *os_; }
|
||||
};
|
||||
|
||||
// Abort the program after logging the mesage if the given condition is not
|
||||
// true. Otherwise, do nothing.
|
||||
#define GRPC_CODEGEN_CHECK(x) \
|
||||
(x) ? (void)0 \
|
||||
: LogMessageVoidify() & LogHelper(&std::cerr).get_os() \
|
||||
<< "CHECK FAILED: " << __FILE__ << ":" \
|
||||
<< __LINE__ << ": "
|
||||
|
||||
// Abort the program after logging the mesage.
|
||||
#define GRPC_CODEGEN_FAIL GRPC_CODEGEN_CHECK(false)
|
||||
|
||||
namespace grpc_java_generator {
|
||||
struct Parameters {
|
||||
// //Defines the custom parameter types for methods
|
||||
// //eg: flatbuffers uses flatbuffers.Builder as input for the client
|
||||
// and output for the server grpc::string custom_method_io_type;
|
||||
|
||||
// Package name for the service
|
||||
grpc::string package_name;
|
||||
};
|
||||
|
||||
// Return the source of the generated service file.
|
||||
grpc::string GenerateServiceSource(grpc_generator::File* file,
|
||||
const grpc_generator::Service* service,
|
||||
grpc_java_generator::Parameters* parameters);
|
||||
|
||||
} // namespace grpc_java_generator
|
||||
|
||||
#endif // NET_GRPC_COMPILER_JAVA_GENERATOR_H_
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2015, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H
|
||||
#define GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H
|
||||
|
||||
#include "src/compiler/config.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#ifndef GRPC_CUSTOM_STRING
|
||||
# include <string>
|
||||
# define GRPC_CUSTOM_STRING std::string
|
||||
#endif
|
||||
|
||||
namespace grpc {
|
||||
|
||||
typedef GRPC_CUSTOM_STRING string;
|
||||
|
||||
} // namespace grpc
|
||||
|
||||
namespace grpc_generator {
|
||||
|
||||
// A common interface for objects having comments in the source.
|
||||
// Return formatted comments to be inserted in generated code.
|
||||
struct CommentHolder {
|
||||
virtual ~CommentHolder() {}
|
||||
virtual grpc::string GetLeadingComments(const grpc::string prefix) const = 0;
|
||||
virtual grpc::string GetTrailingComments(const grpc::string prefix) const = 0;
|
||||
virtual std::vector<grpc::string> GetAllComments() const = 0;
|
||||
};
|
||||
|
||||
// An abstract interface representing a method.
|
||||
struct Method : public CommentHolder {
|
||||
virtual ~Method() {}
|
||||
|
||||
virtual grpc::string name() const = 0;
|
||||
|
||||
virtual grpc::string input_type_name() const = 0;
|
||||
virtual grpc::string output_type_name() const = 0;
|
||||
|
||||
virtual bool get_module_and_message_path_input(
|
||||
grpc::string *str, grpc::string generator_file_name,
|
||||
bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0;
|
||||
virtual bool get_module_and_message_path_output(
|
||||
grpc::string *str, grpc::string generator_file_name,
|
||||
bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0;
|
||||
|
||||
virtual grpc::string get_input_type_name() const = 0;
|
||||
virtual grpc::string get_output_type_name() const = 0;
|
||||
virtual bool NoStreaming() const = 0;
|
||||
virtual bool ClientStreaming() const = 0;
|
||||
virtual bool ServerStreaming() const = 0;
|
||||
virtual bool BidiStreaming() const = 0;
|
||||
};
|
||||
|
||||
// An abstract interface representing a service.
|
||||
struct Service : public CommentHolder {
|
||||
virtual ~Service() {}
|
||||
|
||||
virtual grpc::string name() const = 0;
|
||||
|
||||
virtual int method_count() const = 0;
|
||||
virtual std::unique_ptr<const Method> method(int i) const = 0;
|
||||
};
|
||||
|
||||
struct Printer {
|
||||
virtual ~Printer() {}
|
||||
|
||||
virtual void Print(const std::map<grpc::string, grpc::string> &vars,
|
||||
const char *template_string) = 0;
|
||||
virtual void Print(const char *string) = 0;
|
||||
virtual void Indent() = 0;
|
||||
virtual void Outdent() = 0;
|
||||
};
|
||||
|
||||
// An interface that allows the source generated to be output using various
|
||||
// libraries/idls/serializers.
|
||||
struct File : public CommentHolder {
|
||||
virtual ~File() {}
|
||||
|
||||
virtual grpc::string filename() const = 0;
|
||||
virtual grpc::string filename_without_ext() const = 0;
|
||||
virtual grpc::string package() const = 0;
|
||||
virtual std::vector<grpc::string> package_parts() const = 0;
|
||||
virtual grpc::string additional_headers() const = 0;
|
||||
|
||||
virtual int service_count() const = 0;
|
||||
virtual std::unique_ptr<const Service> service(int i) const = 0;
|
||||
|
||||
virtual std::unique_ptr<Printer> CreatePrinter(grpc::string *str) const = 0;
|
||||
};
|
||||
} // namespace grpc_generator
|
||||
|
||||
#endif // GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H
|
||||
@@ -0,0 +1,42 @@
|
||||
import java.nio.ByteBuffer;
|
||||
import MyGame.Example.Monster;
|
||||
import MyGame.Example.Stat;
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
|
||||
class GameFactory {
|
||||
public static Monster createMonster(String monsterName, short nestedMonsterHp, short nestedMonsterMana) {
|
||||
FlatBufferBuilder builder = new FlatBufferBuilder();
|
||||
|
||||
int name_offset = builder.createString(monsterName);
|
||||
Monster.startMonster(builder);
|
||||
Monster.addName(builder, name_offset);
|
||||
Monster.addHp(builder, nestedMonsterHp);
|
||||
Monster.addMana(builder, nestedMonsterMana);
|
||||
int monster_offset = Monster.endMonster(builder);
|
||||
Monster.finishMonsterBuffer(builder, monster_offset);
|
||||
|
||||
ByteBuffer buffer = builder.dataBuffer();
|
||||
Monster monster = Monster.getRootAsMonster(buffer);
|
||||
return monster;
|
||||
}
|
||||
|
||||
public static Monster createMonsterFromStat(Stat stat, int seqNo) {
|
||||
FlatBufferBuilder builder = new FlatBufferBuilder();
|
||||
int name_offset = builder.createString(stat.id() + " No." + seqNo);
|
||||
Monster.startMonster(builder);
|
||||
Monster.addName(builder, name_offset);
|
||||
int monster_offset = Monster.endMonster(builder);
|
||||
Monster.finishMonsterBuffer(builder, monster_offset);
|
||||
Monster monster = Monster.getRootAsMonster(builder.dataBuffer());
|
||||
return monster;
|
||||
}
|
||||
|
||||
public static Stat createStat(String greeting, long val, int count) {
|
||||
FlatBufferBuilder builder = new FlatBufferBuilder();
|
||||
int statOffset = Stat.createStat(builder, builder.createString(greeting), val, count);
|
||||
builder.finish(statOffset);
|
||||
Stat stat = Stat.getRootAsStat(builder.dataBuffer());
|
||||
return stat;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import MyGame.Example.Monster;
|
||||
import MyGame.Example.MonsterStorageGrpc;
|
||||
import MyGame.Example.Stat;
|
||||
import com.google.flatbuffers.FlatBufferBuilder;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import io.grpc.Server;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.InterruptedException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
|
||||
/**
|
||||
* Demonstrates basic client-server interaction using grpc-java over netty.
|
||||
*/
|
||||
public class JavaGrpcTest {
|
||||
static final String BIG_MONSTER_NAME = "Cyberdemon";
|
||||
static final short nestedMonsterHp = 600;
|
||||
static final short nestedMonsterMana = 1024;
|
||||
static final int numStreamedMsgs = 10;
|
||||
static final int timeoutMs = 3000;
|
||||
static Server server;
|
||||
static ManagedChannel channel;
|
||||
static MonsterStorageGrpc.MonsterStorageBlockingStub blockingStub;
|
||||
static MonsterStorageGrpc.MonsterStorageStub asyncStub;
|
||||
|
||||
static class MyService extends MonsterStorageGrpc.MonsterStorageImplBase {
|
||||
@Override
|
||||
public void store(Monster request, io.grpc.stub.StreamObserver<Stat> responseObserver) {
|
||||
Assert.assertEquals(request.name(), BIG_MONSTER_NAME);
|
||||
Assert.assertEquals(request.hp(), nestedMonsterHp);
|
||||
Assert.assertEquals(request.mana(), nestedMonsterMana);
|
||||
System.out.println("Received store request from " + request.name());
|
||||
// Create a response from the incoming request name.
|
||||
Stat stat = GameFactory.createStat("Hello " + request.name(), 100, 10);
|
||||
responseObserver.onNext(stat);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieve(Stat request, io.grpc.stub.StreamObserver<Monster> responseObserver) {
|
||||
// Create 10 monsters for streaming response.
|
||||
for (int i=0; i<numStreamedMsgs; i++) {
|
||||
Monster monster = GameFactory.createMonsterFromStat(request, i);
|
||||
responseObserver.onNext(monster);
|
||||
}
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamObserver<Monster> getMaxHitPoint(final StreamObserver<Stat> responseObserver) {
|
||||
return computeMinMax(responseObserver, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamObserver<Monster> getMinMaxHitPoints(final StreamObserver<Stat> responseObserver) {
|
||||
return computeMinMax(responseObserver, true);
|
||||
}
|
||||
|
||||
private StreamObserver<Monster> computeMinMax(final StreamObserver<Stat> responseObserver, final boolean includeMin) {
|
||||
final AtomicInteger maxHp = new AtomicInteger(Integer.MIN_VALUE);
|
||||
final AtomicReference<String> maxHpMonsterName = new AtomicReference<String>();
|
||||
final AtomicInteger maxHpCount = new AtomicInteger();
|
||||
|
||||
final AtomicInteger minHp = new AtomicInteger(Integer.MAX_VALUE);
|
||||
final AtomicReference<String> minHpMonsterName = new AtomicReference<String>();
|
||||
final AtomicInteger minHpCount = new AtomicInteger();
|
||||
|
||||
return new StreamObserver<Monster>() {
|
||||
public void onNext(Monster monster) {
|
||||
if (monster.hp() > maxHp.get()) {
|
||||
// Found a monster of higher hit points.
|
||||
maxHp.set(monster.hp());
|
||||
maxHpMonsterName.set(monster.name());
|
||||
maxHpCount.set(1);
|
||||
}
|
||||
else if (monster.hp() == maxHp.get()) {
|
||||
// Count how many times we saw a monster of current max hit points.
|
||||
maxHpCount.getAndIncrement();
|
||||
}
|
||||
|
||||
if (monster.hp() < minHp.get()) {
|
||||
// Found a monster of a lower hit points.
|
||||
minHp.set(monster.hp());
|
||||
minHpMonsterName.set(monster.name());
|
||||
minHpCount.set(1);
|
||||
}
|
||||
else if (monster.hp() == minHp.get()) {
|
||||
// Count how many times we saw a monster of current min hit points.
|
||||
minHpCount.getAndIncrement();
|
||||
}
|
||||
}
|
||||
public void onCompleted() {
|
||||
Stat maxHpStat = GameFactory.createStat(maxHpMonsterName.get(), maxHp.get(), maxHpCount.get());
|
||||
// Send max hit points first.
|
||||
responseObserver.onNext(maxHpStat);
|
||||
if (includeMin) {
|
||||
// Send min hit points.
|
||||
Stat minHpStat = GameFactory.createStat(minHpMonsterName.get(), minHp.get(), minHpCount.get());
|
||||
responseObserver.onNext(minHpStat);
|
||||
}
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
public void onError(Throwable t) {
|
||||
// Not expected
|
||||
Assert.fail();
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@org.junit.BeforeClass
|
||||
public static void startServer() throws IOException {
|
||||
server = ServerBuilder.forPort(0).addService(new MyService()).build().start();
|
||||
int port = server.getPort();
|
||||
channel = ManagedChannelBuilder.forAddress("localhost", port)
|
||||
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
|
||||
// needing certificates.
|
||||
.usePlaintext(true)
|
||||
.directExecutor()
|
||||
.build();
|
||||
blockingStub = MonsterStorageGrpc.newBlockingStub(channel);
|
||||
asyncStub = MonsterStorageGrpc.newStub(channel);
|
||||
}
|
||||
|
||||
@org.junit.Test
|
||||
public void testUnary() throws IOException {
|
||||
Monster monsterRequest = GameFactory.createMonster(BIG_MONSTER_NAME, nestedMonsterHp, nestedMonsterMana);
|
||||
Stat stat = blockingStub.store(monsterRequest);
|
||||
Assert.assertEquals(stat.id(), "Hello " + BIG_MONSTER_NAME);
|
||||
System.out.println("Received stat response from service: " + stat.id());
|
||||
}
|
||||
|
||||
@org.junit.Test
|
||||
public void testServerStreaming() throws IOException {
|
||||
Monster monsterRequest = GameFactory.createMonster(BIG_MONSTER_NAME, nestedMonsterHp, nestedMonsterMana);
|
||||
Stat stat = blockingStub.store(monsterRequest);
|
||||
Iterator<Monster> iterator = blockingStub.retrieve(stat);
|
||||
int counter = 0;
|
||||
while(iterator.hasNext()) {
|
||||
Monster m = iterator.next();
|
||||
System.out.println("Received monster " + m.name());
|
||||
counter ++;
|
||||
}
|
||||
Assert.assertEquals(counter, numStreamedMsgs);
|
||||
System.out.println("FlatBuffers GRPC client/server test: completed successfully");
|
||||
}
|
||||
|
||||
@org.junit.Test
|
||||
public void testClientStreaming() throws IOException, InterruptedException {
|
||||
final AtomicReference<Stat> maxHitStat = new AtomicReference<Stat>();
|
||||
final CountDownLatch streamAlive = new CountDownLatch(1);
|
||||
|
||||
StreamObserver<Stat> statObserver = new StreamObserver<Stat>() {
|
||||
public void onCompleted() {
|
||||
streamAlive.countDown();
|
||||
}
|
||||
public void onError(Throwable ex) { }
|
||||
public void onNext(Stat stat) {
|
||||
maxHitStat.set(stat);
|
||||
}
|
||||
};
|
||||
StreamObserver<Monster> monsterStream = asyncStub.getMaxHitPoint(statObserver);
|
||||
short count = 10;
|
||||
for (short i = 0;i < count; ++i) {
|
||||
Monster monster = GameFactory.createMonster(BIG_MONSTER_NAME + i, (short) (nestedMonsterHp * i), nestedMonsterMana);
|
||||
monsterStream.onNext(monster);
|
||||
}
|
||||
monsterStream.onCompleted();
|
||||
// Wait a little bit for the server to send the stats of the monster with the max hit-points.
|
||||
streamAlive.await(timeoutMs, TimeUnit.MILLISECONDS);
|
||||
Assert.assertEquals(maxHitStat.get().id(), BIG_MONSTER_NAME + (count - 1));
|
||||
Assert.assertEquals(maxHitStat.get().val(), nestedMonsterHp * (count - 1));
|
||||
Assert.assertEquals(maxHitStat.get().count(), 1);
|
||||
}
|
||||
|
||||
@org.junit.Test
|
||||
public void testBiDiStreaming() throws IOException, InterruptedException {
|
||||
final AtomicReference<Stat> maxHitStat = new AtomicReference<Stat>();
|
||||
final AtomicReference<Stat> minHitStat = new AtomicReference<Stat>();
|
||||
final CountDownLatch streamAlive = new CountDownLatch(1);
|
||||
|
||||
StreamObserver<Stat> statObserver = new StreamObserver<Stat>() {
|
||||
public void onCompleted() {
|
||||
streamAlive.countDown();
|
||||
}
|
||||
public void onError(Throwable ex) { }
|
||||
public void onNext(Stat stat) {
|
||||
// We expect the server to send the max stat first and then the min stat.
|
||||
if (maxHitStat.get() == null) {
|
||||
maxHitStat.set(stat);
|
||||
}
|
||||
else {
|
||||
minHitStat.set(stat);
|
||||
}
|
||||
}
|
||||
};
|
||||
StreamObserver<Monster> monsterStream = asyncStub.getMinMaxHitPoints(statObserver);
|
||||
short count = 10;
|
||||
for (short i = 0;i < count; ++i) {
|
||||
Monster monster = GameFactory.createMonster(BIG_MONSTER_NAME + i, (short) (nestedMonsterHp * i), nestedMonsterMana);
|
||||
monsterStream.onNext(monster);
|
||||
}
|
||||
monsterStream.onCompleted();
|
||||
|
||||
// Wait a little bit for the server to send the stats of the monster with the max and min hit-points.
|
||||
streamAlive.await(timeoutMs, TimeUnit.MILLISECONDS);
|
||||
|
||||
Assert.assertEquals(maxHitStat.get().id(), BIG_MONSTER_NAME + (count - 1));
|
||||
Assert.assertEquals(maxHitStat.get().val(), nestedMonsterHp * (count - 1));
|
||||
Assert.assertEquals(maxHitStat.get().count(), 1);
|
||||
|
||||
Assert.assertEquals(minHitStat.get().id(), BIG_MONSTER_NAME + 0);
|
||||
Assert.assertEquals(minHitStat.get().val(), nestedMonsterHp * 0);
|
||||
Assert.assertEquals(minHitStat.get().count(), 1);
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package testing
|
||||
|
||||
import (
|
||||
"../../tests/MyGame/Example"
|
||||
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type server struct{}
|
||||
|
||||
// test used to send and receive in grpc methods
|
||||
var test = "Flatbuffers"
|
||||
var addr = "0.0.0.0:50051"
|
||||
|
||||
// gRPC server store method
|
||||
func (s *server) Store(context context.Context, in *Example.Monster) (*flatbuffers.Builder, error) {
|
||||
b := flatbuffers.NewBuilder(0)
|
||||
i := b.CreateString(test)
|
||||
Example.StatStart(b)
|
||||
Example.StatAddId(b, i)
|
||||
b.Finish(Example.StatEnd(b))
|
||||
return b, nil
|
||||
|
||||
}
|
||||
|
||||
// gRPC server retrieve method
|
||||
func (s *server) Retrieve(context context.Context, in *Example.Stat) (*flatbuffers.Builder, error) {
|
||||
b := flatbuffers.NewBuilder(0)
|
||||
i := b.CreateString(test)
|
||||
Example.MonsterStart(b)
|
||||
Example.MonsterAddName(b, i)
|
||||
b.Finish(Example.MonsterEnd(b))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func StoreClient(c Example.MonsterStorageClient, t *testing.T) {
|
||||
b := flatbuffers.NewBuilder(0)
|
||||
i := b.CreateString(test)
|
||||
Example.MonsterStart(b)
|
||||
Example.MonsterAddName(b, i)
|
||||
b.Finish(Example.MonsterEnd(b))
|
||||
out, err := c.Store(context.Background(), b)
|
||||
if err != nil {
|
||||
t.Fatalf("Store client failed: %v", err)
|
||||
}
|
||||
if string(out.Id()) != test {
|
||||
t.Errorf("StoreClient failed: expected=%s, got=%s\n", test, out.Id())
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func RetrieveClient(c Example.MonsterStorageClient, t *testing.T) {
|
||||
b := flatbuffers.NewBuilder(0)
|
||||
i := b.CreateString(test)
|
||||
Example.StatStart(b)
|
||||
Example.StatAddId(b, i)
|
||||
b.Finish(Example.StatEnd(b))
|
||||
out, err := c.Retrieve(context.Background(), b)
|
||||
if err != nil {
|
||||
t.Fatalf("Retrieve client failed: %v", err)
|
||||
}
|
||||
if string(out.Name()) != test {
|
||||
t.Errorf("RetrieveClient failed: expected=%s, got=%s\n", test, out.Name())
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestGRPC(t *testing.T) {
|
||||
lis, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to listen: %v", err)
|
||||
}
|
||||
ser := grpc.NewServer(grpc.CustomCodec(flatbuffers.FlatbuffersCodec{}))
|
||||
Example.RegisterMonsterStorageServer(ser, &server{})
|
||||
go func() {
|
||||
if err := ser.Serve(lis); err != nil {
|
||||
t.Fatalf("Failed to serve: %v", err)
|
||||
t.FailNow()
|
||||
}
|
||||
}()
|
||||
conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithCodec(flatbuffers.FlatbuffersCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := Example.NewMonsterStorageClient(conn)
|
||||
StoreClient(client, t)
|
||||
RetrieveClient(client, t)
|
||||
}
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include <grpc++/grpc++.h>
|
||||
|
||||
#include "monster_test.grpc.fb.h"
|
||||
#include "monster_test_generated.h"
|
||||
#include "test_assert.h"
|
||||
|
||||
using namespace MyGame::Example;
|
||||
using flatbuffers::grpc::MessageBuilder;
|
||||
using flatbuffers::FlatBufferBuilder;
|
||||
|
||||
void message_builder_tests();
|
||||
|
||||
// The callback implementation of our server, that derives from the generated
|
||||
// code. It implements all rpcs specified in the FlatBuffers schema.
|
||||
class ServiceImpl final : public MyGame::Example::MonsterStorage::Service {
|
||||
virtual ::grpc::Status Store(
|
||||
::grpc::ServerContext *context,
|
||||
const flatbuffers::grpc::Message<Monster> *request,
|
||||
flatbuffers::grpc::Message<Stat> *response) override {
|
||||
// Create a response from the incoming request name.
|
||||
fbb_.Clear();
|
||||
auto stat_offset = CreateStat(
|
||||
fbb_, fbb_.CreateString("Hello, " + request->GetRoot()->name()->str()));
|
||||
fbb_.Finish(stat_offset);
|
||||
// Transfer ownership of the message to gRPC
|
||||
*response = fbb_.ReleaseMessage<Stat>();
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
virtual ::grpc::Status Retrieve(
|
||||
::grpc::ServerContext *context,
|
||||
const flatbuffers::grpc::Message<Stat> *request,
|
||||
::grpc::ServerWriter<flatbuffers::grpc::Message<Monster>> *writer)
|
||||
override {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
fbb_.Clear();
|
||||
// Create 5 monsters for resposne.
|
||||
auto monster_offset =
|
||||
CreateMonster(fbb_, 0, 0, 0,
|
||||
fbb_.CreateString(request->GetRoot()->id()->str() +
|
||||
" No." + std::to_string(i)));
|
||||
fbb_.Finish(monster_offset);
|
||||
|
||||
flatbuffers::grpc::Message<Monster> monster =
|
||||
fbb_.ReleaseMessage<Monster>();
|
||||
|
||||
// Send monster to client using streaming.
|
||||
writer->Write(monster);
|
||||
}
|
||||
return grpc::Status::OK;
|
||||
}
|
||||
|
||||
private:
|
||||
flatbuffers::grpc::MessageBuilder fbb_;
|
||||
};
|
||||
|
||||
// Track the server instance, so we can terminate it later.
|
||||
grpc::Server *server_instance = nullptr;
|
||||
// Mutex to protec this variable.
|
||||
std::mutex wait_for_server;
|
||||
std::condition_variable server_instance_cv;
|
||||
|
||||
// This function implements the server thread.
|
||||
void RunServer() {
|
||||
auto server_address = "0.0.0.0:50051";
|
||||
// Callback interface we implemented above.
|
||||
ServiceImpl service;
|
||||
grpc::ServerBuilder builder;
|
||||
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
|
||||
builder.RegisterService(&service);
|
||||
|
||||
// Start the server. Lock to change the variable we're changing.
|
||||
wait_for_server.lock();
|
||||
server_instance = builder.BuildAndStart().release();
|
||||
wait_for_server.unlock();
|
||||
server_instance_cv.notify_one();
|
||||
|
||||
std::cout << "Server listening on " << server_address << std::endl;
|
||||
// This will block the thread and serve requests.
|
||||
server_instance->Wait();
|
||||
}
|
||||
|
||||
template <class Builder>
|
||||
void StoreRPC(MonsterStorage::Stub *stub) {
|
||||
Builder fbb;
|
||||
grpc::ClientContext context;
|
||||
// Build a request with the name set.
|
||||
auto monster_offset = CreateMonster(fbb, 0, 0, 0, fbb.CreateString("Fred"));
|
||||
MessageBuilder mb(std::move(fbb));
|
||||
mb.Finish(monster_offset);
|
||||
auto request = mb.ReleaseMessage<Monster>();
|
||||
flatbuffers::grpc::Message<Stat> response;
|
||||
|
||||
// The actual RPC.
|
||||
auto status = stub->Store(&context, request, &response);
|
||||
|
||||
if (status.ok()) {
|
||||
auto resp = response.GetRoot()->id();
|
||||
std::cout << "RPC response: " << resp->str() << std::endl;
|
||||
} else {
|
||||
std::cout << "RPC failed" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Builder>
|
||||
void RetrieveRPC(MonsterStorage::Stub *stub) {
|
||||
Builder fbb;
|
||||
grpc::ClientContext context;
|
||||
fbb.Clear();
|
||||
auto stat_offset = CreateStat(fbb, fbb.CreateString("Fred"));
|
||||
fbb.Finish(stat_offset);
|
||||
auto request = MessageBuilder(std::move(fbb)).ReleaseMessage<Stat>();
|
||||
|
||||
flatbuffers::grpc::Message<Monster> response;
|
||||
auto stream = stub->Retrieve(&context, request);
|
||||
while (stream->Read(&response)) {
|
||||
auto resp = response.GetRoot()->name();
|
||||
std::cout << "RPC Streaming response: " << resp->str() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int grpc_server_test() {
|
||||
// Launch server.
|
||||
std::thread server_thread(RunServer);
|
||||
|
||||
// wait for server to spin up.
|
||||
std::unique_lock<std::mutex> lock(wait_for_server);
|
||||
while (!server_instance) server_instance_cv.wait(lock);
|
||||
|
||||
// Now connect the client.
|
||||
auto channel = grpc::CreateChannel("localhost:50051",
|
||||
grpc::InsecureChannelCredentials());
|
||||
auto stub = MyGame::Example::MonsterStorage::NewStub(channel);
|
||||
|
||||
StoreRPC<MessageBuilder>(stub.get());
|
||||
StoreRPC<FlatBufferBuilder>(stub.get());
|
||||
|
||||
RetrieveRPC<MessageBuilder>(stub.get());
|
||||
RetrieveRPC<FlatBufferBuilder>(stub.get());
|
||||
|
||||
|
||||
#if !FLATBUFFERS_GRPC_DISABLE_AUTO_VERIFICATION
|
||||
{
|
||||
// Test that an invalid request errors out correctly
|
||||
grpc::ClientContext context;
|
||||
flatbuffers::grpc::Message<Monster> request; // simulate invalid message
|
||||
flatbuffers::grpc::Message<Stat> response;
|
||||
auto status = stub->Store(&context, request, &response);
|
||||
// The rpc status should be INTERNAL to indicate a verification error. This
|
||||
// matches the protobuf gRPC status code for an unparseable message.
|
||||
assert(!status.ok());
|
||||
assert(status.error_code() == ::grpc::StatusCode::INTERNAL);
|
||||
assert(strcmp(status.error_message().c_str(),
|
||||
"Message verification failed") == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
server_instance->Shutdown();
|
||||
|
||||
server_thread.join();
|
||||
|
||||
delete server_instance;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int /*argc*/, const char * /*argv*/ []) {
|
||||
message_builder_tests();
|
||||
grpc_server_test();
|
||||
|
||||
if (!testing_fails) {
|
||||
TEST_OUTPUT_LINE("ALL TESTS PASSED");
|
||||
return 0;
|
||||
} else {
|
||||
TEST_OUTPUT_LINE("%d FAILED TESTS", testing_fails);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
# NOTE: make sure `mvn install` in /gprc is executed before running this test
|
||||
mvn test
|
||||
@@ -0,0 +1,340 @@
|
||||
#include "flatbuffers/grpc.h"
|
||||
#include "monster_test_generated.h"
|
||||
#include "test_assert.h"
|
||||
#include "test_builder.h"
|
||||
|
||||
using MyGame::Example::Vec3;
|
||||
using MyGame::Example::CreateStat;
|
||||
using MyGame::Example::Any_NONE;
|
||||
|
||||
bool verify(flatbuffers::grpc::Message<Monster> &msg, const std::string &expected_name, Color color) {
|
||||
const Monster *monster = msg.GetRoot();
|
||||
return (monster->name()->str() == expected_name) && (monster->color() == color);
|
||||
}
|
||||
|
||||
bool release_n_verify(flatbuffers::grpc::MessageBuilder &mbb, const std::string &expected_name, Color color) {
|
||||
flatbuffers::grpc::Message<Monster> msg = mbb.ReleaseMessage<Monster>();
|
||||
const Monster *monster = msg.GetRoot();
|
||||
return (monster->name()->str() == expected_name) && (monster->color() == color);
|
||||
}
|
||||
|
||||
void builder_move_assign_after_releaseraw_test(flatbuffers::grpc::MessageBuilder dst) {
|
||||
auto root_offset1 = populate1(dst);
|
||||
dst.Finish(root_offset1);
|
||||
size_t size, offset;
|
||||
grpc_slice slice;
|
||||
dst.ReleaseRaw(size, offset, slice);
|
||||
flatbuffers::FlatBufferBuilder src;
|
||||
auto root_offset2 = populate2(src);
|
||||
src.Finish(root_offset2);
|
||||
auto src_size = src.GetSize();
|
||||
// Move into a released builder.
|
||||
dst = std::move(src);
|
||||
TEST_EQ(dst.GetSize(), src_size);
|
||||
TEST_ASSERT(release_n_verify(dst, m2_name, m2_color));
|
||||
TEST_EQ(src.GetSize(), 0);
|
||||
grpc_slice_unref(slice);
|
||||
}
|
||||
|
||||
template <class SrcBuilder>
|
||||
struct BuilderReuseTests<flatbuffers::grpc::MessageBuilder, SrcBuilder> {
|
||||
static void builder_reusable_after_release_message_test(TestSelector selector) {
|
||||
if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
flatbuffers::grpc::MessageBuilder mb;
|
||||
std::vector<flatbuffers::grpc::Message<Monster>> buffers;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto root_offset1 = populate1(mb);
|
||||
mb.Finish(root_offset1);
|
||||
buffers.push_back(mb.ReleaseMessage<Monster>());
|
||||
TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
|
||||
}
|
||||
}
|
||||
|
||||
static void builder_reusable_after_release_test(TestSelector selector) {
|
||||
if (!selector.count(REUSABLE_AFTER_RELEASE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Populate-Release loop fails assert(GRPC_SLICE_IS_EMPTY(slice_)) in SliceAllocator::allocate
|
||||
// in the second iteration.
|
||||
|
||||
flatbuffers::grpc::MessageBuilder mb;
|
||||
std::vector<flatbuffers::DetachedBuffer> buffers;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto root_offset1 = populate1(mb);
|
||||
mb.Finish(root_offset1);
|
||||
buffers.push_back(mb.Release());
|
||||
TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
|
||||
}
|
||||
}
|
||||
|
||||
static void builder_reusable_after_releaseraw_test(TestSelector selector) {
|
||||
if (!selector.count(REUSABLE_AFTER_RELEASE_RAW)) {
|
||||
return;
|
||||
}
|
||||
|
||||
flatbuffers::grpc::MessageBuilder mb;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto root_offset1 = populate1(mb);
|
||||
mb.Finish(root_offset1);
|
||||
size_t size, offset;
|
||||
grpc_slice slice;
|
||||
const uint8_t *buf = mb.ReleaseRaw(size, offset, slice);
|
||||
TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color));
|
||||
grpc_slice_unref(slice);
|
||||
}
|
||||
}
|
||||
|
||||
static void builder_reusable_after_release_and_move_assign_test(TestSelector selector) {
|
||||
if (!selector.count(REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Release-move_assign loop fails assert(p == GRPC_SLICE_START_PTR(slice_))
|
||||
// in DetachedBuffer destructor after all the iterations
|
||||
|
||||
flatbuffers::grpc::MessageBuilder dst;
|
||||
std::vector<flatbuffers::DetachedBuffer> buffers;
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
auto root_offset1 = populate1(dst);
|
||||
dst.Finish(root_offset1);
|
||||
buffers.push_back(dst.Release());
|
||||
TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
|
||||
|
||||
// bring dst back to life.
|
||||
SrcBuilder src;
|
||||
dst = std::move(src);
|
||||
TEST_EQ_FUNC(dst.GetSize(), 0);
|
||||
TEST_EQ_FUNC(src.GetSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void builder_reusable_after_release_message_and_move_assign_test(TestSelector selector) {
|
||||
if (!selector.count(REUSABLE_AFTER_RELEASE_MESSAGE_AND_MOVE_ASSIGN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
flatbuffers::grpc::MessageBuilder dst;
|
||||
std::vector<flatbuffers::grpc::Message<Monster>> buffers;
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto root_offset1 = populate1(dst);
|
||||
dst.Finish(root_offset1);
|
||||
buffers.push_back(dst.ReleaseMessage<Monster>());
|
||||
TEST_ASSERT_FUNC(verify(buffers[i], m1_name, m1_color));
|
||||
|
||||
// bring dst back to life.
|
||||
SrcBuilder src;
|
||||
dst = std::move(src);
|
||||
TEST_EQ_FUNC(dst.GetSize(), 0);
|
||||
TEST_EQ_FUNC(src.GetSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void builder_reusable_after_releaseraw_and_move_assign_test(TestSelector selector) {
|
||||
if (!selector.count(REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
flatbuffers::grpc::MessageBuilder dst;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
auto root_offset1 = populate1(dst);
|
||||
dst.Finish(root_offset1);
|
||||
size_t size, offset;
|
||||
grpc_slice slice = grpc_empty_slice();
|
||||
const uint8_t *buf = dst.ReleaseRaw(size, offset, slice);
|
||||
TEST_ASSERT_FUNC(verify(buf, offset, m1_name, m1_color));
|
||||
grpc_slice_unref(slice);
|
||||
|
||||
SrcBuilder src;
|
||||
dst = std::move(src);
|
||||
TEST_EQ_FUNC(dst.GetSize(), 0);
|
||||
TEST_EQ_FUNC(src.GetSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void run_tests(TestSelector selector) {
|
||||
builder_reusable_after_release_test(selector);
|
||||
builder_reusable_after_release_message_test(selector);
|
||||
builder_reusable_after_releaseraw_test(selector);
|
||||
builder_reusable_after_release_and_move_assign_test(selector);
|
||||
builder_reusable_after_releaseraw_and_move_assign_test(selector);
|
||||
builder_reusable_after_release_message_and_move_assign_test(selector);
|
||||
}
|
||||
};
|
||||
|
||||
void slice_allocator_tests() {
|
||||
// move-construct no-delete test
|
||||
{
|
||||
size_t size = 2048;
|
||||
flatbuffers::grpc::SliceAllocator sa1;
|
||||
uint8_t *buf = sa1.allocate(size);
|
||||
TEST_ASSERT_FUNC(buf != 0);
|
||||
buf[0] = 100;
|
||||
buf[size-1] = 200;
|
||||
flatbuffers::grpc::SliceAllocator sa2(std::move(sa1));
|
||||
// buf should not be deleted after move-construct
|
||||
TEST_EQ_FUNC(buf[0], 100);
|
||||
TEST_EQ_FUNC(buf[size-1], 200);
|
||||
// buf is freed here
|
||||
}
|
||||
|
||||
// move-assign test
|
||||
{
|
||||
flatbuffers::grpc::SliceAllocator sa1, sa2;
|
||||
uint8_t *buf = sa1.allocate(2048);
|
||||
sa1 = std::move(sa2);
|
||||
// sa1 deletes previously allocated memory in move-assign.
|
||||
// So buf is no longer usable here.
|
||||
TEST_ASSERT_FUNC(buf != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// This function does not populate exactly the first half of the table. But it could.
|
||||
void populate_first_half(MyGame::Example::MonsterBuilder &wrapper, flatbuffers::Offset<flatbuffers::String> name_offset) {
|
||||
wrapper.add_name(name_offset);
|
||||
wrapper.add_color(m1_color);
|
||||
}
|
||||
|
||||
/// This function does not populate exactly the second half of the table. But it could.
|
||||
void populate_second_half(MyGame::Example::MonsterBuilder &wrapper) {
|
||||
wrapper.add_hp(77);
|
||||
wrapper.add_mana(88);
|
||||
Vec3 vec3;
|
||||
wrapper.add_pos(&vec3);
|
||||
}
|
||||
|
||||
/// This function is a hack to update the FlatBufferBuilder reference (fbb_) in the MonsterBuilder object.
|
||||
/// This function will break if fbb_ is not the first member in MonsterBuilder. In that case, some offset must be added.
|
||||
/// This function is used exclusively for testing correctness of move operations between FlatBufferBuilders.
|
||||
/// If MonsterBuilder had a fbb_ pointer, this hack would be unnecessary. That involves a code-generator change though.
|
||||
void test_only_hack_update_fbb_reference(MyGame::Example::MonsterBuilder &monsterBuilder,
|
||||
flatbuffers::grpc::MessageBuilder &mb) {
|
||||
*reinterpret_cast<flatbuffers::FlatBufferBuilder **>(&monsterBuilder) = &mb;
|
||||
}
|
||||
|
||||
/// This test validates correctness of move conversion of FlatBufferBuilder to a MessageBuilder DURING
|
||||
/// a table construction. Half of the table is constructed using FlatBufferBuilder and the other half
|
||||
/// of the table is constructed using a MessageBuilder.
|
||||
void builder_move_ctor_conversion_before_finish_half_n_half_table_test() {
|
||||
for (size_t initial_size = 4 ; initial_size <= 2048; initial_size *= 2) {
|
||||
flatbuffers::FlatBufferBuilder fbb(initial_size);
|
||||
auto name_offset = fbb.CreateString(m1_name);
|
||||
MyGame::Example::MonsterBuilder monsterBuilder(fbb); // starts a table in FlatBufferBuilder
|
||||
populate_first_half(monsterBuilder, name_offset);
|
||||
flatbuffers::grpc::MessageBuilder mb(std::move(fbb));
|
||||
test_only_hack_update_fbb_reference(monsterBuilder, mb); // hack
|
||||
populate_second_half(monsterBuilder);
|
||||
mb.Finish(monsterBuilder.Finish()); // ends the table in MessageBuilder
|
||||
TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
|
||||
TEST_EQ_FUNC(fbb.GetSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// This test populates a COMPLETE inner table before move conversion and later populates more members in the outer table.
|
||||
void builder_move_ctor_conversion_before_finish_test() {
|
||||
for (size_t initial_size = 4 ; initial_size <= 2048; initial_size *= 2) {
|
||||
flatbuffers::FlatBufferBuilder fbb(initial_size);
|
||||
auto stat_offset = CreateStat(fbb, fbb.CreateString("SomeId"), 0, 0);
|
||||
flatbuffers::grpc::MessageBuilder mb(std::move(fbb));
|
||||
auto monster_offset = CreateMonster(mb, 0, 150, 100, mb.CreateString(m1_name), 0, m1_color, Any_NONE, 0, 0, 0, 0, 0, 0, stat_offset);
|
||||
mb.Finish(monster_offset);
|
||||
TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
|
||||
TEST_EQ_FUNC(fbb.GetSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// This test validates correctness of move conversion of FlatBufferBuilder to a MessageBuilder DURING
|
||||
/// a table construction. Half of the table is constructed using FlatBufferBuilder and the other half
|
||||
/// of the table is constructed using a MessageBuilder.
|
||||
void builder_move_assign_conversion_before_finish_half_n_half_table_test() {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
flatbuffers::grpc::MessageBuilder mb;
|
||||
|
||||
for (int i = 0;i < 5; ++i) {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
auto name_offset = fbb.CreateString(m1_name);
|
||||
MyGame::Example::MonsterBuilder monsterBuilder(fbb); // starts a table in FlatBufferBuilder
|
||||
populate_first_half(monsterBuilder, name_offset);
|
||||
mb = std::move(fbb);
|
||||
test_only_hack_update_fbb_reference(monsterBuilder, mb); // hack
|
||||
populate_second_half(monsterBuilder);
|
||||
mb.Finish(monsterBuilder.Finish()); // ends the table in MessageBuilder
|
||||
TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
|
||||
TEST_EQ_FUNC(fbb.GetSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// This test populates a COMPLETE inner table before move conversion and later populates more members in the outer table.
|
||||
void builder_move_assign_conversion_before_finish_test() {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
flatbuffers::grpc::MessageBuilder mb;
|
||||
|
||||
for (int i = 0;i < 5; ++i) {
|
||||
auto stat_offset = CreateStat(fbb, fbb.CreateString("SomeId"), 0, 0);
|
||||
mb = std::move(fbb);
|
||||
auto monster_offset = CreateMonster(mb, 0, 150, 100, mb.CreateString(m1_name), 0, m1_color, Any_NONE, 0, 0, 0, 0, 0, 0, stat_offset);
|
||||
mb.Finish(monster_offset);
|
||||
TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
|
||||
TEST_EQ_FUNC(fbb.GetSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// This test populates data, finishes the buffer, and does move conversion after.
|
||||
void builder_move_ctor_conversion_after_finish_test() {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
fbb.Finish(populate1(fbb));
|
||||
flatbuffers::grpc::MessageBuilder mb(std::move(fbb));
|
||||
TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
|
||||
TEST_EQ_FUNC(fbb.GetSize(), 0);
|
||||
}
|
||||
|
||||
/// This test populates data, finishes the buffer, and does move conversion after.
|
||||
void builder_move_assign_conversion_after_finish_test() {
|
||||
flatbuffers::FlatBufferBuilder fbb;
|
||||
flatbuffers::grpc::MessageBuilder mb;
|
||||
|
||||
for (int i = 0;i < 5; ++i) {
|
||||
fbb.Finish(populate1(fbb));
|
||||
mb = std::move(fbb);
|
||||
TEST_ASSERT_FUNC(release_n_verify(mb, m1_name, m1_color));
|
||||
TEST_EQ_FUNC(fbb.GetSize(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void message_builder_tests() {
|
||||
using flatbuffers::grpc::MessageBuilder;
|
||||
using flatbuffers::FlatBufferBuilder;
|
||||
|
||||
slice_allocator_tests();
|
||||
|
||||
#ifndef __APPLE__
|
||||
builder_move_ctor_conversion_before_finish_half_n_half_table_test();
|
||||
builder_move_assign_conversion_before_finish_half_n_half_table_test();
|
||||
#endif // __APPLE__
|
||||
builder_move_ctor_conversion_before_finish_test();
|
||||
builder_move_assign_conversion_before_finish_test();
|
||||
|
||||
builder_move_ctor_conversion_after_finish_test();
|
||||
builder_move_assign_conversion_after_finish_test();
|
||||
|
||||
BuilderTests<MessageBuilder, MessageBuilder>::all_tests();
|
||||
BuilderTests<MessageBuilder, FlatBufferBuilder>::all_tests();
|
||||
|
||||
BuilderReuseTestSelector tests[6] = {
|
||||
//REUSABLE_AFTER_RELEASE, // Assertion failed: (GRPC_SLICE_IS_EMPTY(slice_))
|
||||
//REUSABLE_AFTER_RELEASE_AND_MOVE_ASSIGN, // Assertion failed: (p == GRPC_SLICE_START_PTR(slice_)
|
||||
|
||||
REUSABLE_AFTER_RELEASE_RAW,
|
||||
REUSABLE_AFTER_RELEASE_MESSAGE,
|
||||
REUSABLE_AFTER_RELEASE_MESSAGE_AND_MOVE_ASSIGN,
|
||||
REUSABLE_AFTER_RELEASE_RAW_AND_MOVE_ASSIGN
|
||||
};
|
||||
|
||||
BuilderReuseTests<MessageBuilder, MessageBuilder>::run_tests(TestSelector(tests, tests+6));
|
||||
BuilderReuseTests<MessageBuilder, FlatBufferBuilder>::run_tests(TestSelector(tests, tests+6));
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.google.flatbuffers</groupId>
|
||||
<artifactId>flatbuffers-parent</artifactId>
|
||||
<version>1.11.1</version>
|
||||
</parent>
|
||||
<artifactId>grpc-test</artifactId>
|
||||
<description>Example/Test project demonstrating usage of flatbuffers with GRPC-Java instead of protobufs
|
||||
</description>
|
||||
<properties>
|
||||
<gRPC.version>1.11.1</gRPC.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.google.flatbuffers</groupId>
|
||||
<artifactId>flatbuffers-java</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.flatbuffers</groupId>
|
||||
<artifactId>flatbuffers-java-grpc</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
<version>${gRPC.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-netty</artifactId>
|
||||
<version>${gRPC.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-test-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${project.basedir}</source>
|
||||
<source>${project.basedir}/../../tests</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<!--<testSourceDirectory>${project.basedir}</testSourceDirectory>-->
|
||||
</build>
|
||||
</project>
|
||||
|
||||
Reference in New Issue
Block a user