blob: bdd6bad5c7f51b6349b0804324473e4a4142b8ef [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Protocol Buffers - Google's data interchange format
2# Copyright 2008 Google Inc. All rights reserved.
3# http://code.google.com/p/protobuf/
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15# * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Contains metaclasses used to create protocol service and service stub
32classes from ServiceDescriptor objects at runtime.
33
34The GeneratedServiceType and GeneratedServiceStubType metaclasses are used to
35inject all useful functionality into the classes output by the protocol
36compiler at compile-time.
37"""
38
39__author__ = 'petar@google.com (Petar Petrov)'
40
41
42class GeneratedServiceType(type):
43
44 """Metaclass for service classes created at runtime from ServiceDescriptors.
45
46 Implementations for all methods described in the Service class are added here
47 by this class. We also create properties to allow getting/setting all fields
48 in the protocol message.
49
50 The protocol compiler currently uses this metaclass to create protocol service
51 classes at runtime. Clients can also manually create their own classes at
52 runtime, as in this example:
53
54 mydescriptor = ServiceDescriptor(.....)
55 class MyProtoService(service.Service):
56 __metaclass__ = GeneratedServiceType
57 DESCRIPTOR = mydescriptor
58 myservice_instance = MyProtoService()
59 ...
60 """
61
62 _DESCRIPTOR_KEY = 'DESCRIPTOR'
63
64 def __init__(cls, name, bases, dictionary):
65 """Creates a message service class.
66
67 Args:
68 name: Name of the class (ignored, but required by the metaclass
69 protocol).
70 bases: Base classes of the class being constructed.
71 dictionary: The class dictionary of the class being constructed.
72 dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object
73 describing this protocol service type.
74 """
75 # Don't do anything if this class doesn't have a descriptor. This happens
76 # when a service class is subclassed.
77 if GeneratedServiceType._DESCRIPTOR_KEY not in dictionary:
78 return
79 descriptor = dictionary[GeneratedServiceType._DESCRIPTOR_KEY]
80 service_builder = _ServiceBuilder(descriptor)
81 service_builder.BuildService(cls)
82
83
84class GeneratedServiceStubType(GeneratedServiceType):
85
86 """Metaclass for service stubs created at runtime from ServiceDescriptors.
87
88 This class has similar responsibilities as GeneratedServiceType, except that
89 it creates the service stub classes.
90 """
91
92 _DESCRIPTOR_KEY = 'DESCRIPTOR'
93
94 def __init__(cls, name, bases, dictionary):
95 """Creates a message service stub class.
96
97 Args:
98 name: Name of the class (ignored, here).
99 bases: Base classes of the class being constructed.
100 dictionary: The class dictionary of the class being constructed.
101 dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object
102 describing this protocol service type.
103 """
104 super(GeneratedServiceStubType, cls).__init__(name, bases, dictionary)
105 # Don't do anything if this class doesn't have a descriptor. This happens
106 # when a service stub is subclassed.
107 if GeneratedServiceStubType._DESCRIPTOR_KEY not in dictionary:
108 return
109 descriptor = dictionary[GeneratedServiceStubType._DESCRIPTOR_KEY]
110 service_stub_builder = _ServiceStubBuilder(descriptor)
111 service_stub_builder.BuildServiceStub(cls)
112
113
114class _ServiceBuilder(object):
115
116 """This class constructs a protocol service class using a service descriptor.
117
118 Given a service descriptor, this class constructs a class that represents
119 the specified service descriptor. One service builder instance constructs
120 exactly one service class. That means all instances of that class share the
121 same builder.
122 """
123
124 def __init__(self, service_descriptor):
125 """Initializes an instance of the service class builder.
126
127 Args:
128 service_descriptor: ServiceDescriptor to use when constructing the
129 service class.
130 """
131 self.descriptor = service_descriptor
132
133 def BuildService(self, cls):
134 """Constructs the service class.
135
136 Args:
137 cls: The class that will be constructed.
138 """
139
140 # CallMethod needs to operate with an instance of the Service class. This
141 # internal wrapper function exists only to be able to pass the service
142 # instance to the method that does the real CallMethod work.
143 def _WrapCallMethod(srvc, method_descriptor,
144 rpc_controller, request, callback):
145 self._CallMethod(srvc, method_descriptor,
146 rpc_controller, request, callback)
147 self.cls = cls
148 cls.CallMethod = _WrapCallMethod
149 cls.GetDescriptor = self._GetDescriptor
150 cls.GetRequestClass = self._GetRequestClass
151 cls.GetResponseClass = self._GetResponseClass
152 for method in self.descriptor.methods:
153 setattr(cls, method.name, self._GenerateNonImplementedMethod(method))
154
155 def _GetDescriptor(self):
156 """Retrieves the service descriptor.
157
158 Returns:
159 The descriptor of the service (of type ServiceDescriptor).
160 """
161 return self.descriptor
162
163 def _CallMethod(self, srvc, method_descriptor,
164 rpc_controller, request, callback):
165 """Calls the method described by a given method descriptor.
166
167 Args:
168 srvc: Instance of the service for which this method is called.
169 method_descriptor: Descriptor that represent the method to call.
170 rpc_controller: RPC controller to use for this method's execution.
171 request: Request protocol message.
172 callback: A callback to invoke after the method has completed.
173 """
174 if method_descriptor.containing_service != self.descriptor:
175 raise RuntimeError(
176 'CallMethod() given method descriptor for wrong service type.')
177 method = getattr(srvc, method_descriptor.name)
178 method(rpc_controller, request, callback)
179
180 def _GetRequestClass(self, method_descriptor):
181 """Returns the class of the request protocol message.
182
183 Args:
184 method_descriptor: Descriptor of the method for which to return the
185 request protocol message class.
186
187 Returns:
188 A class that represents the input protocol message of the specified
189 method.
190 """
191 if method_descriptor.containing_service != self.descriptor:
192 raise RuntimeError(
193 'GetRequestClass() given method descriptor for wrong service type.')
194 return method_descriptor.input_type._concrete_class
195
196 def _GetResponseClass(self, method_descriptor):
197 """Returns the class of the response protocol message.
198
199 Args:
200 method_descriptor: Descriptor of the method for which to return the
201 response protocol message class.
202
203 Returns:
204 A class that represents the output protocol message of the specified
205 method.
206 """
207 if method_descriptor.containing_service != self.descriptor:
208 raise RuntimeError(
209 'GetResponseClass() given method descriptor for wrong service type.')
210 return method_descriptor.output_type._concrete_class
211
212 def _GenerateNonImplementedMethod(self, method):
213 """Generates and returns a method that can be set for a service methods.
214
215 Args:
216 method: Descriptor of the service method for which a method is to be
217 generated.
218
219 Returns:
220 A method that can be added to the service class.
221 """
222 return lambda inst, rpc_controller, request, callback: (
223 self._NonImplementedMethod(method.name, rpc_controller, callback))
224
225 def _NonImplementedMethod(self, method_name, rpc_controller, callback):
226 """The body of all methods in the generated service class.
227
228 Args:
229 method_name: Name of the method being executed.
230 rpc_controller: RPC controller used to execute this method.
231 callback: A callback which will be invoked when the method finishes.
232 """
233 rpc_controller.SetFailed('Method %s not implemented.' % method_name)
234 callback(None)
235
236
237class _ServiceStubBuilder(object):
238
239 """Constructs a protocol service stub class using a service descriptor.
240
241 Given a service descriptor, this class constructs a suitable stub class.
242 A stub is just a type-safe wrapper around an RpcChannel which emulates a
243 local implementation of the service.
244
245 One service stub builder instance constructs exactly one class. It means all
246 instances of that class share the same service stub builder.
247 """
248
249 def __init__(self, service_descriptor):
250 """Initializes an instance of the service stub class builder.
251
252 Args:
253 service_descriptor: ServiceDescriptor to use when constructing the
254 stub class.
255 """
256 self.descriptor = service_descriptor
257
258 def BuildServiceStub(self, cls):
259 """Constructs the stub class.
260
261 Args:
262 cls: The class that will be constructed.
263 """
264
265 def _ServiceStubInit(stub, rpc_channel):
266 stub.rpc_channel = rpc_channel
267 self.cls = cls
268 cls.__init__ = _ServiceStubInit
269 for method in self.descriptor.methods:
270 setattr(cls, method.name, self._GenerateStubMethod(method))
271
272 def _GenerateStubMethod(self, method):
273 return lambda inst, rpc_controller, request, callback: self._StubMethod(
274 inst, method, rpc_controller, request, callback)
275
276 def _StubMethod(self, stub, method_descriptor,
277 rpc_controller, request, callback):
278 """The body of all service methods in the generated stub class.
279
280 Args:
281 stub: Stub instance.
282 method_descriptor: Descriptor of the invoked method.
283 rpc_controller: Rpc controller to execute the method.
284 request: Request protocol message.
285 callback: A callback to execute when the method finishes.
286 """
287 stub.rpc_channel.CallMethod(
288 method_descriptor, rpc_controller, request,
289 method_descriptor.output_type._concrete_class, callback)