• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2014 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.base.MoreObjects;
22 import com.google.common.base.Preconditions;
23 import java.io.InputStream;
24 import java.util.concurrent.atomic.AtomicReferenceArray;
25 import javax.annotation.CheckReturnValue;
26 import javax.annotation.Nullable;
27 import javax.annotation.concurrent.Immutable;
28 
29 /**
30  * Description of a remote method used by {@link Channel} to initiate a call.
31  *
32  * <p>Provides the name of the operation to execute as well as {@link Marshaller} instances
33  * used to parse and serialize request and response messages.
34  *
35  * <p>Can be constructed manually but will often be generated by stub code generators.
36  *
37  * @since 1.0.0
38  */
39 @Immutable
40 public final class MethodDescriptor<ReqT, RespT> {
41 
42   private final MethodType type;
43   private final String fullMethodName;
44   @Nullable private final String serviceName;
45   private final Marshaller<ReqT> requestMarshaller;
46   private final Marshaller<RespT> responseMarshaller;
47   private final @Nullable Object schemaDescriptor;
48   private final boolean idempotent;
49   private final boolean safe;
50   private final boolean sampledToLocalTracing;
51 
52   // Must be set to InternalKnownTransport.values().length
53   // Not referenced to break the dependency.
54   private final AtomicReferenceArray<Object> rawMethodNames = new AtomicReferenceArray<>(2);
55 
56   /**
57    * Gets the cached "raw" method name for this Method Descriptor.  The raw name is transport
58    * specific, and should be set using {@link #setRawMethodName} by the transport.
59    *
60    * @param transportOrdinal the unique ID of the transport, given by
61    *        {@link InternalKnownTransport#ordinal}.
62    * @return a transport specific representation of the method name.
63    */
getRawMethodName(int transportOrdinal)64   final Object getRawMethodName(int transportOrdinal) {
65     return rawMethodNames.get(transportOrdinal);
66   }
67 
68   /**
69    * Safely, but weakly, sets the raw method name for this Method Descriptor.  This should only be
70    * called by the transport.  See {@link #getRawMethodName} for more detail.
71    */
setRawMethodName(int transportOrdinal, Object o)72   final void setRawMethodName(int transportOrdinal, Object o) {
73     rawMethodNames.lazySet(transportOrdinal, o);
74   }
75 
76   /**
77    * The call type of a method.
78    *
79    * @since 1.0.0
80    */
81   public enum MethodType {
82     /**
83      * One request message followed by one response message.
84      */
85     UNARY,
86 
87     /**
88      * Zero or more request messages with one response message.
89      */
90     CLIENT_STREAMING,
91 
92     /**
93      * One request message followed by zero or more response messages.
94      */
95     SERVER_STREAMING,
96 
97     /**
98      * Zero or more request and response messages arbitrarily interleaved in time.
99      */
100     BIDI_STREAMING,
101 
102     /**
103      * Cardinality and temporal relationships are not known. Implementations should not make
104      * buffering assumptions and should largely treat the same as {@link #BIDI_STREAMING}.
105      */
106     UNKNOWN;
107 
108     /**
109      * Returns {@code true} for {@code UNARY} and {@code SERVER_STREAMING}, which do not permit the
110      * client to stream.
111      *
112      * @since 1.0.0
113      */
clientSendsOneMessage()114     public final boolean clientSendsOneMessage() {
115       return this == UNARY || this == SERVER_STREAMING;
116     }
117 
118     /**
119      * Returns {@code true} for {@code UNARY} and {@code CLIENT_STREAMING}, which do not permit the
120      * server to stream.
121      *
122      * @since 1.0.0
123      */
serverSendsOneMessage()124     public final boolean serverSendsOneMessage() {
125       return this == UNARY || this == CLIENT_STREAMING;
126     }
127   }
128 
129   /**
130    * A typed abstraction over message serialization and deserialization, a.k.a. marshalling and
131    * unmarshalling.
132    *
133    * <p>Stub implementations will define implementations of this interface for each of the request
134    * and response messages provided by a service.
135    *
136    * @param <T> type of serializable message
137    * @since 1.0.0
138    */
139   public interface Marshaller<T> {
140     /**
141      * Given a message, produce an {@link InputStream} for it so that it can be written to the wire.
142      * Where possible implementations should produce streams that are {@link io.grpc.KnownLength}
143      * to improve transport efficiency.
144      *
145      * @param value to serialize.
146      * @return serialized value as stream of bytes.
147      */
stream(T value)148     public InputStream stream(T value);
149 
150     /**
151      * Given an {@link InputStream} parse it into an instance of the declared type so that it can be
152      * passed to application code.
153      *
154      * @param stream of bytes for serialized value
155      * @return parsed value
156      */
parse(InputStream stream)157     public T parse(InputStream stream);
158   }
159 
160   /**
161    * A marshaller that supports retrieving it's type parameter {@code T} at runtime.
162    *
163    * @since 1.1.0
164    */
165   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2222")
166   public interface ReflectableMarshaller<T> extends Marshaller<T> {
167     /**
168      * Returns the {@code Class} that this marshaller serializes and deserializes. If inheritance is
169      * allowed, this is the base class or interface for all supported classes.
170      *
171      * @return non-{@code null} base class for all objects produced and consumed by this marshaller
172      */
getMessageClass()173     public Class<T> getMessageClass();
174   }
175 
176   /**
177    * A marshaller that uses a fixed instance of the type it produces.
178    *
179    * @since 1.1.0
180    */
181   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2222")
182   public interface PrototypeMarshaller<T> extends ReflectableMarshaller<T> {
183     /**
184      * An instance of the expected message type, typically used as a schema and helper for producing
185      * other message instances. The {@code null} value may be a special value for the marshaller
186      * (like the equivalent of {@link Void}), so it is a valid return value. {@code null} does
187      * <em>not</em> mean "unsupported" or "unknown".
188      *
189      * <p>It is generally expected this would return the same instance each invocation, but it is
190      * not a requirement.
191      */
192     @Nullable
getMessagePrototype()193     public T getMessagePrototype();
194   }
195 
196   /**
197    * Creates a new {@code MethodDescriptor}.
198    *
199    * @param type the call type of this method
200    * @param fullMethodName the fully qualified name of this method
201    * @param requestMarshaller the marshaller used to encode and decode requests
202    * @param responseMarshaller the marshaller used to encode and decode responses
203    * @since 1.0.0
204    * @deprecated use {@link #newBuilder()}.
205    */
206   @Deprecated
create( MethodType type, String fullMethodName, Marshaller<RequestT> requestMarshaller, Marshaller<ResponseT> responseMarshaller)207   public static <RequestT, ResponseT> MethodDescriptor<RequestT, ResponseT> create(
208       MethodType type, String fullMethodName,
209       Marshaller<RequestT> requestMarshaller,
210       Marshaller<ResponseT> responseMarshaller) {
211     return new MethodDescriptor<>(
212         type, fullMethodName, requestMarshaller, responseMarshaller, null, false, false, false);
213   }
214 
MethodDescriptor( MethodType type, String fullMethodName, Marshaller<ReqT> requestMarshaller, Marshaller<RespT> responseMarshaller, Object schemaDescriptor, boolean idempotent, boolean safe, boolean sampledToLocalTracing)215   private MethodDescriptor(
216       MethodType type,
217       String fullMethodName,
218       Marshaller<ReqT> requestMarshaller,
219       Marshaller<RespT> responseMarshaller,
220       Object schemaDescriptor,
221       boolean idempotent,
222       boolean safe,
223       boolean sampledToLocalTracing) {
224     assert !safe || idempotent : "safe should imply idempotent";
225     this.type = Preconditions.checkNotNull(type, "type");
226     this.fullMethodName = Preconditions.checkNotNull(fullMethodName, "fullMethodName");
227     this.serviceName = extractFullServiceName(fullMethodName);
228     this.requestMarshaller = Preconditions.checkNotNull(requestMarshaller, "requestMarshaller");
229     this.responseMarshaller = Preconditions.checkNotNull(responseMarshaller, "responseMarshaller");
230     this.schemaDescriptor = schemaDescriptor;
231     this.idempotent = idempotent;
232     this.safe = safe;
233     this.sampledToLocalTracing = sampledToLocalTracing;
234   }
235 
236   /**
237    * The call type of the method.
238    *
239    * @since 1.0.0
240    */
getType()241   public MethodType getType() {
242     return type;
243   }
244 
245   /**
246    * The fully qualified name of the method.
247    *
248    * @since 1.0.0
249    */
getFullMethodName()250   public String getFullMethodName() {
251     return fullMethodName;
252   }
253 
254   /**
255    * A convenience method for {@code extractFullServiceName(getFullMethodName())}.
256    *
257    * @since 1.21.0
258    */
259   @Nullable
260   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/5635")
getServiceName()261   public String getServiceName() {
262     return serviceName;
263   }
264 
265   /**
266    * A convenience method for {@code extractBareMethodName(getFullMethodName())}.
267    *
268    * @since 1.33.0
269    */
270   @Nullable
271   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/5635")
getBareMethodName()272   public String getBareMethodName() {
273     return extractBareMethodName(fullMethodName);
274   }
275 
276   /**
277    * Parse a response payload from the given {@link InputStream}.
278    *
279    * @param input stream containing response message to parse.
280    * @return parsed response message object.
281    * @since 1.0.0
282    */
parseResponse(InputStream input)283   public RespT parseResponse(InputStream input) {
284     return responseMarshaller.parse(input);
285   }
286 
287   /**
288    * Convert a request message to an {@link InputStream}.
289    * The returned InputStream should be closed by the caller.
290    *
291    * @param requestMessage to serialize using the request {@link Marshaller}.
292    * @return serialized request message.
293    * @since 1.0.0
294    */
streamRequest(ReqT requestMessage)295   public InputStream streamRequest(ReqT requestMessage) {
296     return requestMarshaller.stream(requestMessage);
297   }
298 
299   /**
300    * Parse an incoming request message.
301    *
302    * @param input the serialized message as a byte stream.
303    * @return a parsed instance of the message.
304    * @since 1.0.0
305    */
parseRequest(InputStream input)306   public ReqT parseRequest(InputStream input) {
307     return requestMarshaller.parse(input);
308   }
309 
310   /**
311    * Serialize an outgoing response message.
312    * The returned InputStream should be closed by the caller.
313    *
314    * @param response the response message to serialize.
315    * @return the serialized message as a byte stream.
316    * @since 1.0.0
317    */
streamResponse(RespT response)318   public InputStream streamResponse(RespT response) {
319     return responseMarshaller.stream(response);
320   }
321 
322   /**
323    * Returns the marshaller for the request type. Allows introspection of the request marshaller.
324    *
325    * @since 1.1.0
326    */
327   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2592")
getRequestMarshaller()328   public Marshaller<ReqT> getRequestMarshaller() {
329     return requestMarshaller;
330   }
331 
332   /**
333    * Returns the marshaller for the response type. Allows introspection of the response marshaller.
334    *
335    * @since 1.1.0
336    */
337   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2592")
getResponseMarshaller()338   public Marshaller<RespT> getResponseMarshaller() {
339     return responseMarshaller;
340   }
341 
342   /**
343    * Returns the schema descriptor for this method. A schema descriptor is an object that is not
344    * used by gRPC core but includes information related to the service method. The type of the
345    * object is specific to the consumer, so both the code setting the schema descriptor and the code
346    * calling {@link #getSchemaDescriptor()} must coordinate.  For example, protobuf generated code
347    * sets this value, in order to be consumed by the server reflection service.  See also:
348    * {@code io.grpc.protobuf.ProtoMethodDescriptorSupplier}.
349    *
350    * @since 1.7.0
351    */
getSchemaDescriptor()352   public @Nullable Object getSchemaDescriptor() {
353     return schemaDescriptor;
354   }
355 
356   /**
357    * Returns whether this method is idempotent.
358    *
359    * @since 1.0.0
360    */
isIdempotent()361   public boolean isIdempotent() {
362     return idempotent;
363   }
364 
365   /**
366    * Returns whether this method is safe.
367    *
368    * <p>A safe request does nothing except retrieval so it has no side effects on the server side.
369    *
370    * @since 1.1.0
371    */
isSafe()372   public boolean isSafe() {
373     return safe;
374   }
375 
376   /**
377    * Returns whether RPCs for this method may be sampled into the local tracing store.
378    */
isSampledToLocalTracing()379   public boolean isSampledToLocalTracing() {
380     return sampledToLocalTracing;
381   }
382 
383   /**
384    * Generate the fully qualified method name.  This matches the name
385    *
386    * @param fullServiceName the fully qualified service name that is prefixed with the package name
387    * @param methodName the short method name
388    * @since 1.0.0
389    */
generateFullMethodName(String fullServiceName, String methodName)390   public static String generateFullMethodName(String fullServiceName, String methodName) {
391     return checkNotNull(fullServiceName, "fullServiceName")
392         + "/"
393         + checkNotNull(methodName, "methodName");
394   }
395 
396   /**
397    * Extract the fully qualified service name out of a fully qualified method name. May return
398    * {@code null} if the input is malformed, but you cannot rely on it for the validity of the
399    * input.
400    *
401    * @since 1.0.0
402    */
403   @Nullable
extractFullServiceName(String fullMethodName)404   public static String extractFullServiceName(String fullMethodName) {
405     int index = checkNotNull(fullMethodName, "fullMethodName").lastIndexOf('/');
406     if (index == -1) {
407       return null;
408     }
409     return fullMethodName.substring(0, index);
410   }
411 
412   /**
413    * Extract the method name out of a fully qualified method name. May return {@code null}
414    * if the input is malformed, but you cannot rely on it for the validity of the input.
415    *
416    * @since 1.33.0
417    */
418   @Nullable
419   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/5635")
extractBareMethodName(String fullMethodName)420   public static String extractBareMethodName(String fullMethodName) {
421     int index = checkNotNull(fullMethodName, "fullMethodName").lastIndexOf('/');
422     if (index == -1) {
423       return null;
424     }
425     return fullMethodName.substring(index + 1);
426   }
427 
428   /**
429    * Creates a new builder for a {@link MethodDescriptor}.
430    *
431    * @since 1.1.0
432    */
433   @CheckReturnValue
newBuilder()434   public static <ReqT, RespT> Builder<ReqT, RespT> newBuilder() {
435     return newBuilder(null, null);
436   }
437 
438   /**
439    * Creates a new builder for a {@link MethodDescriptor}.
440    *
441    * @since 1.1.0
442    */
443   @CheckReturnValue
newBuilder( Marshaller<ReqT> requestMarshaller, Marshaller<RespT> responseMarshaller)444   public static <ReqT, RespT> Builder<ReqT, RespT> newBuilder(
445       Marshaller<ReqT> requestMarshaller, Marshaller<RespT> responseMarshaller) {
446     return new Builder<ReqT, RespT>()
447         .setRequestMarshaller(requestMarshaller)
448         .setResponseMarshaller(responseMarshaller);
449   }
450 
451   /**
452    * Turns this descriptor into a builder.
453    *
454    * @since 1.1.0
455    */
456   @CheckReturnValue
toBuilder()457   public Builder<ReqT, RespT> toBuilder() {
458     return toBuilder(requestMarshaller, responseMarshaller);
459   }
460 
461   /**
462    * Turns this descriptor into a builder, replacing the request and response marshallers.
463    *
464    * @since 1.1.0
465    */
466   @CheckReturnValue
toBuilder( Marshaller<NewReqT> requestMarshaller, Marshaller<NewRespT> responseMarshaller)467   public <NewReqT, NewRespT> Builder<NewReqT, NewRespT> toBuilder(
468       Marshaller<NewReqT> requestMarshaller, Marshaller<NewRespT> responseMarshaller) {
469     return MethodDescriptor.<NewReqT, NewRespT>newBuilder()
470         .setRequestMarshaller(requestMarshaller)
471         .setResponseMarshaller(responseMarshaller)
472         .setType(type)
473         .setFullMethodName(fullMethodName)
474         .setIdempotent(idempotent)
475         .setSafe(safe)
476         .setSampledToLocalTracing(sampledToLocalTracing)
477         .setSchemaDescriptor(schemaDescriptor);
478   }
479 
480   /**
481    * A builder for a {@link MethodDescriptor}.
482    *
483    * @since 1.1.0
484    */
485   public static final class Builder<ReqT, RespT> {
486 
487     private Marshaller<ReqT> requestMarshaller;
488     private Marshaller<RespT> responseMarshaller;
489     private MethodType type;
490     private String fullMethodName;
491     private boolean idempotent;
492     private boolean safe;
493     private Object schemaDescriptor;
494     private boolean sampledToLocalTracing;
495 
Builder()496     private Builder() {}
497 
498     /**
499      * Sets the request marshaller.
500      *
501      * @param requestMarshaller the marshaller to use.
502      * @since 1.1.0
503      */
setRequestMarshaller(Marshaller<ReqT> requestMarshaller)504     public Builder<ReqT, RespT> setRequestMarshaller(Marshaller<ReqT> requestMarshaller) {
505       this.requestMarshaller = requestMarshaller;
506       return this;
507     }
508 
509     /**
510      * Sets the response marshaller.
511      *
512      * @param responseMarshaller the marshaller to use.
513      * @since 1.1.0
514      */
setResponseMarshaller(Marshaller<RespT> responseMarshaller)515     public Builder<ReqT, RespT> setResponseMarshaller(Marshaller<RespT> responseMarshaller) {
516       this.responseMarshaller = responseMarshaller;
517       return this;
518     }
519 
520     /**
521      * Sets the method type.
522      *
523      * @param type the type of the method.
524      * @since 1.1.0
525      */
setType(MethodType type)526     public Builder<ReqT, RespT> setType(MethodType type) {
527       this.type = type;
528       return this;
529     }
530 
531     /**
532      * Sets the fully qualified (service and method) method name.
533      *
534      * @see MethodDescriptor#generateFullMethodName
535      * @since 1.1.0
536      */
setFullMethodName(String fullMethodName)537     public Builder<ReqT, RespT> setFullMethodName(String fullMethodName) {
538       this.fullMethodName = fullMethodName;
539       return this;
540     }
541 
542     /**
543      * Sets the schema descriptor for this builder.  A schema descriptor is an object that is not
544      * used by gRPC core but includes information related to the methods. The type of the object
545      * is specific to the consumer, so both the code calling this and the code calling
546      * {@link MethodDescriptor#getSchemaDescriptor()} must coordinate.
547      *
548      * @param schemaDescriptor an object that describes the service structure.  Should be immutable.
549      * @since 1.7.0
550      */
setSchemaDescriptor(@ullable Object schemaDescriptor)551     public Builder<ReqT, RespT> setSchemaDescriptor(@Nullable Object schemaDescriptor) {
552       this.schemaDescriptor = schemaDescriptor;
553       return this;
554     }
555 
556     /**
557      * Sets whether the method is idempotent.  If true, calling this method more than once doesn't
558      * have additional side effects. If {@code false}, method is also not safe. Note that implies
559      * calling {@code builder.setIdempotent(false).setIdempotent(true)} will leave {@code
560      * isSafe() == false}.
561      *
562      * @since 1.1.0
563      */
setIdempotent(boolean idempotent)564     public Builder<ReqT, RespT> setIdempotent(boolean idempotent) {
565       this.idempotent = idempotent;
566       if (!idempotent) {
567         this.safe = false;
568       }
569       return this;
570     }
571 
572     /**
573      * Sets whether this method is safe.  If true, calling this method any number of times doesn't
574      * have side effects. If {@code true}, method is also idempotent. Note that implies calling
575      * {@code builder.setSafe(true).setSafe(false)} will leave {@code isIdempotent() == true}.
576      *
577      * @since 1.1.0
578      */
setSafe(boolean safe)579     public Builder<ReqT, RespT> setSafe(boolean safe) {
580       this.safe = safe;
581       if (safe) {
582         this.idempotent = true;
583       }
584       return this;
585     }
586 
587     /**
588      * Sets whether RPCs for this method may be sampled into the local tracing store.  If true,
589      * sampled traces of this method may be kept in memory by tracing libraries.
590      *
591      * @since 1.8.0
592      */
setSampledToLocalTracing(boolean value)593     public Builder<ReqT, RespT> setSampledToLocalTracing(boolean value) {
594       this.sampledToLocalTracing = value;
595       return this;
596     }
597 
598     /**
599      * Builds the method descriptor.
600      *
601      * @since 1.1.0
602      */
603     @CheckReturnValue
build()604     public MethodDescriptor<ReqT, RespT> build() {
605       return new MethodDescriptor<>(
606           type,
607           fullMethodName,
608           requestMarshaller,
609           responseMarshaller,
610           schemaDescriptor,
611           idempotent,
612           safe,
613           sampledToLocalTracing);
614     }
615   }
616 
617   @Override
toString()618   public String toString() {
619     return MoreObjects.toStringHelper(this)
620       .add("fullMethodName", fullMethodName)
621       .add("type", type)
622       .add("idempotent", idempotent)
623       .add("safe", safe)
624       .add("sampledToLocalTracing", sampledToLocalTracing)
625       .add("requestMarshaller", requestMarshaller)
626       .add("responseMarshaller", responseMarshaller)
627       .add("schemaDescriptor", schemaDescriptor)
628       .omitNullValues()
629       .toString();
630   }
631 }
632