@Override
    public ServiceSpecification generateSpecification(Set<ServiceConfig> serviceConfigs,
                                                      DocServiceFilter filter,
                                                      DescriptiveTypeInfoProvider descriptiveTypeInfoProvider) {
        requireNonNull(serviceConfigs, "serviceConfigs");
        requireNonNull(filter, "filter");
        requireNonNull(descriptiveTypeInfoProvider, "descriptiveTypeInfoProvider");

        final Set<GrpcService> addedService = new HashSet<>();
        final ImmutableList.Builder<HttpEndpoint> httpEndpoints = ImmutableList.builder();
        final ServiceInfosBuilder serviceInfosBuilder = new ServiceInfosBuilder();
        for (ServiceConfig serviceConfig : serviceConfigs) {
            final GrpcService grpcService = serviceConfig.service().as(GrpcService.class);
            assert grpcService != null;

            if (addedService.add(grpcService)) {
                addServiceDescriptor(serviceInfosBuilder, grpcService);
            }

            final HttpEndpointSupport httpEndpointSupport = grpcService.as(HttpEndpointSupport.class);
            if (httpEndpointSupport != null) {
                // grpcService can be unwrapped into HttpJsonTranscodingService.
                // There are two routes for a method in HttpJsonTranscodingService:
                // - The HTTP route is added below using the spec.
                // - The auto generated route(e.g. /package.name/MethodName) is added using EndpointInfo.
                final HttpEndpointSpecification spec =
                        httpEndpointSupport.httpEndpointSpecification(
                                serviceConfig.mappedRoute()); // Use mappedRoute to find the specification.
                if (spec != null) {
                    if (filter.test(NAME, spec.serviceName(), spec.methodName())) {
                        httpEndpoints.add(new HttpEndpoint(serviceConfig,
                                                           // Use route which has the full path.
                                                           spec.withRoute(serviceConfig.route())));
                    }
                    continue;
                } else {
                    // The current route is one of the routes from FramedGrpcService.routes()
                    // so we add it below.
                }
            }

            final Route route = serviceConfig.route();
            final Set<MediaType> supportedMediaTypes = supportedMediaTypes(grpcService);
            if (route.pathType() == RoutePathType.PREFIX) {
                // The route is PREFIX type when the grpcService is set via route builder:
                // - serverBuilder.route().pathPrefix("/prefix")...build(grpcService);
                // So we add endpoints of all methods in the grpcService with the pathPrefix.
                final String pathPrefix = route.paths().get(0);
                grpcService.methodsByRoute().forEach((methodRoute, method) -> {
                    final EndpointInfo endpointInfo =
                            EndpointInfo.builder(serviceConfig.virtualHost().hostnamePattern(),
                                                 concatPaths(pathPrefix, methodRoute.patternString()))
                                        .availableMimeTypes(supportedMediaTypes)
                                        .build();

                    serviceInfosBuilder.addEndpoint(method.getMethodDescriptor(), endpointInfo);
                });
            } else if (route.pathType() == RoutePathType.EXACT) {
                // The route is EXACT type when the grpcService is set via:
                // - serverBuilder.service(grpcService);
                // - serverBuilder.serviceUnder("/prefix", grpcService);
                final ServerMethodDefinition<?, ?> methodDefinition =
                        // Use mappedRoute to find the methodDefinition.
                        grpcService.methodsByRoute().get(serviceConfig.mappedRoute());
                assert methodDefinition != null;
                final EndpointInfo endpointInfo =
                        EndpointInfo.builder(serviceConfig.virtualHost().hostnamePattern(),
                                             route.patternString())
                                    .availableMimeTypes(supportedMediaTypes)
                                    .build();
                serviceInfosBuilder.addEndpoint(methodDefinition.getMethodDescriptor(), endpointInfo);
            } else {
                // Should never reach here.
                throw new Error();
            }
        }

        return generate(ImmutableList.<ServiceInfo>builder()
                                     .addAll(serviceInfosBuilder.build(filter))
                                     .addAll(buildHttpServiceInfos(httpEndpoints.build()))
                                     .build(), descriptiveTypeInfoProvider);
    }
{@link DocServicePlugin} implementation that supports {@link GrpcService}s.
grpc/src/main/java/com/linecorp/armeria/internal/server/grpc/GrpcDocServicePlugin.java