/* * * 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 #include #include #include "TestOptions.h" #include "qpid/client/AsyncSession.h" #include "qpid/client/FailoverManager.h" #include "qpid/client/Message.h" #include "qpid/client/SubscriptionManager.h" #include "qpid/log/Statement.h" #include "qpid/sys/Thread.h" using namespace qpid::client; using namespace qpid::sys; namespace qpid { namespace tests { struct Args : public qpid::TestOptions { std::string workQueue; size_t workers; Args() : workQueue("txshift-control"), workers(1) { addOptions() ("workers", qpid::optValue(workers, "N"), "Number of separate worker sessions to start") ("work-queue", qpid::optValue(workQueue, "NAME"), "work queue from which to take instructions"); } }; struct Transfer : MessageListener { std::string control; std::string source; std::string destination; uint expected; uint transfered; SubscriptionSettings controlSettings; Subscription controlSubscription; SubscriptionSettings sourceSettings; Subscription sourceSubscription; Transfer(const std::string control_) : control(control_), expected(0), transfered(0) {} void subscribeToSource(SubscriptionManager manager) { sourceSettings.autoAck = 0;//will accept once at the end of the batch sourceSettings.flowControl = FlowControl::messageCredit(expected); sourceSubscription = manager.subscribe(*this, source, sourceSettings); QPID_LOG(info, "Subscribed to source: " << source << " expecting: " << expected); } void subscribeToControl(SubscriptionManager manager) { controlSettings.flowControl = FlowControl::messageCredit(1); controlSubscription = manager.subscribe(*this, control, controlSettings); QPID_LOG(info, "Subscribed to job queue"); } void received(Message& message) { QPID_LOG(debug, "received: " << message.getData() << " for " << message.getDestination()); if (message.getDestination() == source) { receivedFromSource(message); } else if (message.getDestination() == control) { receivedFromControl(message); } else { QPID_LOG(error, "Unexpected message: " << message.getData() << " to " << message.getDestination()); } } void receivedFromSource(Message& message) { QPID_LOG(debug, "transfering " << (transfered+1) << " of " << expected); message.getDeliveryProperties().setRoutingKey(destination); async(sourceSubscription.getSession()).messageTransfer(arg::content=message); if (++transfered == expected) { QPID_LOG(info, "completed job: " << transfered << " messages shifted from " << source << " to " << destination); sourceSubscription.accept(sourceSubscription.getUnaccepted()); sourceSubscription.getSession().txCommit(); sourceSubscription.cancel(); //grant credit to allow broker to send us another control message controlSubscription.grantMessageCredit(1); } } void receivedFromControl(Message& message) { if (message.getData() == "transfer") { source = message.getHeaders().getAsString("src"); destination = message.getHeaders().getAsString("dest"); expected = message.getHeaders().getAsInt("count"); transfered = 0; QPID_LOG(info, "received transfer request: " << expected << " messages to be shifted from " << source << " to " << destination); subscribeToSource(controlSubscription.getSubscriptionManager()); } else if (message.getData() == "quit") { QPID_LOG(info, "received quit request"); controlSubscription.cancel(); } else { std::cerr << "Rejecting invalid message: " << message.getData() << std::endl; controlSubscription.getSession().messageReject(SequenceSet(message.getId())); } } }; struct Worker : FailoverManager::Command, Runnable { FailoverManager& connection; Transfer transfer; Thread runner; Worker(FailoverManager& c, const std::string& controlQueue) : connection(c), transfer(controlQueue) {} void run() { connection.execute(*this); } void start() { runner = Thread(this); } void join() { runner.join(); } void execute(AsyncSession& session, bool isRetry) { if (isRetry) QPID_LOG(info, "Retrying..."); session.txSelect(); SubscriptionManager subs(session); transfer.subscribeToControl(subs); subs.run(); session.txCommit();//commit accept of control messages } }; }} // namespace qpid::tests using namespace qpid::tests; int main(int argc, char** argv) { Args opts; try { opts.parse(argc, argv); FailoverManager connection(opts.con); connection.connect(); if (opts.workers == 1) { Worker worker(connection, opts.workQueue); worker.run(); } else { boost::ptr_vector workers; for (size_t i = 0; i < opts.workers; i++) { workers.push_back(new Worker(connection, opts.workQueue)); } std::for_each(workers.begin(), workers.end(), boost::bind(&Worker::start, _1)); std::for_each(workers.begin(), workers.end(), boost::bind(&Worker::join, _1)); } return 0; } catch(const std::exception& e) { std::cout << e.what() << std::endl; return 1; } }