You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by ea...@apache.org on 2022/06/29 06:19:55 UTC
[dubbo] branch 3.0 updated: [3.0-Triple] Support reflection api (#10168)
This is an automated email from the ASF dual-hosted git repository.
earthchen pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.0 by this push:
new 47f7be2fd6 [3.0-Triple] Support reflection api (#10168)
47f7be2fd6 is described below
commit 47f7be2fd6137dc01433d9cd0e37204b0d350686
Author: GuoHao <gu...@gmail.com>
AuthorDate: Wed Jun 29 14:19:48 2022 +0800
[3.0-Triple] Support reflection api (#10168)
* Add proto
* Update compiler
* Add v1Alpha for compatibility
* Abstract builtin service export
* Try add get fd by name
* get service by symbol now works
* Add type
* Support extension
* Ignore reflection code scan
* Remove v1
---
.../org/apache/dubbo/gen/AbstractGenerator.java | 66 ++++---
.../src/main/resources/Dubbo3TripleStub.mustache | 1 +
.../org/apache/dubbo/rpc/stub/StubSuppliers.java | 1 -
.../tri/service/ReflectionV1AlphaService.java | 202 +++++++++++++++++++++
.../tri/service/SchemaDescriptorRegistry.java | 90 +++++++++
.../protocol/tri/service/TriBuiltinService.java | 25 ++-
.../src/main/proto/reflectionV1Alpha.proto | 144 +++++++++++++++
pom.xml | 1 +
8 files changed, 500 insertions(+), 30 deletions(-)
diff --git a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java
index 587e9c310f..8d95ecdf87 100644
--- a/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java
+++ b/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java
@@ -72,22 +72,25 @@ public abstract class AbstractGenerator extends Generator {
}
@Override
- public List<PluginProtos.CodeGeneratorResponse.File> generateFiles(PluginProtos.CodeGeneratorRequest request) throws GeneratorException {
+ public List<PluginProtos.CodeGeneratorResponse.File> generateFiles(
+ PluginProtos.CodeGeneratorRequest request) throws GeneratorException {
final ProtoTypeMap typeMap = ProtoTypeMap.of(request.getProtoFileList());
List<FileDescriptorProto> protosToGenerate = request.getProtoFileList().stream()
- .filter(protoFile -> request.getFileToGenerateList().contains(protoFile.getName()))
- .collect(Collectors.toList());
+ .filter(protoFile -> request.getFileToGenerateList().contains(protoFile.getName()))
+ .collect(Collectors.toList());
List<ServiceContext> services = findServices(protosToGenerate, typeMap);
return generateFiles(services);
}
- private List<ServiceContext> findServices(List<FileDescriptorProto> protos, ProtoTypeMap typeMap) {
+ private List<ServiceContext> findServices(List<FileDescriptorProto> protos,
+ ProtoTypeMap typeMap) {
List<ServiceContext> contexts = new ArrayList<>();
protos.forEach(fileProto -> {
- for (int serviceNumber = 0; serviceNumber < fileProto.getServiceCount(); serviceNumber++) {
+ for (int serviceNumber = 0; serviceNumber < fileProto.getServiceCount();
+ serviceNumber++) {
ServiceContext serviceContext = buildServiceContext(
fileProto.getService(serviceNumber),
typeMap,
@@ -96,8 +99,12 @@ public abstract class AbstractGenerator extends Generator {
);
serviceContext.protoName = fileProto.getName();
serviceContext.packageName = extractPackageName(fileProto);
+ if (!Strings.isNullOrEmpty(fileProto.getOptions().getJavaOuterClassname())) {
+ serviceContext.outerClassName = fileProto.getOptions().getJavaOuterClassname();
+ }
serviceContext.commonPackageName = extractCommonPackageName(fileProto);
- serviceContext.multipleFiles = fileProto.getOptions() != null && fileProto.getOptions().getJavaMultipleFiles();
+ serviceContext.multipleFiles =
+ fileProto.getOptions() != null && fileProto.getOptions().getJavaMultipleFiles();
contexts.add(serviceContext);
}
});
@@ -121,15 +128,18 @@ public abstract class AbstractGenerator extends Generator {
return Strings.nullToEmpty(proto.getPackage());
}
- private ServiceContext buildServiceContext(ServiceDescriptorProto serviceProto, ProtoTypeMap typeMap, List<Location> locations, int serviceNumber) {
+ private ServiceContext buildServiceContext(ServiceDescriptorProto serviceProto,
+ ProtoTypeMap typeMap, List<Location> locations, int serviceNumber) {
ServiceContext serviceContext = new ServiceContext();
- serviceContext.fileName = getClassPrefix() + serviceProto.getName() + getClassSuffix() + ".java";
+ serviceContext.fileName =
+ getClassPrefix() + serviceProto.getName() + getClassSuffix() + ".java";
serviceContext.className = getClassPrefix() + serviceProto.getName() + getClassSuffix();
-
+ serviceContext.outerClassName = serviceProto.getName() + "OuterClass";
serviceContext.interfaceFileName = serviceProto.getName() + ".java";
serviceContext.interfaceClassName = serviceProto.getName();
serviceContext.serviceName = serviceProto.getName();
- serviceContext.deprecated = serviceProto.getOptions() != null && serviceProto.getOptions().getDeprecated();
+ serviceContext.deprecated =
+ serviceProto.getOptions() != null && serviceProto.getOptions().getDeprecated();
List<Location> allLocationsForService = locations.stream()
.filter(location ->
@@ -143,7 +153,8 @@ public abstract class AbstractGenerator extends Generator {
.filter(location -> location.getPathCount() == SERVICE_NUMBER_OF_PATHS)
.findFirst()
.orElseGet(Location::getDefaultInstance);
- serviceContext.javaDoc = getJavaDoc(getComments(serviceLocation), getServiceJavaDocPrefix());
+ serviceContext.javaDoc = getJavaDoc(getComments(serviceLocation),
+ getServiceJavaDocPrefix());
for (int methodNumber = 0; methodNumber < serviceProto.getMethodCount(); methodNumber++) {
MethodContext methodContext = buildMethodContext(
@@ -160,13 +171,15 @@ public abstract class AbstractGenerator extends Generator {
return serviceContext;
}
- private MethodContext buildMethodContext(MethodDescriptorProto methodProto, ProtoTypeMap typeMap, List<Location> locations, int methodNumber) {
+ private MethodContext buildMethodContext(MethodDescriptorProto methodProto,
+ ProtoTypeMap typeMap, List<Location> locations, int methodNumber) {
MethodContext methodContext = new MethodContext();
methodContext.originMethodName = methodProto.getName();
methodContext.methodName = lowerCaseFirst(methodProto.getName());
methodContext.inputType = typeMap.toJavaTypeName(methodProto.getInputType());
methodContext.outputType = typeMap.toJavaTypeName(methodProto.getOutputType());
- methodContext.deprecated = methodProto.getOptions() != null && methodProto.getOptions().getDeprecated();
+ methodContext.deprecated =
+ methodProto.getOptions() != null && methodProto.getOptions().getDeprecated();
methodContext.isManyInput = methodProto.getClientStreaming();
methodContext.isManyOutput = methodProto.getServerStreaming();
methodContext.methodNumber = methodNumber;
@@ -203,7 +216,8 @@ public abstract class AbstractGenerator extends Generator {
return Character.toLowerCase(s.charAt(0)) + s.substring(1);
}
- private List<PluginProtos.CodeGeneratorResponse.File> generateFiles(List<ServiceContext> services) {
+ private List<PluginProtos.CodeGeneratorResponse.File> generateFiles(
+ List<ServiceContext> services) {
List<PluginProtos.CodeGeneratorResponse.File> allServiceFiles = new ArrayList<>();
for (ServiceContext context : services) {
List<PluginProtos.CodeGeneratorResponse.File> files = buildFile(context);
@@ -212,7 +226,7 @@ public abstract class AbstractGenerator extends Generator {
return allServiceFiles;
}
- protected boolean enableMultipleTemplateFiles(){
+ protected boolean enableMultipleTemplateFiles() {
return false;
}
@@ -266,7 +280,8 @@ public abstract class AbstractGenerator extends Generator {
}
private String getComments(Location location) {
- return location.getLeadingComments().isEmpty() ? location.getTrailingComments() : location.getLeadingComments();
+ return location.getLeadingComments().isEmpty() ? location.getTrailingComments()
+ : location.getLeadingComments();
}
private String getJavaDoc(String comments, String prefix) {
@@ -288,6 +303,7 @@ public abstract class AbstractGenerator extends Generator {
* Template class for proto Service objects.
*/
private class ServiceContext {
+
// CHECKSTYLE DISABLE VisibilityModifier FOR 8 LINES
public String fileName;
public String interfaceFileName;
@@ -300,6 +316,8 @@ public abstract class AbstractGenerator extends Generator {
public boolean deprecated;
public String javaDoc;
public boolean multipleFiles;
+
+ public String outerClassName;
public List<MethodContext> methods = new ArrayList<>();
public Set<String> methodTypes = new HashSet<>();
@@ -309,11 +327,13 @@ public abstract class AbstractGenerator extends Generator {
}
public List<MethodContext> unaryMethods() {
- return methods.stream().filter(m -> (!m.isManyInput && !m.isManyOutput)).collect(Collectors.toList());
+ return methods.stream().filter(m -> (!m.isManyInput && !m.isManyOutput))
+ .collect(Collectors.toList());
}
public List<MethodContext> serverStreamingMethods() {
- return methods.stream().filter(m -> !m.isManyInput && m.isManyOutput).collect(Collectors.toList());
+ return methods.stream().filter(m -> !m.isManyInput && m.isManyOutput)
+ .collect(Collectors.toList());
}
public List<MethodContext> biStreamingMethods() {
@@ -321,11 +341,13 @@ public abstract class AbstractGenerator extends Generator {
}
public List<MethodContext> biStreamingWithoutClientStreamMethods() {
- return methods.stream().filter(m->m.isManyInput&&m.isManyOutput).collect(Collectors.toList());
+ return methods.stream().filter(m -> m.isManyInput && m.isManyOutput)
+ .collect(Collectors.toList());
}
public List<MethodContext> clientStreamingMethods() {
- return methods.stream().filter(m->m.isManyInput&&!m.isManyOutput).collect(Collectors.toList());
+ return methods.stream().filter(m -> m.isManyInput && !m.isManyOutput)
+ .collect(Collectors.toList());
}
public List<MethodContext> methods() {
@@ -338,6 +360,7 @@ public abstract class AbstractGenerator extends Generator {
* Template class for proto RPC objects.
*/
private class MethodContext {
+
// CHECKSTYLE DISABLE VisibilityModifier FOR 10 LINES
public String originMethodName;
public String methodName;
@@ -358,7 +381,8 @@ public abstract class AbstractGenerator extends Generator {
for (int i = 0; i < methodName.length(); i++) {
char c = methodName.charAt(i);
s.append(Character.toUpperCase(c));
- if ((i < methodName.length() - 1) && Character.isLowerCase(c) && Character.isUpperCase(methodName.charAt(i + 1))) {
+ if ((i < methodName.length() - 1) && Character.isLowerCase(c)
+ && Character.isUpperCase(methodName.charAt(i + 1))) {
s.append('_');
}
}
diff --git a/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache b/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache
index d6f6fa9fc2..77645108de 100644
--- a/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache
+++ b/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache
@@ -52,6 +52,7 @@ public final class {{className}} {
private static final StubServiceDescriptor serviceDescriptor = new StubServiceDescriptor(SERVICE_NAME,{{interfaceClassName}}.class);
static {
+ org.apache.dubbo.rpc.protocol.tri.service.SchemaDescriptorRegistry.addSchemaDescriptor(SERVICE_NAME,{{outerClassName}}.getDescriptor());
StubSuppliers.addSupplier(SERVICE_NAME, {{className}}::newStub);
StubSuppliers.addSupplier({{interfaceClassName}}.JAVA_SERVICE_NAME, {{className}}::newStub);
StubSuppliers.addDescriptor(SERVICE_NAME, serviceDescriptor);
diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubSuppliers.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubSuppliers.java
index 6096caca0a..a56e52f643 100644
--- a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubSuppliers.java
+++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/StubSuppliers.java
@@ -33,7 +33,6 @@ public class StubSuppliers {
public static void addDescriptor(String interfaceName, ServiceDescriptor serviceDescriptor) {
SERVICE_DESCRIPTOR_MAP.put(interfaceName, serviceDescriptor);
}
-
public static void addSupplier(String interfaceName, Function<Invoker<?>, Object> supplier) {
STUB_SUPPLIERS.put(interfaceName, supplier);
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/ReflectionV1AlphaService.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/ReflectionV1AlphaService.java
new file mode 100644
index 0000000000..b1a2659eb6
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/ReflectionV1AlphaService.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.service;
+
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.TriRpcStatus;
+
+import com.google.protobuf.Descriptors.FileDescriptor;
+import io.grpc.reflection.v1alpha.DubboServerReflectionTriple;
+import io.grpc.reflection.v1alpha.ErrorResponse;
+import io.grpc.reflection.v1alpha.ExtensionNumberResponse;
+import io.grpc.reflection.v1alpha.ExtensionRequest;
+import io.grpc.reflection.v1alpha.FileDescriptorResponse;
+import io.grpc.reflection.v1alpha.ListServiceResponse;
+import io.grpc.reflection.v1alpha.ServerReflectionRequest;
+import io.grpc.reflection.v1alpha.ServerReflectionResponse;
+import io.grpc.reflection.v1alpha.ServiceResponse;
+
+import java.util.ArrayDeque;
+import java.util.HashSet;
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ * Provides a reflection service for Protobuf service for service test and dynamic gateway.
+ *
+ * @link https://github.com/grpc/grpc/blob/master/doc/server-reflection.md
+ */
+public class ReflectionV1AlphaService extends DubboServerReflectionTriple.ServerReflectionImplBase {
+
+
+ @Override
+ public StreamObserver<ServerReflectionRequest> serverReflectionInfo(
+ StreamObserver<ServerReflectionResponse> responseObserver) {
+ return new StreamObserver<ServerReflectionRequest>() {
+ @Override
+ public void onNext(ServerReflectionRequest request) {
+ switch (request.getMessageRequestCase()) {
+ case FILE_BY_FILENAME:
+ getFileByName(request, responseObserver);
+ break;
+ case FILE_CONTAINING_SYMBOL:
+ getFileContainingSymbol(request, responseObserver);
+ break;
+ case FILE_CONTAINING_EXTENSION:
+ getFileByExtension(request, responseObserver);
+ break;
+ case ALL_EXTENSION_NUMBERS_OF_TYPE:
+ getAllExtensions(request, responseObserver);
+ break;
+ case LIST_SERVICES:
+ listServices(request, responseObserver);
+ break;
+ default:
+ sendErrorResponse(
+ request,
+ TriRpcStatus.Code.UNIMPLEMENTED,
+ "not implemented " + request.getMessageRequestCase(), responseObserver);
+ }
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ responseObserver.onError(throwable);
+ }
+
+ @Override
+ public void onCompleted() {
+ responseObserver.onCompleted();
+ }
+ };
+ }
+
+ private void getFileByName(ServerReflectionRequest request,
+ StreamObserver<ServerReflectionResponse> responseObserver) {
+ String name = request.getFileByFilename();
+ FileDescriptor fd = SchemaDescriptorRegistry.getSchemaDescriptor(name);
+ if (fd != null) {
+ responseObserver.onNext(createServerReflectionResponse(request, fd));
+ } else {
+ sendErrorResponse(request, TriRpcStatus.Code.NOT_FOUND, "File not found.",
+ responseObserver);
+ }
+ }
+
+ private void getFileContainingSymbol(ServerReflectionRequest request,
+ StreamObserver<ServerReflectionResponse> responseObserver) {
+ String symbol = request.getFileContainingSymbol();
+ FileDescriptor fd = SchemaDescriptorRegistry.getSchemaDescriptor(symbol);
+ if (fd != null) {
+ responseObserver.onNext(createServerReflectionResponse(request, fd));
+ } else {
+ sendErrorResponse(request, TriRpcStatus.Code.NOT_FOUND, "Symbol not found.",
+ responseObserver);
+ }
+ }
+
+ private void getFileByExtension(ServerReflectionRequest request,
+ StreamObserver<ServerReflectionResponse> responseObserver) {
+ ExtensionRequest extensionRequest = request.getFileContainingExtension();
+ String type = extensionRequest.getContainingType();
+ int extension = extensionRequest.getExtensionNumber();
+ FileDescriptor fd =
+ SchemaDescriptorRegistry.getFileDescriptorByExtensionAndNumber(type, extension);
+ if (fd != null) {
+ responseObserver.onNext(createServerReflectionResponse(request, fd));
+ } else {
+ sendErrorResponse(request, TriRpcStatus.Code.NOT_FOUND, "Extension not found.",
+ responseObserver);
+ }
+ }
+
+ private void getAllExtensions(ServerReflectionRequest request,
+ StreamObserver<ServerReflectionResponse> responseObserver) {
+ String type = request.getAllExtensionNumbersOfType();
+ Set<Integer> extensions = SchemaDescriptorRegistry.getExtensionNumbers(type);
+ if (extensions != null) {
+ ExtensionNumberResponse.Builder builder =
+ ExtensionNumberResponse.newBuilder()
+ .setBaseTypeName(type)
+ .addAllExtensionNumber(extensions);
+ responseObserver.onNext(
+ ServerReflectionResponse.newBuilder()
+ .setValidHost(request.getHost())
+ .setOriginalRequest(request)
+ .setAllExtensionNumbersResponse(builder)
+ .build());
+ } else {
+ sendErrorResponse(request, TriRpcStatus.Code.NOT_FOUND, "Type not found.",
+ responseObserver);
+ }
+ }
+
+ private void listServices(ServerReflectionRequest request,
+ StreamObserver<ServerReflectionResponse> responseObserver) {
+ ListServiceResponse.Builder builder = ListServiceResponse.newBuilder();
+
+ for (String serviceName : SchemaDescriptorRegistry.listServiceNames()) {
+ builder.addService(ServiceResponse.newBuilder().setName(serviceName));
+ }
+ responseObserver.onNext(
+ ServerReflectionResponse.newBuilder()
+ .setValidHost(request.getHost())
+ .setOriginalRequest(request)
+ .setListServicesResponse(builder)
+ .build());
+ }
+
+ private void sendErrorResponse(
+ ServerReflectionRequest request, TriRpcStatus.Code code, String message,
+ StreamObserver<ServerReflectionResponse> responseObserver) {
+ ServerReflectionResponse response =
+ ServerReflectionResponse.newBuilder()
+ .setValidHost(request.getHost())
+ .setOriginalRequest(request)
+ .setErrorResponse(
+ ErrorResponse.newBuilder()
+ .setErrorCode(code.code)
+ .setErrorMessage(message))
+ .build();
+ responseObserver.onNext(response);
+ }
+
+ private ServerReflectionResponse createServerReflectionResponse(
+ ServerReflectionRequest request, FileDescriptor fd) {
+ FileDescriptorResponse.Builder fdRBuilder = FileDescriptorResponse.newBuilder();
+ Set<String> seenFiles = new HashSet<>();
+ Queue<FileDescriptor> frontier = new ArrayDeque<>();
+ seenFiles.add(fd.getName());
+ frontier.add(fd);
+ while (!frontier.isEmpty()) {
+ FileDescriptor nextFd = frontier.remove();
+ fdRBuilder.addFileDescriptorProto(nextFd.toProto().toByteString());
+ for (FileDescriptor dependencyFd : nextFd.getDependencies()) {
+ if (!seenFiles.contains(dependencyFd.getName())) {
+ seenFiles.add(dependencyFd.getName());
+ frontier.add(dependencyFd);
+ }
+ }
+ }
+ return ServerReflectionResponse.newBuilder()
+ .setValidHost(request.getHost())
+ .setOriginalRequest(request)
+ .setFileDescriptorResponse(fdRBuilder)
+ .build();
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/SchemaDescriptorRegistry.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/SchemaDescriptorRegistry.java
new file mode 100644
index 0000000000..3d37505ae1
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/SchemaDescriptorRegistry.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dubbo.rpc.protocol.tri.service;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Descriptors.FileDescriptor;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class SchemaDescriptorRegistry {
+
+ private static final Map<String, FileDescriptor> DESCRIPTORS_BY_SYMBOL = new ConcurrentHashMap<>();
+
+ private static final Map<String, Map<Integer, FileDescriptor>> EXTENSIONS = new ConcurrentHashMap<>();
+
+ private static final Set<String> SERVICES = new HashSet<>();
+
+ public static void addSchemaDescriptor(String serviceName, FileDescriptor fd) {
+ SERVICES.add(serviceName);
+ DESCRIPTORS_BY_SYMBOL.put(serviceName, fd);
+ for (Descriptor messageType : fd.getMessageTypes()) {
+ addType(messageType);
+ }
+ for (FieldDescriptor extension : fd.getExtensions()) {
+ addExtension(extension, fd);
+ }
+ }
+
+ private static void addType(Descriptor descriptor) {
+ DESCRIPTORS_BY_SYMBOL.put(descriptor.getFullName(), descriptor.getFile());
+ for (Descriptor nestedType : descriptor.getNestedTypes()) {
+ addType(nestedType);
+ }
+ }
+
+ private static void addExtension(FieldDescriptor extension, FileDescriptor fd) {
+ String name = extension.getContainingType().getFullName();
+ int number = extension.getNumber();
+ if (!EXTENSIONS.containsKey(name)) {
+ EXTENSIONS.put(name, new HashMap<>());
+ }
+ Map<Integer, FileDescriptor> fdMap = EXTENSIONS.get(name);
+ fdMap.put(number, fd);
+ }
+
+ public static FileDescriptor getFileDescriptorByExtensionAndNumber(String extension,
+ int number) {
+ return EXTENSIONS.getOrDefault(extension, Collections.emptyMap()).get(number);
+ }
+
+ public static Set<Integer> getExtensionNumbers(String extension) {
+ Map<Integer, FileDescriptor> ret = EXTENSIONS.get(extension);
+ if (ret == null) {
+ return null;
+ } else {
+ return ret.keySet();
+ }
+ }
+
+ public static FileDescriptor getSchemaDescriptor(String serviceName) {
+ return DESCRIPTORS_BY_SYMBOL.get(serviceName);
+ }
+
+ public static List<String> listServiceNames() {
+ return new ArrayList<>(SERVICES);
+ }
+}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java
index 35d6ad48ea..2bf3ca0ece 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/service/TriBuiltinService.java
@@ -24,6 +24,7 @@ import org.apache.dubbo.rpc.PathResolver;
import org.apache.dubbo.rpc.ProxyFactory;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.model.ModuleModel;
import io.grpc.health.v1.DubboHealthTriple;
import io.grpc.health.v1.Health;
@@ -44,6 +45,7 @@ public class TriBuiltinService {
private final Health healthService;
+ private final ReflectionV1AlphaService reflectionServiceV1Alpha;
private final HealthStatusManager healthStatusManager;
private final AtomicBoolean init = new AtomicBoolean();
@@ -51,6 +53,7 @@ public class TriBuiltinService {
public TriBuiltinService(FrameworkModel frameworkModel) {
healthStatusManager = new HealthStatusManager(new TriHealthImpl());
healthService = healthStatusManager.getHealthService();
+ reflectionServiceV1Alpha = new ReflectionV1AlphaService();
proxyFactory = frameworkModel.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
pathResolver = frameworkModel.getExtensionLoader(PathResolver.class).getDefaultExtension();
init();
@@ -58,17 +61,23 @@ public class TriBuiltinService {
public void init() {
if (init.compareAndSet(false, true)) {
- URL url = new ServiceConfigURL(CommonConstants.TRIPLE, null, null, ANYHOST_VALUE, 0,
- DubboHealthTriple.SERVICE_NAME)
- .addParameter(PROXY_KEY, CommonConstants.NATIVE_STUB)
- .setScopeModel(ApplicationModel.defaultModel().getInternalModule());
- Invoker<?> invoker = proxyFactory.getInvoker(healthService, Health.class, url);
- pathResolver.add(DubboHealthTriple.SERVICE_NAME, invoker);
- ApplicationModel.defaultModel().getInternalModule()
- .addDestroyListener(scopeModel -> pathResolver.remove(DubboHealthTriple.SERVICE_NAME));
+ addSingleBuiltinService(DubboHealthTriple.SERVICE_NAME, healthService, Health.class);
+ addSingleBuiltinService(ReflectionV1AlphaService.SERVICE_NAME, reflectionServiceV1Alpha,
+ ReflectionV1AlphaService.class);
}
}
+ private <T> void addSingleBuiltinService(String serviceName, T impl, Class<T> interfaceClass) {
+ ModuleModel internalModule = ApplicationModel.defaultModel().getInternalModule();
+ URL url = new ServiceConfigURL(CommonConstants.TRIPLE, null, null, ANYHOST_VALUE, 0,
+ serviceName)
+ .addParameter(PROXY_KEY, CommonConstants.NATIVE_STUB)
+ .setScopeModel(internalModule);
+ Invoker<?> invoker = proxyFactory.getInvoker(impl, interfaceClass, url);
+ pathResolver.add(serviceName, invoker);
+ internalModule.addDestroyListener(scopeModel -> pathResolver.remove(serviceName));
+ }
+
public HealthStatusManager getHealthStatusManager() {
return healthStatusManager;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/proto/reflectionV1Alpha.proto b/dubbo-rpc/dubbo-rpc-triple/src/main/proto/reflectionV1Alpha.proto
new file mode 100644
index 0000000000..8c5e06fe14
--- /dev/null
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/proto/reflectionV1Alpha.proto
@@ -0,0 +1,144 @@
+// Copyright 2016 The gRPC Authors
+//
+// 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.
+// Service exported by server reflection
+
+
+// Warning: this entire file is deprecated. Use this instead:
+// https://github.com/grpc/grpc-proto/blob/master/grpc/reflection/v1/reflection.proto
+
+syntax = "proto3";
+
+package grpc.reflection.v1alpha;
+
+option deprecated = true;
+option java_multiple_files = true;
+option java_package = "io.grpc.reflection.v1alpha";
+option java_outer_classname = "ServerReflectionProto";
+
+service ServerReflection {
+ // The reflection service is structured as a bidirectional stream, ensuring
+ // all related requests go to a single server.
+ rpc ServerReflectionInfo(stream ServerReflectionRequest)
+ returns (stream ServerReflectionResponse);
+}
+
+// The message sent by the client when calling ServerReflectionInfo method.
+message ServerReflectionRequest {
+ string host = 1;
+ // To use reflection service, the client should set one of the following
+ // fields in message_request. The server distinguishes requests by their
+ // defined field and then handles them using corresponding methods.
+ oneof message_request {
+ // Find a proto file by the file name.
+ string file_by_filename = 3;
+
+ // Find the proto file that declares the given fully-qualified symbol name.
+ // This field should be a fully-qualified symbol name
+ // (e.g. <package>.<service>[.<method>] or <package>.<type>).
+ string file_containing_symbol = 4;
+
+ // Find the proto file which defines an extension extending the given
+ // message type with the given field number.
+ ExtensionRequest file_containing_extension = 5;
+
+ // Finds the tag numbers used by all known extensions of extendee_type, and
+ // appends them to ExtensionNumberResponse in an undefined order.
+ // Its corresponding method is best-effort: it's not guaranteed that the
+ // reflection service will implement this method, and it's not guaranteed
+ // that this method will provide all extensions. Returns
+ // StatusCode::UNIMPLEMENTED if it's not implemented.
+ // This field should be a fully-qualified type name. The format is
+ // <package>.<type>
+ string all_extension_numbers_of_type = 6;
+
+ // List the full names of registered services. The content will not be
+ // checked.
+ string list_services = 7;
+ }
+}
+
+// The type name and extension number sent by the client when requesting
+// file_containing_extension.
+message ExtensionRequest {
+ // Fully-qualified type name. The format should be <package>.<type>
+ string containing_type = 1;
+ int32 extension_number = 2;
+}
+
+// The message sent by the server to answer ServerReflectionInfo method.
+message ServerReflectionResponse {
+ string valid_host = 1;
+ ServerReflectionRequest original_request = 2;
+ // The server set one of the following fields accroding to the message_request
+ // in the request.
+ oneof message_response {
+ // This message is used to answer file_by_filename, file_containing_symbol,
+ // file_containing_extension requests with transitive dependencies. As
+ // the repeated label is not allowed in oneof fields, we use a
+ // FileDescriptorResponse message to encapsulate the repeated fields.
+ // The reflection service is allowed to avoid sending FileDescriptorProtos
+ // that were previously sent in response to earlier requests in the stream.
+ FileDescriptorResponse file_descriptor_response = 4;
+
+ // This message is used to answer all_extension_numbers_of_type requst.
+ ExtensionNumberResponse all_extension_numbers_response = 5;
+
+ // This message is used to answer list_services request.
+ ListServiceResponse list_services_response = 6;
+
+ // This message is used when an error occurs.
+ ErrorResponse error_response = 7;
+ }
+}
+
+// Serialized FileDescriptorProto messages sent by the server answering
+// a file_by_filename, file_containing_symbol, or file_containing_extension
+// request.
+message FileDescriptorResponse {
+ // Serialized FileDescriptorProto messages. We avoid taking a dependency on
+ // descriptor.proto, which uses proto2 only features, by making them opaque
+ // bytes instead.
+ repeated bytes file_descriptor_proto = 1;
+}
+
+// A list of extension numbers sent by the server answering
+// all_extension_numbers_of_type request.
+message ExtensionNumberResponse {
+ // Full name of the base type, including the package name. The format
+ // is <package>.<type>
+ string base_type_name = 1;
+ repeated int32 extension_number = 2;
+}
+
+// A list of ServiceResponse sent by the server answering list_services request.
+message ListServiceResponse {
+ // The information of each service may be expanded in the future, so we use
+ // ServiceResponse message to encapsulate it.
+ repeated ServiceResponse service = 1;
+}
+
+// The information of a single service used by ListServiceResponse to answer
+// list_services request.
+message ServiceResponse {
+ // Full name of a registered service, including its package name. The format
+ // is <package>.<service>
+ string name = 1;
+}
+
+// The error code and error message sent by the server when an error occurs.
+message ErrorResponse {
+ // This field uses the error codes defined in grpc::StatusCode.
+ int32 error_code = 1;
+ string error_message = 2;
+}
diff --git a/pom.xml b/pom.xml
index afb9b5c1c5..54b105f0d9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -331,6 +331,7 @@
**/com/google/rpc/*,
**/generated/**/*,
**/grpc/health/**/*,
+ **/grpc/reflection/**/*,
**/target/**/*,
**/*.json
</excludes>