Flatbuffers library added to the list of third party libraries.

This commit is contained in:
Denis Chaplygin
2019-08-02 10:46:23 +03:00
parent 59a83bd537
commit 3f34c8d88c
604 changed files with 126053 additions and 0 deletions
+31
View File
@@ -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
View File
@@ -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>
@@ -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);
}
}
};
}
}
+213
View File
@@ -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>
+85
View File
@@ -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");
}
+80
View File
@@ -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;
}
+40
View File
@@ -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 &params);
// Return the includes needed for generated header file.
grpc::string GetHeaderIncludes(grpc_generator::File *file,
const Parameters &params);
// Return the includes needed for generated source file.
grpc::string GetSourceIncludes(grpc_generator::File *file,
const Parameters &params);
// Return the epilogue of the generated header file.
grpc::string GetHeaderEpilogue(grpc_generator::File *file,
const Parameters &params);
// Return the prologue of the generated source file.
grpc::string GetSourcePrologue(grpc_generator::File *file,
const Parameters &params);
// Return the services for generated header file.
grpc::string GetHeaderServices(grpc_generator::File *file,
const Parameters &params);
// Return the services for generated source file.
grpc::string GetSourceServices(grpc_generator::File *file,
const Parameters &params);
// Return the epilogue of the generated source file.
grpc::string GetSourceEpilogue(grpc_generator::File *file,
const Parameters &params);
// Return the prologue of the generated mock file.
grpc::string GetMockPrologue(grpc_generator::File *file,
const Parameters &params);
// Return the includes needed for generated mock file.
grpc::string GetMockIncludes(grpc_generator::File *file,
const Parameters &params);
// Return the services for generated mock file.
grpc::string GetMockServices(grpc_generator::File *file,
const Parameters &params);
// Return the epilogue of generated mock file.
grpc::string GetMockEpilogue(grpc_generator::File *file,
const Parameters &params);
// Return the prologue of the generated mock file.
grpc::string GetMockPrologue(grpc_generator::File *file,
const Parameters &params);
// Return the includes needed for generated mock file.
grpc::string GetMockIncludes(grpc_generator::File *file,
const Parameters &params);
// Return the services for generated mock file.
grpc::string GetMockServices(grpc_generator::File *file,
const Parameters &params);
// Return the epilogue of generated mock file.
grpc::string GetMockEpilogue(grpc_generator::File *file,
const Parameters &params);
} // 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
+42
View File
@@ -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;
}
}
+242
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>