| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| #include <iostream> |
| #include <ClientChannel.h> |
| #include <sys/Monitor.h> |
| #include <ClientMessage.h> |
| #include <QpidError.h> |
| #include <MethodBodyInstances.h> |
| #include "Connection.h" |
| |
| // FIXME aconway 2007-01-26: Evaluate all throws, ensure consistent |
| // handling of errors that should close the connection or the channel. |
| // Make sure the user thread receives a connection in each case. |
| // |
| using namespace std; |
| using namespace boost; |
| using namespace qpid::client; |
| using namespace qpid::framing; |
| using namespace qpid::sys; |
| |
| Channel::Channel(bool _transactional, uint16_t _prefetch) : |
| basic(*this), |
| connection(0), |
| prefetch(_prefetch), |
| transactional(_transactional) |
| { } |
| |
| Channel::~Channel(){ |
| close(); |
| } |
| |
| void Channel::open(ChannelId id, Connection& con) |
| { |
| if (isOpen()) |
| THROW_QPID_ERROR(INTERNAL_ERROR, "Attempt to re-open channel "+id); |
| connection = &con; |
| init(id, con, con.getVersion()); // ChannelAdapter initialization. |
| string oob; |
| if (id != 0) |
| sendAndReceive<ChannelOpenOkBody>(new ChannelOpenBody(version, oob)); |
| } |
| |
| void Channel::protocolInit( |
| const std::string& uid, const std::string& pwd, const std::string& vhost) { |
| assert(connection); |
| responses.expect(); |
| connection->connector->init(); // Send ProtocolInit block. |
| responses.receive<ConnectionStartBody>(); |
| |
| FieldTable props; |
| string mechanism("PLAIN"); |
| string response = ((char)0) + uid + ((char)0) + pwd; |
| string locale("en_US"); |
| ConnectionTuneBody::shared_ptr proposal = |
| sendAndReceive<ConnectionTuneBody>( |
| new ConnectionStartOkBody( |
| version, responses.getRequestId(), props, mechanism, |
| response, locale)); |
| |
| /** |
| * Assume for now that further challenges will not be required |
| //receive connection.secure |
| responses.receive(connection_secure)); |
| //send connection.secure-ok |
| connection->send(new AMQFrame(0, new ConnectionSecureOkBody(response))); |
| **/ |
| |
| send(new ConnectionTuneOkBody( |
| version, responses.getRequestId(), proposal->getChannelMax(), connection->getMaxFrameSize(), |
| proposal->getHeartbeat())); |
| |
| uint16_t heartbeat = proposal->getHeartbeat(); |
| connection->connector->setReadTimeout(heartbeat * 2); |
| connection->connector->setWriteTimeout(heartbeat); |
| |
| // Send connection open. |
| std::string capabilities; |
| responses.expect(); |
| send(new ConnectionOpenBody(version, vhost, capabilities, true)); |
| //receive connection.open-ok (or redirect, but ignore that for now |
| //esp. as using force=true). |
| responses.waitForResponse(); |
| if(responses.validate<ConnectionOpenOkBody>()) { |
| //ok |
| }else if(responses.validate<ConnectionRedirectBody>()){ |
| //ignore for now |
| ConnectionRedirectBody::shared_ptr redirect( |
| shared_polymorphic_downcast<ConnectionRedirectBody>( |
| responses.getResponse())); |
| cout << "Received redirection to " << redirect->getHost() |
| << endl; |
| } else { |
| THROW_QPID_ERROR(PROTOCOL_ERROR, "Bad response"); |
| } |
| } |
| |
| bool Channel::isOpen() const { return connection; } |
| |
| void Channel::setQos() { |
| basic.setQos(); |
| // FIXME aconway 2007-02-22: message |
| } |
| |
| void Channel::setPrefetch(uint16_t _prefetch){ |
| prefetch = _prefetch; |
| setQos(); |
| } |
| |
| void Channel::declareExchange(Exchange& exchange, bool synch){ |
| string name = exchange.getName(); |
| string type = exchange.getType(); |
| FieldTable args; |
| sendAndReceiveSync<ExchangeDeclareOkBody>( |
| synch, |
| new ExchangeDeclareBody( |
| version, 0, name, type, false, false, false, false, !synch, args)); |
| } |
| |
| void Channel::deleteExchange(Exchange& exchange, bool synch){ |
| string name = exchange.getName(); |
| sendAndReceiveSync<ExchangeDeleteOkBody>( |
| synch, |
| new ExchangeDeleteBody(version, 0, name, false, !synch)); |
| } |
| |
| void Channel::declareQueue(Queue& queue, bool synch){ |
| string name = queue.getName(); |
| FieldTable args; |
| sendAndReceiveSync<QueueDeclareOkBody>( |
| synch, |
| new QueueDeclareBody( |
| version, 0, name, false/*passive*/, queue.isDurable(), |
| queue.isExclusive(), queue.isAutoDelete(), !synch, args)); |
| if(synch){ |
| if(queue.getName().length() == 0){ |
| QueueDeclareOkBody::shared_ptr response = |
| shared_polymorphic_downcast<QueueDeclareOkBody>( |
| responses.getResponse()); |
| queue.setName(response->getQueue()); |
| } |
| } |
| } |
| |
| void Channel::deleteQueue(Queue& queue, bool ifunused, bool ifempty, bool synch){ |
| //ticket, queue, ifunused, ifempty, nowait |
| string name = queue.getName(); |
| sendAndReceiveSync<QueueDeleteOkBody>( |
| synch, |
| new QueueDeleteBody(version, 0, name, ifunused, ifempty, !synch)); |
| } |
| |
| void Channel::bind(const Exchange& exchange, const Queue& queue, const std::string& key, const FieldTable& args, bool synch){ |
| string e = exchange.getName(); |
| string q = queue.getName(); |
| sendAndReceiveSync<QueueBindOkBody>( |
| synch, |
| new QueueBindBody(version, 0, q, e, key,!synch, args)); |
| } |
| |
| void Channel::commit(){ |
| sendAndReceive<TxCommitOkBody>(new TxCommitBody(version)); |
| } |
| |
| void Channel::rollback(){ |
| sendAndReceive<TxRollbackOkBody>(new TxRollbackBody(version)); |
| } |
| |
| void Channel::handleMethodInContext( |
| AMQMethodBody::shared_ptr method, const MethodContext&) |
| { |
| if(responses.isWaiting()) { |
| responses.signalResponse(method); |
| return; |
| } |
| try { |
| switch (method->amqpClassId()) { |
| case BasicDeliverBody::CLASS_ID: basic.handle(method); break; |
| case ChannelCloseBody::CLASS_ID: handleChannel(method); break; |
| case ConnectionCloseBody::CLASS_ID: handleConnection(method); break; |
| default: throw UnknownMethod(); |
| } |
| } |
| catch (const UnknownMethod&) { |
| connection->close( |
| 504, "Unknown method", |
| method->amqpClassId(), method->amqpMethodId()); |
| } |
| } |
| |
| void Channel::handleChannel(AMQMethodBody::shared_ptr method) { |
| switch (method->amqpMethodId()) { |
| case ChannelCloseBody::METHOD_ID: |
| peerClose(shared_polymorphic_downcast<ChannelCloseBody>(method)); |
| return; |
| case ChannelFlowBody::METHOD_ID: |
| // FIXME aconway 2007-02-22: Not yet implemented. |
| return; |
| } |
| throw UnknownMethod(); |
| } |
| |
| void Channel::handleConnection(AMQMethodBody::shared_ptr method) { |
| if (method->amqpMethodId() == ConnectionCloseBody::METHOD_ID) { |
| connection->close(); |
| return; |
| } |
| throw UnknownMethod(); |
| } |
| |
| void Channel::handleHeader(AMQHeaderBody::shared_ptr body){ |
| basic.incoming.add(body); |
| } |
| |
| void Channel::handleContent(AMQContentBody::shared_ptr body){ |
| basic.incoming.add(body); |
| } |
| |
| void Channel::handleHeartbeat(AMQHeartbeatBody::shared_ptr /*body*/){ |
| THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Channel received heartbeat"); |
| } |
| |
| void Channel::start(){ |
| basicDispatcher = Thread(basic); |
| } |
| |
| // Close called by local application. |
| void Channel::close( |
| uint16_t code, const std::string& text, |
| ClassId classId, MethodId methodId) |
| { |
| if (isOpen()) { |
| try { |
| if (getId() != 0) { |
| sendAndReceive<ChannelCloseOkBody>( |
| new ChannelCloseBody( |
| version, code, text, classId, methodId)); |
| } |
| static_cast<ConnectionForChannel*>(connection)->erase(getId()); |
| closeInternal(); |
| } catch (...) { |
| static_cast<ConnectionForChannel*>(connection)->erase(getId()); |
| closeInternal(); |
| throw; |
| } |
| } |
| } |
| |
| // Channel closed by peer. |
| void Channel::peerClose(ChannelCloseBody::shared_ptr) { |
| assert(isOpen()); |
| closeInternal(); |
| // FIXME aconway 2007-01-26: How to throw the proper exception |
| // to the application thread? |
| } |
| |
| void Channel::closeInternal() { |
| if (isOpen()); |
| { |
| basic.cancelAll(); |
| basic.incoming.shutdown(); |
| connection = 0; |
| // A 0 response means we are closed. |
| responses.signalResponse(AMQMethodBody::shared_ptr()); |
| } |
| basicDispatcher.join(); |
| } |
| |
| void Channel::sendAndReceive(AMQMethodBody* toSend, ClassId c, MethodId m) |
| { |
| responses.expect(); |
| send(toSend); |
| responses.receive(c, m); |
| } |
| |
| void Channel::sendAndReceiveSync( |
| bool sync, AMQMethodBody* body, ClassId c, MethodId m) |
| { |
| if(sync) |
| sendAndReceive(body, c, m); |
| else |
| send(body); |
| } |
| |
| |