blob: 84aa73e6bcde3ae1040acbb2d18763acd66ba60b [file] [log] [blame]
/*
*
* 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);
}