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.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static com.google.common.base.Preconditions.checkState; 22 23 import java.util.ArrayList; 24 import java.util.Collection; 25 import java.util.Collections; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 30 /** Definition of a service to be exposed via a Server. */ 31 public final class ServerServiceDefinition { 32 /** Convenience that constructs a {@link ServiceDescriptor} simultaneously. */ builder(String serviceName)33 public static Builder builder(String serviceName) { 34 return new Builder(serviceName); 35 } 36 builder(ServiceDescriptor serviceDescriptor)37 public static Builder builder(ServiceDescriptor serviceDescriptor) { 38 return new Builder(serviceDescriptor); 39 } 40 41 private final ServiceDescriptor serviceDescriptor; 42 private final Map<String, ServerMethodDefinition<?, ?>> methods; 43 ServerServiceDefinition( ServiceDescriptor serviceDescriptor, Map<String, ServerMethodDefinition<?, ?>> methods)44 private ServerServiceDefinition( 45 ServiceDescriptor serviceDescriptor, Map<String, ServerMethodDefinition<?, ?>> methods) { 46 this.serviceDescriptor = checkNotNull(serviceDescriptor, "serviceDescriptor"); 47 this.methods = 48 Collections.unmodifiableMap(new HashMap<String, ServerMethodDefinition<?, ?>>(methods)); 49 } 50 51 /** 52 * The descriptor for the service. 53 */ getServiceDescriptor()54 public ServiceDescriptor getServiceDescriptor() { 55 return serviceDescriptor; 56 } 57 58 /** 59 * Gets all the methods of service. 60 */ getMethods()61 public Collection<ServerMethodDefinition<?, ?>> getMethods() { 62 return methods.values(); 63 } 64 65 /** 66 * Look up a method by its fully qualified name. 67 * 68 * @param methodName the fully qualified name without leading slash. E.g., "com.foo.Foo/Bar" 69 */ 70 @Internal getMethod(String methodName)71 public ServerMethodDefinition<?, ?> getMethod(String methodName) { 72 return methods.get(methodName); 73 } 74 75 /** 76 * Builder for constructing Service instances. 77 */ 78 public static final class Builder { 79 private final String serviceName; 80 private final ServiceDescriptor serviceDescriptor; 81 private final Map<String, ServerMethodDefinition<?, ?>> methods = 82 new HashMap<String, ServerMethodDefinition<?, ?>>(); 83 Builder(String serviceName)84 private Builder(String serviceName) { 85 this.serviceName = checkNotNull(serviceName, "serviceName"); 86 this.serviceDescriptor = null; 87 } 88 Builder(ServiceDescriptor serviceDescriptor)89 private Builder(ServiceDescriptor serviceDescriptor) { 90 this.serviceDescriptor = checkNotNull(serviceDescriptor, "serviceDescriptor"); 91 this.serviceName = serviceDescriptor.getName(); 92 } 93 94 /** 95 * Add a method to be supported by the service. 96 * 97 * @param method the {@link MethodDescriptor} of this method. 98 * @param handler handler for incoming calls 99 */ addMethod( MethodDescriptor<ReqT, RespT> method, ServerCallHandler<ReqT, RespT> handler)100 public <ReqT, RespT> Builder addMethod( 101 MethodDescriptor<ReqT, RespT> method, ServerCallHandler<ReqT, RespT> handler) { 102 return addMethod(ServerMethodDefinition.create( 103 checkNotNull(method, "method must not be null"), 104 checkNotNull(handler, "handler must not be null"))); 105 } 106 107 /** Add a method to be supported by the service. */ addMethod(ServerMethodDefinition<ReqT, RespT> def)108 public <ReqT, RespT> Builder addMethod(ServerMethodDefinition<ReqT, RespT> def) { 109 MethodDescriptor<ReqT, RespT> method = def.getMethodDescriptor(); 110 checkArgument( 111 serviceName.equals(MethodDescriptor.extractFullServiceName(method.getFullMethodName())), 112 "Method name should be prefixed with service name and separated with '/'. " 113 + "Expected service name: '%s'. Actual fully qualifed method name: '%s'.", 114 serviceName, method.getFullMethodName()); 115 String name = method.getFullMethodName(); 116 checkState(!methods.containsKey(name), "Method by same name already registered: %s", name); 117 methods.put(name, def); 118 return this; 119 } 120 121 /** 122 * Construct new ServerServiceDefinition. 123 */ build()124 public ServerServiceDefinition build() { 125 ServiceDescriptor serviceDescriptor = this.serviceDescriptor; 126 if (serviceDescriptor == null) { 127 List<MethodDescriptor<?, ?>> methodDescriptors 128 = new ArrayList<MethodDescriptor<?, ?>>(methods.size()); 129 for (ServerMethodDefinition<?, ?> serverMethod : methods.values()) { 130 methodDescriptors.add(serverMethod.getMethodDescriptor()); 131 } 132 serviceDescriptor = new ServiceDescriptor(serviceName, methodDescriptors); 133 } 134 Map<String, ServerMethodDefinition<?, ?>> tmpMethods = 135 new HashMap<String, ServerMethodDefinition<?, ?>>(methods); 136 for (MethodDescriptor<?, ?> descriptorMethod : serviceDescriptor.getMethods()) { 137 ServerMethodDefinition<?, ?> removed = tmpMethods.remove( 138 descriptorMethod.getFullMethodName()); 139 if (removed == null) { 140 throw new IllegalStateException( 141 "No method bound for descriptor entry " + descriptorMethod.getFullMethodName()); 142 } 143 if (removed.getMethodDescriptor() != descriptorMethod) { 144 throw new IllegalStateException( 145 "Bound method for " + descriptorMethod.getFullMethodName() 146 + " not same instance as method in service descriptor"); 147 } 148 } 149 if (tmpMethods.size() > 0) { 150 throw new IllegalStateException( 151 "No entry in descriptor matching bound method " 152 + tmpMethods.values().iterator().next().getMethodDescriptor().getFullMethodName()); 153 } 154 return new ServerServiceDefinition(serviceDescriptor, methods); 155 } 156 } 157 } 158