blob: 138f95bc48f46f1557792db41fd2b35e92d24bd5 [file] [log] [blame]
Jens Geyere8ca73f2014-03-24 21:41:12 +02001<?php
2/*
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 * @package thrift.processor
21 */
22
23namespace Thrift;
24
Jens Geyer80014212014-04-01 21:24:27 +020025use Thrift\Exception\TException;
Jens Geyere8ca73f2014-03-24 21:41:12 +020026use Thrift\Protocol\TProtocol;
27use Thrift\Protocol\TMultiplexedProtocol;
28use Thrift\Protocol\TProtocolDecorator;
29use Thrift\Type\TMessageType;
30
31/**
32 * <code>TMultiplexedProcessor</code> is a Processor allowing
33 * a single <code>TServer</code> to provide multiple services.
34 *
35 * <p>To do so, you instantiate the processor and then register additional
36 * processors with it, as shown in the following example:</p>
37 *
38 * <blockquote><code>
39 * $processor = new TMultiplexedProcessor();
40 *
41 * processor->registerProcessor(
42 * "Calculator",
43 * new \tutorial\CalculatorProcessor(new CalculatorHandler()));
44 *
45 * processor->registerProcessor(
46 * "WeatherReport",
47 * new \tutorial\WeatherReportProcessor(new WeatherReportHandler()));
48 *
49 * $processor->process($protocol, $protocol);
50 * </code></blockquote>
51 */
52
Roger Thomas6fb59232014-11-04 10:09:23 +000053class TMultiplexedProcessor
54{
Jens Geyere8ca73f2014-03-24 21:41:12 +020055 private $serviceProcessorMap_;
56
57 /**
58 * 'Register' a service with this <code>TMultiplexedProcessor</code>. This
59 * allows us to broker requests to individual services by using the service
60 * name to select them at request time.
61 *
62 * @param serviceName Name of a service, has to be identical to the name
63 * declared in the Thrift IDL, e.g. "WeatherReport".
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +010064 * @param processor Implementation of a service, usually referred to
Jens Geyere8ca73f2014-03-24 21:41:12 +020065 * as "handlers", e.g. WeatherReportHandler implementing WeatherReport.Iface.
66 */
Roger Thomas6fb59232014-11-04 10:09:23 +000067 public function registerProcessor($serviceName, $processor)
68 {
Jens Geyere8ca73f2014-03-24 21:41:12 +020069 $this->serviceProcessorMap_[$serviceName] = $processor;
70 }
71
72 /**
73 * This implementation of <code>process</code> performs the following steps:
74 *
75 * <ol>
76 * <li>Read the beginning of the message.</li>
77 * <li>Extract the service name from the message.</li>
78 * <li>Using the service name to locate the appropriate processor.</li>
79 * <li>Dispatch to the processor, with a decorated instance of TProtocol
80 * that allows readMessageBegin() to return the original Message.</li>
81 * </ol>
82 *
83 * @throws TException If the message type is not CALL or ONEWAY, if
Roger Thomas6fb59232014-11-04 10:09:23 +000084 * the service name was not found in the message, or if the service
85 * name was not found in the service map.
Jens Geyere8ca73f2014-03-24 21:41:12 +020086 */
Roger Thomas6fb59232014-11-04 10:09:23 +000087 public function process(TProtocol $input, TProtocol $output)
88 {
Jens Geyere8ca73f2014-03-24 21:41:12 +020089 /*
90 Use the actual underlying protocol (e.g. TBinaryProtocol) to read the
91 message header. This pulls the message "off the wire", which we'll
92 deal with at the end of this method.
93 */
94 $input->readMessageBegin($fname, $mtype, $rseqid);
95
96 if ($mtype !== TMessageType::CALL && $mtype != TMessageType::ONEWAY) {
97 throw new TException("This should not have happened!?");
98 }
99
100 // Extract the service name and the new Message name.
101 if (strpos($fname, TMultiplexedProtocol::SEPARATOR) === false) {
102 throw new TException("Service name not found in message name: {$fname}. Did you " .
103 "forget to use a TMultiplexProtocol in your client?");
104 }
105 list($serviceName, $messageName) = explode(':', $fname, 2);
106 if (!array_key_exists($serviceName, $this->serviceProcessorMap_)) {
107 throw new TException("Service name not found: {$serviceName}. Did you forget " .
108 "to call registerProcessor()?");
109 }
110
111 // Dispatch processing to the stored processor
112 $processor = $this->serviceProcessorMap_[$serviceName];
Roger Thomas6fb59232014-11-04 10:09:23 +0000113
Jens Geyere8ca73f2014-03-24 21:41:12 +0200114 return $processor->process(
115 new StoredMessageProtocol($input, $messageName, $mtype, $rseqid), $output
116 );
117 }
118}
119
120/**
121 * Our goal was to work with any protocol. In order to do that, we needed
122 * to allow them to call readMessageBegin() and get the Message in exactly
123 * the standard format, without the service name prepended to the Message name.
124 */
Roger Thomas6fb59232014-11-04 10:09:23 +0000125class StoredMessageProtocol extends TProtocolDecorator
126{
Jens Geyere8ca73f2014-03-24 21:41:12 +0200127 private $fname_, $mtype_, $rseqid_;
128
Roger Thomas6fb59232014-11-04 10:09:23 +0000129 public function __construct(TProtocol $protocol, $fname, $mtype, $rseqid)
130 {
Jens Geyere8ca73f2014-03-24 21:41:12 +0200131 parent::__construct($protocol);
132 $this->fname_ = $fname;
133 $this->mtype_ = $mtype;
134 $this->rseqid_ = $rseqid;
135 }
136
Roger Thomas6fb59232014-11-04 10:09:23 +0000137 public function readMessageBegin(&$name, &$type, &$seqid)
138 {
Jens Geyere8ca73f2014-03-24 21:41:12 +0200139 $name = $this->fname_;
140 $type = $this->mtype_;
141 $seqid = $this->rseqid_;
142 }
143}