]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /** |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | * | |
19 | * Contains some contributions under the Thrift Software License. | |
20 | * Please see doc/old-thrift-license.txt in the Thrift distribution for | |
21 | * details. | |
22 | */ | |
23 | ||
24 | using System; | |
25 | using System.Text; | |
26 | using Thrift.Transport; | |
27 | using System.Collections.Generic; | |
28 | using System.IO; | |
29 | ||
30 | namespace Thrift.Protocol | |
31 | { | |
32 | /// <summary> | |
33 | /// <see cref="TMultiplexedProcessor"/> is a <see cref="TProcessor"/> allowing a single <see cref="Thrift.Server.TServer"/> | |
34 | /// to provide multiple services. | |
35 | /// <para/> | |
36 | /// To do so, you instantiate the processor and then register additional processors with it, | |
37 | /// as shown in the following example: | |
38 | /// <para/> | |
39 | /// <code> | |
40 | /// TMultiplexedProcessor processor = new TMultiplexedProcessor(); | |
41 | /// | |
42 | /// processor.registerProcessor( | |
43 | /// "Calculator", | |
44 | /// new Calculator.Processor(new CalculatorHandler())); | |
45 | /// | |
46 | /// processor.registerProcessor( | |
47 | /// "WeatherReport", | |
48 | /// new WeatherReport.Processor(new WeatherReportHandler())); | |
49 | /// | |
50 | /// TServerTransport t = new TServerSocket(9090); | |
51 | /// TSimpleServer server = new TSimpleServer(processor, t); | |
52 | /// | |
53 | /// server.serve(); | |
54 | /// </code> | |
55 | /// </summary> | |
56 | public class TMultiplexedProcessor : TProcessor | |
57 | { | |
58 | private Dictionary<string, TProcessor> ServiceProcessorMap = new Dictionary<string, TProcessor>(); | |
59 | ||
60 | /// <summary> | |
61 | /// 'Register' a service with this TMultiplexedProcessor. This allows us to broker | |
62 | /// requests to individual services by using the service name to select them at request time. | |
63 | /// | |
64 | /// Args: | |
65 | /// - serviceName Name of a service, has to be identical to the name | |
66 | /// declared in the Thrift IDL, e.g. "WeatherReport". | |
67 | /// - processor Implementation of a service, usually referred to as "handlers", | |
68 | /// e.g. WeatherReportHandler implementing WeatherReport.Iface. | |
69 | /// </summary> | |
70 | public void RegisterProcessor(string serviceName, TProcessor processor) | |
71 | { | |
72 | ServiceProcessorMap.Add(serviceName, processor); | |
73 | } | |
74 | ||
75 | ||
76 | private void Fail(TProtocol oprot, TMessage message, TApplicationException.ExceptionType extype, string etxt) | |
77 | { | |
78 | TApplicationException appex = new TApplicationException(extype, etxt); | |
79 | ||
80 | TMessage newMessage = new TMessage(message.Name, TMessageType.Exception, message.SeqID); | |
81 | ||
82 | oprot.WriteMessageBegin(newMessage); | |
83 | appex.Write(oprot); | |
84 | oprot.WriteMessageEnd(); | |
85 | oprot.Transport.Flush(); | |
86 | } | |
87 | ||
88 | ||
89 | /// <summary> | |
90 | /// This implementation of process performs the following steps: | |
91 | /// | |
92 | /// - Read the beginning of the message. | |
93 | /// - Extract the service name from the message. | |
94 | /// - Using the service name to locate the appropriate processor. | |
95 | /// - Dispatch to the processor, with a decorated instance of TProtocol | |
96 | /// that allows readMessageBegin() to return the original TMessage. | |
97 | /// <para/> | |
98 | /// Throws an exception if | |
99 | /// - the message type is not CALL or ONEWAY, | |
100 | /// - the service name was not found in the message, or | |
101 | /// - the service name has not been RegisterProcessor()ed. | |
102 | /// </summary> | |
103 | public bool Process(TProtocol iprot, TProtocol oprot) | |
104 | { | |
105 | /* Use the actual underlying protocol (e.g. TBinaryProtocol) to read the | |
106 | message header. This pulls the message "off the wire", which we'll | |
107 | deal with at the end of this method. */ | |
108 | ||
109 | try | |
110 | { | |
111 | TMessage message = iprot.ReadMessageBegin(); | |
112 | ||
113 | if ((message.Type != TMessageType.Call) && (message.Type != TMessageType.Oneway)) | |
114 | { | |
115 | Fail(oprot, message, | |
116 | TApplicationException.ExceptionType.InvalidMessageType, | |
117 | "Message type CALL or ONEWAY expected"); | |
118 | return false; | |
119 | } | |
120 | ||
121 | // Extract the service name | |
122 | int index = message.Name.IndexOf(TMultiplexedProtocol.SEPARATOR); | |
123 | if (index < 0) | |
124 | { | |
125 | Fail(oprot, message, | |
126 | TApplicationException.ExceptionType.InvalidProtocol, | |
127 | "Service name not found in message name: " + message.Name + ". " + | |
128 | "Did you forget to use a TMultiplexProtocol in your client?"); | |
129 | return false; | |
130 | } | |
131 | ||
132 | // Create a new TMessage, something that can be consumed by any TProtocol | |
133 | string serviceName = message.Name.Substring(0, index); | |
134 | TProcessor actualProcessor; | |
135 | if (!ServiceProcessorMap.TryGetValue(serviceName, out actualProcessor)) | |
136 | { | |
137 | Fail(oprot, message, | |
138 | TApplicationException.ExceptionType.InternalError, | |
139 | "Service name not found: " + serviceName + ". " + | |
140 | "Did you forget to call RegisterProcessor()?"); | |
141 | return false; | |
142 | } | |
143 | ||
144 | // Create a new TMessage, removing the service name | |
145 | TMessage newMessage = new TMessage( | |
146 | message.Name.Substring(serviceName.Length + TMultiplexedProtocol.SEPARATOR.Length), | |
147 | message.Type, | |
148 | message.SeqID); | |
149 | ||
150 | // Dispatch processing to the stored processor | |
151 | return actualProcessor.Process(new StoredMessageProtocol(iprot, newMessage), oprot); | |
152 | ||
153 | } | |
154 | catch (IOException) | |
155 | { | |
156 | return false; // similar to all other processors | |
157 | } | |
158 | ||
159 | } | |
160 | ||
161 | /// <summary> | |
162 | /// Our goal was to work with any protocol. In order to do that, we needed | |
163 | /// to allow them to call readMessageBegin() and get a TMessage in exactly | |
164 | /// the standard format, without the service name prepended to TMessage.name. | |
165 | /// </summary> | |
166 | private class StoredMessageProtocol : TProtocolDecorator | |
167 | { | |
168 | TMessage MsgBegin; | |
169 | ||
170 | public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin) | |
171 | : base(protocol) | |
172 | { | |
173 | this.MsgBegin = messageBegin; | |
174 | } | |
175 | ||
176 | public override TMessage ReadMessageBegin() | |
177 | { | |
178 | return MsgBegin; | |
179 | } | |
180 | } | |
181 | ||
182 | } | |
183 | } |