1 /*
2  * Copyright 2016 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.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 
22 import com.google.common.base.MoreObjects;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 import javax.annotation.Nullable;
31 
32 /**
33  * Descriptor for a service.
34  *
35  * @since 1.0.0
36  */
37 public final class ServiceDescriptor {
38 
39   private final String name;
40   private final Collection<MethodDescriptor<?, ?>> methods;
41   private final Object schemaDescriptor;
42 
43   /**
44    * Constructs a new Service Descriptor.  Users are encouraged to use {@link #newBuilder}
45    * instead.
46    *
47    * @param name The name of the service
48    * @param methods The methods that are part of the service
49    * @since 1.0.0
50    */
ServiceDescriptor(String name, MethodDescriptor<?, ?>... methods)51   public ServiceDescriptor(String name, MethodDescriptor<?, ?>... methods) {
52     this(name, Arrays.asList(methods));
53   }
54 
55   /**
56    * Constructs a new Service Descriptor.  Users are encouraged to use {@link #newBuilder}
57    * instead.
58    *
59    * @param name The name of the service
60    * @param methods The methods that are part of the service
61    * @since 1.0.0
62    */
ServiceDescriptor(String name, Collection<MethodDescriptor<?, ?>> methods)63   public ServiceDescriptor(String name, Collection<MethodDescriptor<?, ?>> methods) {
64     this(newBuilder(name).addAllMethods(checkNotNull(methods, "methods")));
65   }
66 
ServiceDescriptor(Builder b)67   private ServiceDescriptor(Builder b) {
68     this.name = b.name;
69     validateMethodNames(name, b.methods);
70     this.methods = Collections.unmodifiableList(new ArrayList<MethodDescriptor<?, ?>>(b.methods));
71     this.schemaDescriptor = b.schemaDescriptor;
72   }
73 
74   /**
75    * Simple name of the service. It is not an absolute path.
76    *
77    * @since 1.0.0
78    */
getName()79   public String getName() {
80     return name;
81   }
82 
83   /**
84    * A collection of {@link MethodDescriptor} instances describing the methods exposed by the
85    * service.
86    *
87    * @since 1.0.0
88    */
getMethods()89   public Collection<MethodDescriptor<?, ?>> getMethods() {
90     return methods;
91   }
92 
93   /**
94    * Returns the schema descriptor for this service.  A schema descriptor is an object that is not
95    * used by gRPC core but includes information related to the service.  The type of the object
96    * is specific to the consumer, so both the code setting the schema descriptor and the code
97    * calling {@link #getSchemaDescriptor()} must coordinate.  For example, protobuf generated code
98    * sets this value, in order to be consumed by the server reflection service.  See also:
99    * {@code io.grpc.protobuf.ProtoFileDescriptorSupplier}.
100    *
101    * @since 1.1.0
102    */
103   @Nullable
104   @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2222")
getSchemaDescriptor()105   public Object getSchemaDescriptor() {
106     return schemaDescriptor;
107   }
108 
validateMethodNames(String serviceName, Collection<MethodDescriptor<?, ?>> methods)109   static void validateMethodNames(String serviceName, Collection<MethodDescriptor<?, ?>> methods) {
110     Set<String> allNames = new HashSet<String>(methods.size());
111     for (MethodDescriptor<?, ?> method : methods) {
112       checkNotNull(method, "method");
113       String methodServiceName =
114           MethodDescriptor.extractFullServiceName(method.getFullMethodName());
115       checkArgument(serviceName.equals(methodServiceName),
116           "service names %s != %s", methodServiceName, serviceName);
117       checkArgument(allNames.add(method.getFullMethodName()),
118           "duplicate name %s", method.getFullMethodName());
119     }
120   }
121 
122   /**
123    * Creates a new builder for a {@link ServiceDescriptor}.
124    *
125    * @since 1.1.0
126    */
newBuilder(String name)127   public static Builder newBuilder(String name) {
128     return new Builder(name);
129   }
130 
131   /**
132    * A builder for a {@link ServiceDescriptor}.
133    *
134    * @since 1.1.0
135    */
136   public static final class Builder {
Builder(String name)137     private Builder(String name) {
138       setName(name);
139     }
140 
141     private String name;
142     private List<MethodDescriptor<?, ?>> methods = new ArrayList<MethodDescriptor<?, ?>>();
143     private Object schemaDescriptor;
144 
145     /**
146      * Sets the name.  This should be non-{@code null}.
147      *
148      * @param name The name of the service.
149      * @return this builder.
150      * @since 1.1.0
151      */
152     @ExperimentalApi("https://github.com/grpc/grpc-java/issues/2666")
setName(String name)153     public Builder setName(String name) {
154       this.name = checkNotNull(name, "name");
155       return this;
156     }
157 
158     /**
159      * Adds a method to this service.  This should be non-{@code null}.
160      *
161      * @param method the method to add to the descriptor.
162      * @return this builder.
163      * @since 1.1.0
164      */
addMethod(MethodDescriptor<?, ?> method)165     public Builder addMethod(MethodDescriptor<?, ?> method) {
166       methods.add(checkNotNull(method, "method"));
167       return this;
168     }
169 
170     /**
171      * Currently not exposed.  Bulk adds methods to this builder.
172      *
173      * @param methods the methods to add.
174      * @return this builder.
175      */
addAllMethods(Collection<MethodDescriptor<?, ?>> methods)176     private Builder addAllMethods(Collection<MethodDescriptor<?, ?>> methods) {
177       this.methods.addAll(methods);
178       return this;
179     }
180 
181     /**
182      * Sets the schema descriptor for this builder.  A schema descriptor is an object that is not
183      * used by gRPC core but includes information related to the service.  The type of the object
184      * is specific to the consumer, so both the code calling this and the code calling
185      * {@link ServiceDescriptor#getSchemaDescriptor()} must coordinate.  For example, protobuf
186      * generated code sets this value, in order to be consumed by the server reflection service.
187      *
188      * @param schemaDescriptor an object that describes the service structure.  Should be immutable.
189      * @return this builder.
190      * @since 1.1.0
191      */
setSchemaDescriptor(@ullable Object schemaDescriptor)192     public Builder setSchemaDescriptor(@Nullable Object schemaDescriptor) {
193       this.schemaDescriptor = schemaDescriptor;
194       return this;
195     }
196 
197     /**
198      * Constructs a new {@link ServiceDescriptor}.  {@link #setName} should have been called with a
199      * non-{@code null} value before calling this.
200      *
201      * @return a new ServiceDescriptor
202      * @since 1.1.0
203      */
build()204     public ServiceDescriptor build() {
205       return new ServiceDescriptor(this);
206     }
207   }
208 
209   @Override
toString()210   public String toString() {
211     return MoreObjects.toStringHelper(this)
212         .add("name", name)
213         .add("schemaDescriptor", schemaDescriptor)
214         .add("methods", methods)
215         .omitNullValues()
216         .toString();
217   }
218 }