-
Notifications
You must be signed in to change notification settings - Fork 839
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start grpc-spring-boot-starter-web project
- Loading branch information
Showing
12 changed files
with
1,213 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
apply from: '../deploy.gradle' | ||
|
||
group = "net.devh" | ||
version = "${projectVersion}" | ||
|
||
compileJava.dependsOn(processResources) | ||
|
||
dependencies { | ||
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor") | ||
|
||
compile("org.springframework.boot:spring-boot-starter") | ||
compile("org.springframework.boot:spring-boot-starter-actuator") | ||
compile("org.springframework.boot:spring-boot-starter-web") | ||
compile("io.grpc:grpc-core:${grpcVersion}") | ||
compile("io.grpc:grpc-protobuf:${grpcVersion}") | ||
} |
58 changes: 58 additions & 0 deletions
58
...eb/src/main/java/net/devh/boot/grpc/web/autoconfigure/GrpcServerWebAutoConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright (c) 2016-2020 Michael Zhang <yidongnan@gmail.com> | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | ||
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the | ||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to | ||
* permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the | ||
* Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
|
||
package net.devh.boot.grpc.web.autoconfigure; | ||
|
||
import java.util.Collection; | ||
|
||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import io.grpc.BindableService; | ||
import net.devh.boot.grpc.web.bridge.GrpcWebController; | ||
import net.devh.boot.grpc.web.conversion.PojoFormat; | ||
|
||
/** | ||
* The auto configuration used by Spring-Boot that configures the web to grpc bridge components. | ||
* | ||
* @author Daniel Theuke (daniel.theuke@heuboe.de) | ||
*/ | ||
@Configuration | ||
@ConditionalOnClass(RestController.class) | ||
public class GrpcServerWebAutoConfiguration { | ||
|
||
@ConditionalOnMissingBean | ||
@Bean | ||
public PojoFormat defaultGrpcWebPojoFormat() { | ||
return new PojoFormat(false, false); | ||
} | ||
|
||
@ConditionalOnMissingBean | ||
@Bean | ||
public GrpcWebController defaultGrpcWebController(final PojoFormat format, | ||
final Collection<BindableService> services) { | ||
final GrpcWebController grpcController = new GrpcWebController(format); | ||
for (final BindableService service : services) { | ||
grpcController.register(service); | ||
} | ||
return grpcController; | ||
} | ||
|
||
} |
5 changes: 5 additions & 0 deletions
5
...ing-boot-starter-web/src/main/java/net/devh/boot/grpc/web/autoconfigure/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/** | ||
* The Spring-Boot auto configuration classes that setup the web server bridge to the grpc-service implementation. | ||
*/ | ||
|
||
package net.devh.boot.grpc.web.autoconfigure; |
109 changes: 109 additions & 0 deletions
109
...spring-boot-starter-web/src/main/java/net/devh/boot/grpc/web/bridge/GrpcMethodResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* | ||
* Copyright (c) 2016-2020 Michael Zhang <yidongnan@gmail.com> | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | ||
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the | ||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to | ||
* permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the | ||
* Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
|
||
package net.devh.boot.grpc.web.bridge; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import java.util.List; | ||
|
||
import io.grpc.Metadata; | ||
import io.grpc.Status; | ||
import io.grpc.StatusRuntimeException; | ||
|
||
public final class GrpcMethodResult<T> { | ||
|
||
public static <T> GrpcMethodResult<T> from(final StatusRuntimeException e) { | ||
return new GrpcMethodResult<>(e.getStatus(), e.getTrailers()); | ||
} | ||
|
||
private final Status status; | ||
private final Metadata headers; | ||
private final List<T> messages; | ||
|
||
/** | ||
* Creates a new grpc method result for a failed call. | ||
* | ||
* @param status The result status of the call. | ||
*/ | ||
public GrpcMethodResult(final Status status) { | ||
this(status, null); | ||
} | ||
|
||
/** | ||
* Creates a new grpc method result for a failed call. | ||
* | ||
* @param status The result status of the call. | ||
* @param headers The headers of the call. Null will be replaced by a new empty instance. | ||
*/ | ||
public GrpcMethodResult(final Status status, final Metadata headers) { | ||
this(status, headers, null); | ||
} | ||
|
||
/** | ||
* Creates a new grpc method result with the given messages. | ||
* | ||
* @param status The result status of the call. | ||
* @param headers The headers of the call. Null will be replaced by a new empty instance. | ||
* @param messages The result messages of call or null if there are no results. | ||
*/ | ||
public GrpcMethodResult(final Status status, final Metadata headers, final List<T> messages) { | ||
this.status = requireNonNull(status, "status"); | ||
this.headers = headers == null ? new Metadata() : headers; | ||
this.messages = messages; | ||
} | ||
|
||
/** | ||
* Gets the result status of the grpc call. | ||
* | ||
* @return The result status of the call. | ||
*/ | ||
public Status getStatus() { | ||
return this.status; | ||
} | ||
|
||
/** | ||
* Gets whether the grpc method call was successful. | ||
* | ||
* @return True, if the call was successful (code = OK). False otherwise. | ||
*/ | ||
public boolean wasSuccessful() { | ||
return this.status.getCode() == Status.Code.OK; | ||
} | ||
|
||
/** | ||
* Gets the response headers that were delivered during the request or its completion. | ||
* | ||
* @return The response headers of the call. | ||
*/ | ||
public Metadata getHeaders() { | ||
return this.headers; | ||
} | ||
|
||
/** | ||
* Gets the messages of the call in the same order they were delivered. | ||
* | ||
* @return The results of the call. | ||
*/ | ||
public List<T> getMessages() { | ||
if (!wasSuccessful()) { | ||
throw new IllegalStateException("Cannot access results of failed call"); | ||
} | ||
return this.messages; | ||
} | ||
|
||
} |
199 changes: 199 additions & 0 deletions
199
...pring-boot-starter-web/src/main/java/net/devh/boot/grpc/web/bridge/GrpcMethodWrapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
/* | ||
* Copyright (c) 2016-2020 Michael Zhang <yidongnan@gmail.com> | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | ||
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the | ||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to | ||
* permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the | ||
* Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
|
||
package net.devh.boot.grpc.web.bridge; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.function.Supplier; | ||
|
||
import com.google.protobuf.Descriptors.Descriptor; | ||
import com.google.protobuf.Message; | ||
|
||
import io.grpc.Metadata; | ||
import io.grpc.MethodDescriptor; | ||
import io.grpc.MethodDescriptor.PrototypeMarshaller; | ||
import io.grpc.ServerCall; | ||
import io.grpc.ServerCall.Listener; | ||
import io.grpc.ServerCallHandler; | ||
import io.grpc.ServerInterceptor; | ||
import io.grpc.ServerMethodDefinition; | ||
import io.grpc.Status; | ||
|
||
public class GrpcMethodWrapper<RequestT extends Message, ResponseT extends Message> | ||
implements ServerCallHandler<RequestT, ResponseT> { | ||
|
||
@SuppressWarnings({"rawtypes", "unchecked"}) | ||
public static GrpcMethodWrapper<?, ?> ofRaw(final ServerMethodDefinition method) { | ||
return of(method); | ||
} | ||
|
||
public static <RequestT extends Message, ResponseT extends Message> GrpcMethodWrapper<RequestT, ResponseT> of( | ||
final ServerMethodDefinition<RequestT, ResponseT> method) { | ||
final MethodDescriptor<RequestT, ResponseT> methodDescriptor = method.getMethodDescriptor(); | ||
return new GrpcMethodWrapper<>( | ||
method.getMethodDescriptor(), | ||
getRequestBuilderFor(methodDescriptor), | ||
getRequestDescriptorFor(methodDescriptor), | ||
method.getServerCallHandler()); | ||
} | ||
|
||
protected static <RequestT extends Message> Supplier<Message.Builder> getRequestBuilderFor( | ||
final MethodDescriptor<RequestT, ?> method) { | ||
final RequestT requestPrototype = getRequestPrototypeFor(method); | ||
return requestPrototype::newBuilderForType; | ||
} | ||
|
||
protected static <RequestT extends Message> Descriptor getRequestDescriptorFor( | ||
final MethodDescriptor<RequestT, ?> method) { | ||
final RequestT requestPrototype = getRequestPrototypeFor(method); | ||
return requestPrototype.getDescriptorForType(); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
protected static <RequestT extends Message> RequestT getRequestPrototypeFor( | ||
final MethodDescriptor<RequestT, ?> method) { | ||
final PrototypeMarshaller<?> requestMarshaller = (PrototypeMarshaller<RequestT>) method.getRequestMarshaller(); | ||
return (RequestT) requestMarshaller.getMessagePrototype(); | ||
} | ||
|
||
private final MethodDescriptor<RequestT, ResponseT> methodDescriptor; | ||
private final Supplier<? extends Message.Builder> requestBuilderSupplier; | ||
private final Descriptor requestDescriptor; | ||
private final ServerCallHandler<RequestT, ResponseT> delegate; | ||
|
||
public GrpcMethodWrapper(final MethodDescriptor<RequestT, ResponseT> methodDescriptor, | ||
final Supplier<? extends Message.Builder> requestBuilderSupplier, | ||
final Descriptor requestDescriptor, | ||
final ServerCallHandler<RequestT, ResponseT> delegate) { | ||
this.methodDescriptor = methodDescriptor; | ||
this.requestBuilderSupplier = requestBuilderSupplier; | ||
this.requestDescriptor = requestDescriptor; | ||
this.delegate = delegate; | ||
} | ||
|
||
public GrpcMethodWrapper<RequestT, ResponseT> intercept(final ServerInterceptor interceptor) { | ||
return new GrpcMethodWrapper<>(this.methodDescriptor, this.requestBuilderSupplier, this.requestDescriptor, | ||
InterceptCallHandler.create(interceptor, this.delegate)); | ||
} | ||
|
||
public WrappedServerCall<RequestT, ResponseT> prepare() { | ||
return new WrappedServerCall<>(this.methodDescriptor); | ||
} | ||
|
||
@Override | ||
public Listener<RequestT> startCall(final ServerCall<RequestT, ResponseT> call, final Metadata headers) { | ||
return this.delegate.startCall(call, headers); | ||
} | ||
|
||
public Supplier<? extends Message.Builder> getRequestBuilderSupplier() { | ||
return this.requestBuilderSupplier; | ||
} | ||
|
||
public Descriptor getRequestDescriptor() { | ||
return this.requestDescriptor; | ||
} | ||
|
||
public String getFullMethodName() { | ||
return this.methodDescriptor.getFullMethodName(); | ||
} | ||
|
||
static final class InterceptCallHandler<ReqT, RespT> implements ServerCallHandler<ReqT, RespT> { | ||
|
||
public static <ReqT, RespT> InterceptCallHandler<ReqT, RespT> create( | ||
final ServerInterceptor interceptor, final ServerCallHandler<ReqT, RespT> callHandler) { | ||
return new InterceptCallHandler<>(interceptor, callHandler); | ||
} | ||
|
||
private final ServerInterceptor interceptor; | ||
private final ServerCallHandler<ReqT, RespT> callHandler; | ||
|
||
private InterceptCallHandler(final ServerInterceptor interceptor, | ||
final ServerCallHandler<ReqT, RespT> callHandler) { | ||
this.interceptor = requireNonNull(interceptor, "interceptor"); | ||
this.callHandler = callHandler; | ||
} | ||
|
||
@Override | ||
public ServerCall.Listener<ReqT> startCall(final ServerCall<ReqT, RespT> call, final Metadata headers) { | ||
return this.interceptor.interceptCall(call, headers, this.callHandler); | ||
} | ||
|
||
} | ||
|
||
static final class WrappedServerCall<RequestT, ResponseT> extends ServerCall<RequestT, ResponseT> { | ||
|
||
private final MethodDescriptor<RequestT, ResponseT> methodDescriptor; | ||
|
||
private Status status; | ||
private Metadata headers; | ||
private final List<ResponseT> messages = new ArrayList<>(2); | ||
|
||
public WrappedServerCall(final MethodDescriptor<RequestT, ResponseT> methodDescriptor) { | ||
this.methodDescriptor = methodDescriptor; | ||
} | ||
|
||
@Override | ||
public void request(final int numMessages) { | ||
// Does nothing | ||
} | ||
|
||
@Override | ||
public void sendHeaders(final Metadata headers) { | ||
if (this.headers != null) { | ||
throw new IllegalStateException("Headers already send"); | ||
} | ||
this.headers = headers; | ||
} | ||
|
||
@Override | ||
public void sendMessage(final ResponseT message) { | ||
this.messages.add(message); | ||
} | ||
|
||
@Override | ||
public void close(final Status status, final Metadata trailers) { | ||
this.status = status; | ||
if (this.headers == null) { | ||
this.headers = trailers; | ||
} else { | ||
this.headers.merge(trailers); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean isCancelled() { | ||
return false; | ||
} | ||
|
||
@Override | ||
public MethodDescriptor<RequestT, ResponseT> getMethodDescriptor() { | ||
return this.methodDescriptor; | ||
} | ||
|
||
public GrpcMethodResult<ResponseT> getResult() { | ||
if (this.status == null) { | ||
throw new IllegalStateException("Call not yet closed!"); | ||
} | ||
return new GrpcMethodResult<>(this.status, this.headers, this.messages); | ||
} | ||
|
||
} | ||
|
||
} |
Oops, something went wrong.