diff options
author | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
---|---|---|
committer | Rajith Muditha Attapattu <rajith@apache.org> | 2011-05-27 15:44:23 +0000 |
commit | 66765100f4257159622cefe57bed50125a5ad017 (patch) | |
tree | a88ee23bb194eb91f0ebb2d9b23ff423e3ea8e37 /qpid/cpp/src/qmf | |
parent | 1aeaa7b16e5ce54f10c901d75c4d40f9f88b9db6 (diff) | |
parent | 88b98b2f4152ef59a671fad55a0d08338b6b78ca (diff) | |
download | qpid-python-66765100f4257159622cefe57bed50125a5ad017.tar.gz |
Creating a branch for experimenting with some ideas for JMS client.rajith_jms_client
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/rajith_jms_client@1128369 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/cpp/src/qmf')
64 files changed, 12661 insertions, 0 deletions
diff --git a/qpid/cpp/src/qmf/Agent.cpp b/qpid/cpp/src/qmf/Agent.cpp new file mode 100644 index 0000000000..915f2a1c88 --- /dev/null +++ b/qpid/cpp/src/qmf/Agent.cpp @@ -0,0 +1,657 @@ +/* + * + * 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 "qmf/AgentImpl.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/ConsoleEventImpl.h" +#include "qmf/ConsoleSession.h" +#include "qmf/DataImpl.h" +#include "qmf/Query.h" +#include "qmf/SchemaImpl.h" +#include "qmf/agentCapability.h" +#include "qmf/constants.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/AddressParser.h" +#include "qpid/management/Buffer.h" +#include "qpid/log/Statement.h" +#include <boost/lexical_cast.hpp> + +using qpid::types::Variant; +using qpid::messaging::Duration; +using qpid::messaging::Message; +using qpid::messaging::Sender; +using namespace std; +using namespace qmf; + +typedef PrivateImplRef<Agent> PI; + +Agent::Agent(AgentImpl* impl) { PI::ctor(*this, impl); } +Agent::Agent(const Agent& s) : qmf::Handle<AgentImpl>() { PI::copy(*this, s); } +Agent::~Agent() { PI::dtor(*this); } +Agent& Agent::operator=(const Agent& s) { return PI::assign(*this, s); } +string Agent::getName() const { return isValid() ? impl->getName() : ""; } +uint32_t Agent::getEpoch() const { return isValid() ? impl->getEpoch() : 0; } +string Agent::getVendor() const { return isValid() ? impl->getVendor() : ""; } +string Agent::getProduct() const { return isValid() ? impl->getProduct() : ""; } +string Agent::getInstance() const { return isValid() ? impl->getInstance() : ""; } +const Variant& Agent::getAttribute(const string& k) const { return impl->getAttribute(k); } +const Variant::Map& Agent::getAttributes() const { return impl->getAttributes(); } +ConsoleEvent Agent::querySchema(Duration t) { return impl->querySchema(t); } +uint32_t Agent::querySchemaAsync() { return impl->querySchemaAsync(); } +ConsoleEvent Agent::query(const Query& q, Duration t) { return impl->query(q, t); } +ConsoleEvent Agent::query(const string& q, Duration t) { return impl->query(q, t); } +uint32_t Agent::queryAsync(const Query& q) { return impl->queryAsync(q); } +uint32_t Agent::queryAsync(const string& q) { return impl->queryAsync(q); } +ConsoleEvent Agent::callMethod(const string& m, const Variant::Map& a, const DataAddr& d, Duration t) { return impl->callMethod(m, a, d, t); } +uint32_t Agent::callMethodAsync(const string& m, const Variant::Map& a, const DataAddr& d) { return impl->callMethodAsync(m, a, d); } +uint32_t Agent::getPackageCount() const { return impl->getPackageCount(); } +const string& Agent::getPackage(uint32_t i) const { return impl->getPackage(i); } +uint32_t Agent::getSchemaIdCount(const string& p) const { return impl->getSchemaIdCount(p); } +SchemaId Agent::getSchemaId(const string& p, uint32_t i) const { return impl->getSchemaId(p, i); } +Schema Agent::getSchema(const SchemaId& s, Duration t) { return impl->getSchema(s, t); } + + + +AgentImpl::AgentImpl(const std::string& n, uint32_t e, ConsoleSessionImpl& s) : + name(n), directSubject(n), epoch(e), session(s), touched(true), untouchedCount(0), capability(0), + sender(session.directSender), nextCorrelator(1), schemaCache(s.schemaCache) +{ +} + +void AgentImpl::setAttribute(const std::string& k, const qpid::types::Variant& v) +{ + attributes[k] = v; + if (k == "qmf.agent_capability") + try { + capability = v.asUint32(); + } catch (std::exception&) {} + if (k == "_direct_subject") + try { + directSubject = v.asString(); + sender = session.topicSender; + } catch (std::exception&) {} +} + +const Variant& AgentImpl::getAttribute(const string& k) const +{ + Variant::Map::const_iterator iter = attributes.find(k); + if (iter == attributes.end()) + throw KeyNotFound(k); + return iter->second; +} + + +ConsoleEvent AgentImpl::query(const Query& query, Duration timeout) +{ + boost::shared_ptr<SyncContext> context(new SyncContext()); + uint32_t correlator; + ConsoleEvent result; + + { + qpid::sys::Mutex::ScopedLock l(lock); + correlator = nextCorrelator++; + contextMap[correlator] = context; + } + try { + sendQuery(query, correlator); + { + uint64_t milliseconds = timeout.getMilliseconds(); + qpid::sys::Mutex::ScopedLock cl(context->lock); + if (!context->response.isValid() || !context->response.isFinal()) + context->cond.wait(context->lock, + qpid::sys::AbsTime(qpid::sys::now(), + qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC))); + if (context->response.isValid() && + ((context->response.getType() == CONSOLE_QUERY_RESPONSE && context->response.isFinal()) || + (context->response.getType() == CONSOLE_EXCEPTION))) + result = context->response; + else { + auto_ptr<ConsoleEventImpl> impl(new ConsoleEventImpl(CONSOLE_EXCEPTION)); + Data exception(new DataImpl()); + exception.setProperty("error_text", "Timed out waiting for the agent to respond"); + impl->addData(exception); + result = ConsoleEvent(impl.release()); + } + } + } catch (qpid::types::Exception&) { + } + + { + qpid::sys::Mutex::ScopedLock l(lock); + contextMap.erase(correlator); + } + + return result; +} + + +ConsoleEvent AgentImpl::query(const string& text, Duration timeout) +{ + return query(stringToQuery(text), timeout); +} + + +uint32_t AgentImpl::queryAsync(const Query& query) +{ + uint32_t correlator; + + { + qpid::sys::Mutex::ScopedLock l(lock); + correlator = nextCorrelator++; + } + + sendQuery(query, correlator); + return correlator; +} + + +uint32_t AgentImpl::queryAsync(const string& text) +{ + return queryAsync(stringToQuery(text)); +} + + +ConsoleEvent AgentImpl::callMethod(const string& method, const Variant::Map& args, const DataAddr& addr, Duration timeout) +{ + boost::shared_ptr<SyncContext> context(new SyncContext()); + uint32_t correlator; + ConsoleEvent result; + + { + qpid::sys::Mutex::ScopedLock l(lock); + correlator = nextCorrelator++; + contextMap[correlator] = context; + } + try { + sendMethod(method, args, addr, correlator); + { + uint64_t milliseconds = timeout.getMilliseconds(); + qpid::sys::Mutex::ScopedLock cl(context->lock); + if (!context->response.isValid()) + context->cond.wait(context->lock, + qpid::sys::AbsTime(qpid::sys::now(), + qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC))); + if (context->response.isValid()) + result = context->response; + else { + auto_ptr<ConsoleEventImpl> impl(new ConsoleEventImpl(CONSOLE_EXCEPTION)); + Data exception(new DataImpl()); + exception.setProperty("error_text", "Timed out waiting for the agent to respond"); + impl->addData(exception); + result = ConsoleEvent(impl.release()); + } + } + } catch (qpid::types::Exception&) { + } + + { + qpid::sys::Mutex::ScopedLock l(lock); + contextMap.erase(correlator); + } + + return result; +} + + +uint32_t AgentImpl::callMethodAsync(const string& method, const Variant::Map& args, const DataAddr& addr) +{ + uint32_t correlator; + + { + qpid::sys::Mutex::ScopedLock l(lock); + correlator = nextCorrelator++; + } + + sendMethod(method, args, addr, correlator); + return correlator; +} + + +uint32_t AgentImpl::getPackageCount() const +{ + qpid::sys::Mutex::ScopedLock l(lock); + + // + // Populate the package set. + // + for (set<SchemaId>::const_iterator iter = schemaIdSet.begin(); iter != schemaIdSet.end(); iter++) + packageSet.insert(iter->getPackageName()); + + return packageSet.size(); +} + + +const string& AgentImpl::getPackage(uint32_t idx) const +{ + qpid::sys::Mutex::ScopedLock l(lock); + uint32_t count(0); + for (set<string>::const_iterator iter = packageSet.begin(); iter != packageSet.end(); iter++) { + if (idx == count) + return *iter; + count++; + } + throw IndexOutOfRange(); +} + + +uint32_t AgentImpl::getSchemaIdCount(const string& pname) const +{ + qpid::sys::Mutex::ScopedLock l(lock); + uint32_t count(0); + for (set<SchemaId>::const_iterator iter = schemaIdSet.begin(); iter != schemaIdSet.end(); iter++) + if (iter->getPackageName() == pname) + count++; + return count; +} + + +SchemaId AgentImpl::getSchemaId(const string& pname, uint32_t idx) const +{ + qpid::sys::Mutex::ScopedLock l(lock); + uint32_t count(0); + for (set<SchemaId>::const_iterator iter = schemaIdSet.begin(); iter != schemaIdSet.end(); iter++) { + if (iter->getPackageName() == pname) { + if (idx == count) + return *iter; + count++; + } + } + throw IndexOutOfRange(); +} + + +Schema AgentImpl::getSchema(const SchemaId& id, Duration timeout) +{ + if (!schemaCache->haveSchema(id)) + // + // The desired schema is not in the cache. We need to asynchronously query the remote + // agent for the information. The call to schemaCache->getSchema will block waiting for + // the response to be received. + // + sendSchemaRequest(id); + + return schemaCache->getSchema(id, timeout); +} + + +void AgentImpl::handleException(const Variant::Map& content, const Message& msg) +{ + const string& cid(msg.getCorrelationId()); + Variant::Map::const_iterator aIter; + uint32_t correlator; + boost::shared_ptr<SyncContext> context; + + try { correlator = boost::lexical_cast<uint32_t>(cid); } + catch(const boost::bad_lexical_cast&) { correlator = 0; } + + { + qpid::sys::Mutex::ScopedLock l(lock); + map<uint32_t, boost::shared_ptr<SyncContext> >::iterator iter = contextMap.find(correlator); + if (iter != contextMap.end()) + context = iter->second; + } + + if (context.get() != 0) { + // + // This exception is associated with a synchronous request. + // + qpid::sys::Mutex::ScopedLock cl(context->lock); + context->response = ConsoleEvent(new ConsoleEventImpl(CONSOLE_EXCEPTION)); + ConsoleEventImplAccess::get(context->response).addData(new DataImpl(content, this)); + ConsoleEventImplAccess::get(context->response).setAgent(this); + context->cond.notify(); + } else { + // + // This exception is associated with an asynchronous request. + // + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_EXCEPTION)); + eventImpl->setCorrelator(correlator); + eventImpl->setAgent(this); + eventImpl->addData(new DataImpl(content, this)); + session.enqueueEvent(eventImpl.release()); + } +} + + +void AgentImpl::handleMethodResponse(const Variant::Map& response, const Message& msg) +{ + const string& cid(msg.getCorrelationId()); + Variant::Map::const_iterator aIter; + Variant::Map argMap; + uint32_t correlator; + boost::shared_ptr<SyncContext> context; + + QPID_LOG(trace, "RCVD MethodResponse cid=" << cid << " map=" << response); + + aIter = response.find("_arguments"); + if (aIter != response.end()) + argMap = aIter->second.asMap(); + + try { correlator = boost::lexical_cast<uint32_t>(cid); } + catch(const boost::bad_lexical_cast&) { correlator = 0; } + + { + qpid::sys::Mutex::ScopedLock l(lock); + map<uint32_t, boost::shared_ptr<SyncContext> >::iterator iter = contextMap.find(correlator); + if (iter != contextMap.end()) + context = iter->second; + } + + if (context.get() != 0) { + // + // This response is associated with a synchronous request. + // + qpid::sys::Mutex::ScopedLock cl(context->lock); + context->response = ConsoleEvent(new ConsoleEventImpl(CONSOLE_METHOD_RESPONSE)); + ConsoleEventImplAccess::get(context->response).setArguments(argMap); + ConsoleEventImplAccess::get(context->response).setAgent(this); + context->cond.notify(); + } else { + // + // This response is associated with an asynchronous request. + // + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_METHOD_RESPONSE)); + eventImpl->setCorrelator(correlator); + eventImpl->setAgent(this); + eventImpl->setArguments(argMap); + session.enqueueEvent(eventImpl.release()); + } +} + + +void AgentImpl::handleDataIndication(const Variant::List& list, const Message& msg) +{ + Variant::Map::const_iterator aIter; + const Variant::Map& props(msg.getProperties()); + boost::shared_ptr<SyncContext> context; + + aIter = props.find("qmf.content"); + if (aIter == props.end()) + return; + + string content_type(aIter->second.asString()); + if (content_type != "_event") + return; + + for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) { + const Variant::Map& eventMap(lIter->asMap()); + Data data(new DataImpl(eventMap, this)); + int severity(SEV_NOTICE); + uint64_t timestamp(0); + + aIter = eventMap.find("_severity"); + if (aIter != eventMap.end()) + severity = int(aIter->second.asInt8()); + + aIter = eventMap.find("_timestamp"); + if (aIter != eventMap.end()) + timestamp = aIter->second.asUint64(); + + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_EVENT)); + eventImpl->setAgent(this); + eventImpl->addData(data); + eventImpl->setSeverity(severity); + eventImpl->setTimestamp(timestamp); + if (data.hasSchema()) + learnSchemaId(data.getSchemaId()); + session.enqueueEvent(eventImpl.release()); + } +} + + +void AgentImpl::handleQueryResponse(const Variant::List& list, const Message& msg) +{ + const string& cid(msg.getCorrelationId()); + Variant::Map::const_iterator aIter; + const Variant::Map& props(msg.getProperties()); + uint32_t correlator; + bool final(false); + boost::shared_ptr<SyncContext> context; + + aIter = props.find("partial"); + if (aIter == props.end()) + final = true; + + aIter = props.find("qmf.content"); + if (aIter == props.end()) + return; + + string content_type(aIter->second.asString()); + if (content_type != "_schema" && content_type != "_schema_id" && content_type != "_data") + return; + + try { correlator = boost::lexical_cast<uint32_t>(cid); } + catch(const boost::bad_lexical_cast&) { correlator = 0; } + + { + qpid::sys::Mutex::ScopedLock l(lock); + map<uint32_t, boost::shared_ptr<SyncContext> >::iterator iter = contextMap.find(correlator); + if (iter != contextMap.end()) + context = iter->second; + } + + if (context.get() != 0) { + // + // This response is associated with a synchronous request. + // + qpid::sys::Mutex::ScopedLock cl(context->lock); + if (!context->response.isValid()) + context->response = ConsoleEvent(new ConsoleEventImpl(CONSOLE_QUERY_RESPONSE)); + + if (content_type == "_data") + for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) { + Data data(new DataImpl(lIter->asMap(), this)); + ConsoleEventImplAccess::get(context->response).addData(data); + if (data.hasSchema()) + learnSchemaId(data.getSchemaId()); + } + else if (content_type == "_schema_id") + for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) { + SchemaId schemaId(new SchemaIdImpl(lIter->asMap())); + ConsoleEventImplAccess::get(context->response).addSchemaId(schemaId); + learnSchemaId(schemaId); + } + else if (content_type == "_schema") + for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) { + Schema schema(new SchemaImpl(lIter->asMap())); + schemaCache->declareSchema(schema); + } + + if (final) { + ConsoleEventImplAccess::get(context->response).setFinal(); + ConsoleEventImplAccess::get(context->response).setAgent(this); + context->cond.notify(); + } + } else { + // + // This response is associated with an asynchronous request. + // + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_QUERY_RESPONSE)); + eventImpl->setCorrelator(correlator); + eventImpl->setAgent(this); + + if (content_type == "_data") + for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) { + Data data(new DataImpl(lIter->asMap(), this)); + eventImpl->addData(data); + if (data.hasSchema()) + learnSchemaId(data.getSchemaId()); + } + else if (content_type == "_schema_id") + for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) { + SchemaId schemaId(new SchemaIdImpl(lIter->asMap())); + eventImpl->addSchemaId(schemaId); + learnSchemaId(schemaId); + } + else if (content_type == "_schema") + for (Variant::List::const_iterator lIter = list.begin(); lIter != list.end(); lIter++) { + Schema schema(new SchemaImpl(lIter->asMap())); + schemaCache->declareSchema(schema); + } + + if (final) + eventImpl->setFinal(); + if (content_type != "_schema") + session.enqueueEvent(eventImpl.release()); + } +} + + +Query AgentImpl::stringToQuery(const std::string& text) +{ + qpid::messaging::AddressParser parser(text); + Variant::Map map; + Variant::Map::const_iterator iter; + string className; + string packageName; + + parser.parseMap(map); + + iter = map.find("class"); + if (iter != map.end()) + className = iter->second.asString(); + + iter = map.find("package"); + if (iter != map.end()) + packageName = iter->second.asString(); + + Query query(QUERY_OBJECT, className, packageName); + + iter = map.find("where"); + if (iter != map.end()) + query.setPredicate(iter->second.asList()); + + return query; +} + + +void AgentImpl::sendQuery(const Query& query, uint32_t correlator) +{ + Message msg; + Variant::Map map; + Variant::Map& headers(msg.getProperties()); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_REQUEST; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + + msg.setReplyTo(session.replyAddress); + msg.setCorrelationId(boost::lexical_cast<string>(correlator)); + msg.setSubject(directSubject); + string userId(session.connection.getAuthenticatedUsername()); + if (!userId.empty()) + msg.setUserId(userId); + encode(QueryImplAccess::get(query).asMap(), msg); + if (sender.isValid()) { + sender.send(msg); + QPID_LOG(trace, "SENT QueryRequest to=" << sender.getName() << "/" << directSubject << " cid=" << correlator); + } +} + + +void AgentImpl::sendMethod(const string& method, const Variant::Map& args, const DataAddr& addr, uint32_t correlator) +{ + Message msg; + Variant::Map map; + Variant::Map& headers(msg.getProperties()); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_METHOD_REQUEST; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + + map["_method_name"] = method; + map["_object_id"] = addr.asMap(); + map["_arguments"] = args; + + msg.setReplyTo(session.replyAddress); + msg.setCorrelationId(boost::lexical_cast<string>(correlator)); + msg.setSubject(directSubject); + string userId(session.connection.getAuthenticatedUsername()); + if (!userId.empty()) + msg.setUserId(userId); + encode(map, msg); + if (sender.isValid()) { + sender.send(msg); + QPID_LOG(trace, "SENT MethodRequest method=" << method << " to=" << sender.getName() << "/" << directSubject << " content=" << map << " cid=" << correlator); + } +} + +void AgentImpl::sendSchemaRequest(const SchemaId& id) +{ + uint32_t correlator; + + { + qpid::sys::Mutex::ScopedLock l(lock); + correlator = nextCorrelator++; + } + + if (capability >= AGENT_CAPABILITY_V2_SCHEMA) { + Query query(QUERY_SCHEMA, id); + sendQuery(query, correlator); + return; + } + +#define RAW_BUFFER_SIZE 1024 + char rawBuffer[RAW_BUFFER_SIZE]; + qpid::management::Buffer buffer(rawBuffer, RAW_BUFFER_SIZE); + + buffer.putOctet('A'); + buffer.putOctet('M'); + buffer.putOctet('2'); + buffer.putOctet('S'); + buffer.putLong(correlator); + buffer.putShortString(id.getPackageName()); + buffer.putShortString(id.getName()); + buffer.putBin128(id.getHash().data()); + + string content(rawBuffer, buffer.getPosition()); + + Message msg; + msg.setReplyTo(session.replyAddress); + msg.setContent(content); + msg.setSubject(directSubject); + string userId(session.connection.getAuthenticatedUsername()); + if (!userId.empty()) + msg.setUserId(userId); + if (sender.isValid()) { + sender.send(msg); + QPID_LOG(trace, "SENT V1SchemaRequest to=" << sender.getName() << "/" << directSubject); + } +} + + +void AgentImpl::learnSchemaId(const SchemaId& id) +{ + schemaCache->declareSchemaId(id); + schemaIdSet.insert(id); +} + + +AgentImpl& AgentImplAccess::get(Agent& item) +{ + return *item.impl; +} + + +const AgentImpl& AgentImplAccess::get(const Agent& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/AgentEvent.cpp b/qpid/cpp/src/qmf/AgentEvent.cpp new file mode 100644 index 0000000000..2dc24ecac1 --- /dev/null +++ b/qpid/cpp/src/qmf/AgentEvent.cpp @@ -0,0 +1,85 @@ +/* + * + * 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 "qmf/AgentEventImpl.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/SchemaImpl.h" + +using namespace std; +using namespace qmf; +using qpid::types::Variant; + +typedef PrivateImplRef<AgentEvent> PI; + +AgentEvent::AgentEvent(AgentEventImpl* impl) { PI::ctor(*this, impl); } +AgentEvent::AgentEvent(const AgentEvent& s) : qmf::Handle<AgentEventImpl>() { PI::copy(*this, s); } +AgentEvent::~AgentEvent() { PI::dtor(*this); } +AgentEvent& AgentEvent::operator=(const AgentEvent& s) { return PI::assign(*this, s); } + +AgentEventCode AgentEvent::getType() const { return impl->getType(); } +const string& AgentEvent::getUserId() const { return impl->getUserId(); } +Query AgentEvent::getQuery() const { return impl->getQuery(); } +bool AgentEvent::hasDataAddr() const { return impl->hasDataAddr(); } +DataAddr AgentEvent::getDataAddr() const { return impl->getDataAddr(); } +const string& AgentEvent::getMethodName() const { return impl->getMethodName(); } +qpid::types::Variant::Map& AgentEvent::getArguments() { return impl->getArguments(); } +qpid::types::Variant::Map& AgentEvent::getArgumentSubtypes() { return impl->getArgumentSubtypes(); } +void AgentEvent::addReturnArgument(const std::string& k, const qpid::types::Variant& v, const std::string& s) { impl->addReturnArgument(k, v, s); } + +uint32_t AgentEventImpl::enqueueData(const Data& data) +{ + qpid::sys::Mutex::ScopedLock l(lock); + dataQueue.push(data); + return dataQueue.size(); +} + + +Data AgentEventImpl::dequeueData() +{ + qpid::sys::Mutex::ScopedLock l(lock); + if (dataQueue.empty()) + return Data(); + Data data(dataQueue.front()); + dataQueue.pop(); + return data; +} + + +void AgentEventImpl::addReturnArgument(const string& key, const Variant& val, const string& subtype) +{ + if (schema.isValid() && !SchemaImplAccess::get(schema).isValidMethodOutArg(methodName, key, val)) + throw QmfException("Output argument is unknown or the type is incompatible"); + outArguments[key] = val; + if (!subtype.empty()) + outArgumentSubtypes[key] = subtype; +} + + +AgentEventImpl& AgentEventImplAccess::get(AgentEvent& item) +{ + return *item.impl; +} + + +const AgentEventImpl& AgentEventImplAccess::get(const AgentEvent& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/AgentEventImpl.h b/qpid/cpp/src/qmf/AgentEventImpl.h new file mode 100644 index 0000000000..1ecb41775a --- /dev/null +++ b/qpid/cpp/src/qmf/AgentEventImpl.h @@ -0,0 +1,96 @@ +#ifndef _QMF_AGENT_EVENT_IMPL_H_ +#define _QMF_AGENT_EVENT_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/IntegerTypes.h" +#include "qpid/messaging/Address.h" +#include "qmf/AgentEvent.h" +#include "qmf/Query.h" +#include "qmf/DataAddr.h" +#include "qmf/Data.h" +#include "qmf/Schema.h" +#include <queue> + +namespace qmf { + class AgentEventImpl : public virtual qpid::RefCounted { + public: + // + // Impl-only methods + // + AgentEventImpl(AgentEventCode e) : eventType(e) {} + void setUserId(const std::string& u) { userId = u; } + void setQuery(const Query& q) { query = q; } + void setDataAddr(const DataAddr& d) { dataAddr = d; } + void setMethodName(const std::string& m) { methodName = m; } + void setArguments(const qpid::types::Variant::Map& a) { arguments = a; } + void setArgumentSubtypes(const qpid::types::Variant::Map& a) { argumentSubtypes = a; } + void setReplyTo(const qpid::messaging::Address& r) { replyTo = r; } + void setSchema(const Schema& s) { schema = s; } + const qpid::messaging::Address& getReplyTo() { return replyTo; } + void setCorrelationId(const std::string& c) { correlationId = c; } + const std::string& getCorrelationId() { return correlationId; } + const qpid::types::Variant::Map& getReturnArguments() const { return outArguments; } + const qpid::types::Variant::Map& getReturnArgumentSubtypes() const { return outArgumentSubtypes; } + uint32_t enqueueData(const Data&); + Data dequeueData(); + + // + // Methods from API handle + // + AgentEventCode getType() const { return eventType; } + const std::string& getUserId() const { return userId; } + Query getQuery() const { return query; } + bool hasDataAddr() const { return dataAddr.isValid(); } + DataAddr getDataAddr() const { return dataAddr; } + const std::string& getMethodName() const { return methodName; } + qpid::types::Variant::Map& getArguments() { return arguments; } + qpid::types::Variant::Map& getArgumentSubtypes() { return argumentSubtypes; } + void addReturnArgument(const std::string&, const qpid::types::Variant&, const std::string&); + + private: + const AgentEventCode eventType; + std::string userId; + qpid::messaging::Address replyTo; + std::string correlationId; + Query query; + DataAddr dataAddr; + Schema schema; + std::string methodName; + qpid::types::Variant::Map arguments; + qpid::types::Variant::Map argumentSubtypes; + qpid::types::Variant::Map outArguments; + qpid::types::Variant::Map outArgumentSubtypes; + + qpid::sys::Mutex lock; + std::queue<Data> dataQueue; + }; + + struct AgentEventImplAccess + { + static AgentEventImpl& get(AgentEvent&); + static const AgentEventImpl& get(const AgentEvent&); + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/AgentImpl.h b/qpid/cpp/src/qmf/AgentImpl.h new file mode 100644 index 0000000000..7fa4f4373a --- /dev/null +++ b/qpid/cpp/src/qmf/AgentImpl.h @@ -0,0 +1,123 @@ +#ifndef _QMF_AGENT_IMPL_H_ +#define _QMF_AGENT_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/Agent.h" +#include "qmf/ConsoleEventImpl.h" +#include "qmf/ConsoleSessionImpl.h" +#include "qmf/QueryImpl.h" +#include "qmf/SchemaCache.h" +#include "qpid/messaging/Session.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Sender.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Condition.h" +#include <boost/shared_ptr.hpp> +#include <map> +#include <set> + +namespace qmf { + class AgentImpl : public virtual qpid::RefCounted { + public: + // + // Impl-only methods + // + AgentImpl(const std::string& n, uint32_t e, ConsoleSessionImpl& s); + void setAttribute(const std::string& k, const qpid::types::Variant& v); + void setAttribute(const std::string& k, const std::string& v) { attributes[k] = v; } + void touch() { touched = true; } + uint32_t age() { untouchedCount = touched ? 0 : untouchedCount + 1; touched = false; return untouchedCount; } + uint32_t getCapability() const { return capability; } + void handleException(const qpid::types::Variant::Map&, const qpid::messaging::Message&); + void handleMethodResponse(const qpid::types::Variant::Map&, const qpid::messaging::Message&); + void handleDataIndication(const qpid::types::Variant::List&, const qpid::messaging::Message&); + void handleQueryResponse(const qpid::types::Variant::List&, const qpid::messaging::Message&); + + // + // Methods from API handle + // + const std::string& getName() const { return name; } + uint32_t getEpoch() const { return epoch; } + void setEpoch(uint32_t e) { epoch = e; } + std::string getVendor() const { return getAttribute("_vendor").asString(); } + std::string getProduct() const { return getAttribute("_product").asString(); } + std::string getInstance() const { return getAttribute("_instance").asString(); } + const qpid::types::Variant& getAttribute(const std::string& k) const; + const qpid::types::Variant::Map& getAttributes() const { return attributes; } + + ConsoleEvent querySchema(qpid::messaging::Duration t) { return query(Query(QUERY_SCHEMA_ID), t); } + uint32_t querySchemaAsync() { return queryAsync(Query(QUERY_SCHEMA_ID)); } + + ConsoleEvent query(const Query& q, qpid::messaging::Duration t); + ConsoleEvent query(const std::string& q, qpid::messaging::Duration t); + uint32_t queryAsync(const Query& q); + uint32_t queryAsync(const std::string& q); + + ConsoleEvent callMethod(const std::string& m, const qpid::types::Variant::Map& a, const DataAddr&, qpid::messaging::Duration t); + uint32_t callMethodAsync(const std::string& m, const qpid::types::Variant::Map& a, const DataAddr&); + + uint32_t getPackageCount() const; + const std::string& getPackage(uint32_t i) const; + uint32_t getSchemaIdCount(const std::string& p) const; + SchemaId getSchemaId(const std::string& p, uint32_t i) const; + Schema getSchema(const SchemaId& s, qpid::messaging::Duration t); + + private: + struct SyncContext { + qpid::sys::Mutex lock; + qpid::sys::Condition cond; + ConsoleEvent response; + }; + + mutable qpid::sys::Mutex lock; + std::string name; + std::string directSubject; + uint32_t epoch; + ConsoleSessionImpl& session; + bool touched; + uint32_t untouchedCount; + uint32_t capability; + qpid::messaging::Sender sender; + qpid::types::Variant::Map attributes; + uint32_t nextCorrelator; + std::map<uint32_t, boost::shared_ptr<SyncContext> > contextMap; + boost::shared_ptr<SchemaCache> schemaCache; + mutable std::set<std::string> packageSet; + std::set<SchemaId, SchemaIdCompare> schemaIdSet; + + Query stringToQuery(const std::string&); + void sendQuery(const Query&, uint32_t); + void sendSchemaIdQuery(uint32_t); + void sendMethod(const std::string&, const qpid::types::Variant::Map&, const DataAddr&, uint32_t); + void sendSchemaRequest(const SchemaId&); + void learnSchemaId(const SchemaId&); + }; + + struct AgentImplAccess + { + static AgentImpl& get(Agent&); + static const AgentImpl& get(const Agent&); + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/AgentSession.cpp b/qpid/cpp/src/qmf/AgentSession.cpp new file mode 100644 index 0000000000..71d369325f --- /dev/null +++ b/qpid/cpp/src/qmf/AgentSession.cpp @@ -0,0 +1,1072 @@ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/exceptions.h" +#include "qmf/AgentSession.h" +#include "qmf/AgentEventImpl.h" +#include "qmf/SchemaIdImpl.h" +#include "qmf/SchemaImpl.h" +#include "qmf/DataAddrImpl.h" +#include "qmf/DataImpl.h" +#include "qmf/QueryImpl.h" +#include "qmf/agentCapability.h" +#include "qmf/constants.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Condition.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/log/Statement.h" +#include "qpid/messaging/Connection.h" +#include "qpid/messaging/Session.h" +#include "qpid/messaging/Receiver.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/AddressParser.h" +#include "qpid/management/Buffer.h" +#include <queue> +#include <map> +#include <set> +#include <iostream> +#include <memory> + +using namespace std; +using namespace qpid::messaging; +using namespace qmf; +using qpid::types::Variant; + +namespace qmf { + class AgentSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable { + public: + ~AgentSessionImpl(); + + // + // Methods from API handle + // + AgentSessionImpl(Connection& c, const string& o); + void setDomain(const string& d) { checkOpen(); domain = d; } + void setVendor(const string& v) { checkOpen(); attributes["_vendor"] = v; } + void setProduct(const string& p) { checkOpen(); attributes["_product"] = p; } + void setInstance(const string& i) { checkOpen(); attributes["_instance"] = i; } + void setAttribute(const string& k, const qpid::types::Variant& v) { checkOpen(); attributes[k] = v; } + const string& getName() const { return agentName; } + void open(); + void close(); + bool nextEvent(AgentEvent& e, Duration t); + int pendingEvents() const; + + void registerSchema(Schema& s); + DataAddr addData(Data& d, const string& n, bool persist); + void delData(const DataAddr&); + + void authAccept(AgentEvent& e); + void authReject(AgentEvent& e, const string& m); + void raiseException(AgentEvent& e, const string& s); + void raiseException(AgentEvent& e, const Data& d); + void response(AgentEvent& e, const Data& d); + void complete(AgentEvent& e); + void methodSuccess(AgentEvent& e); + void raiseEvent(const Data& d); + void raiseEvent(const Data& d, int s); + + private: + typedef map<DataAddr, Data, DataAddrCompare> DataIndex; + typedef map<SchemaId, Schema, SchemaIdCompare> SchemaMap; + + mutable qpid::sys::Mutex lock; + qpid::sys::Condition cond; + Connection connection; + Session session; + Sender directSender; + Sender topicSender; + string domain; + Variant::Map attributes; + Variant::Map options; + string agentName; + bool opened; + queue<AgentEvent> eventQueue; + qpid::sys::Thread* thread; + bool threadCanceled; + uint32_t bootSequence; + uint32_t interval; + uint64_t lastHeartbeat; + uint64_t lastVisit; + bool forceHeartbeat; + bool externalStorage; + bool autoAllowQueries; + bool autoAllowMethods; + uint32_t maxSubscriptions; + uint32_t minSubInterval; + uint32_t subLifetime; + bool publicEvents; + bool listenOnDirect; + bool strictSecurity; + uint64_t schemaUpdateTime; + string directBase; + string topicBase; + + SchemaMap schemata; + DataIndex globalIndex; + map<SchemaId, DataIndex, SchemaIdCompareNoHash> schemaIndex; + + void checkOpen(); + void setAgentName(); + void enqueueEvent(const AgentEvent&); + void handleLocateRequest(const Variant::List& content, const Message& msg); + void handleMethodRequest(const Variant::Map& content, const Message& msg); + void handleQueryRequest(const Variant::Map& content, const Message& msg); + void handleSchemaRequest(AgentEvent&); + void handleV1SchemaRequest(qpid::management::Buffer&, uint32_t, const Message&); + void dispatch(Message); + void sendHeartbeat(); + void send(Message, const Address&); + void flushResponses(AgentEvent&, bool); + void periodicProcessing(uint64_t); + void run(); + }; +} + +typedef qmf::PrivateImplRef<AgentSession> PI; + +AgentSession::AgentSession(AgentSessionImpl* impl) { PI::ctor(*this, impl); } +AgentSession::AgentSession(const AgentSession& s) : qmf::Handle<AgentSessionImpl>() { PI::copy(*this, s); } +AgentSession::~AgentSession() { PI::dtor(*this); } +AgentSession& AgentSession::operator=(const AgentSession& s) { return PI::assign(*this, s); } + +AgentSession::AgentSession(Connection& c, const string& o) { PI::ctor(*this, new AgentSessionImpl(c, o)); } +void AgentSession::setDomain(const string& d) { impl->setDomain(d); } +void AgentSession::setVendor(const string& v) { impl->setVendor(v); } +void AgentSession::setProduct(const string& p) { impl->setProduct(p); } +void AgentSession::setInstance(const string& i) { impl->setInstance(i); } +void AgentSession::setAttribute(const string& k, const qpid::types::Variant& v) { impl->setAttribute(k, v); } +const string& AgentSession::getName() const { return impl->getName(); } +void AgentSession::open() { impl->open(); } +void AgentSession::close() { impl->close(); } +bool AgentSession::nextEvent(AgentEvent& e, Duration t) { return impl->nextEvent(e, t); } +int AgentSession::pendingEvents() const { return impl->pendingEvents(); } +void AgentSession::registerSchema(Schema& s) { impl->registerSchema(s); } +DataAddr AgentSession::addData(Data& d, const string& n, bool p) { return impl->addData(d, n, p); } +void AgentSession::delData(const DataAddr& a) { impl->delData(a); } +void AgentSession::authAccept(AgentEvent& e) { impl->authAccept(e); } +void AgentSession::authReject(AgentEvent& e, const string& m) { impl->authReject(e, m); } +void AgentSession::raiseException(AgentEvent& e, const string& s) { impl->raiseException(e, s); } +void AgentSession::raiseException(AgentEvent& e, const Data& d) { impl->raiseException(e, d); } +void AgentSession::response(AgentEvent& e, const Data& d) { impl->response(e, d); } +void AgentSession::complete(AgentEvent& e) { impl->complete(e); } +void AgentSession::methodSuccess(AgentEvent& e) { impl->methodSuccess(e); } +void AgentSession::raiseEvent(const Data& d) { impl->raiseEvent(d); } +void AgentSession::raiseEvent(const Data& d, int s) { impl->raiseEvent(d, s); } + +//======================================================================================== +// Impl Method Bodies +//======================================================================================== + +AgentSessionImpl::AgentSessionImpl(Connection& c, const string& options) : + connection(c), domain("default"), opened(false), thread(0), threadCanceled(false), + bootSequence(1), interval(60), lastHeartbeat(0), lastVisit(0), forceHeartbeat(false), + externalStorage(false), autoAllowQueries(true), autoAllowMethods(true), + maxSubscriptions(64), minSubInterval(3000), subLifetime(300), publicEvents(true), + listenOnDirect(true), strictSecurity(false), + schemaUpdateTime(uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()))) +{ + // + // Set Agent Capability Level + // + attributes["qmf.agent_capability"] = AGENT_CAPABILITY_0_8; + + if (!options.empty()) { + qpid::messaging::AddressParser parser(options); + Variant::Map optMap; + Variant::Map::const_iterator iter; + + parser.parseMap(optMap); + + iter = optMap.find("domain"); + if (iter != optMap.end()) + domain = iter->second.asString(); + + iter = optMap.find("interval"); + if (iter != optMap.end()) { + interval = iter->second.asUint32(); + if (interval < 1) + interval = 1; + } + + iter = optMap.find("external"); + if (iter != optMap.end()) + externalStorage = iter->second.asBool(); + + iter = optMap.find("allow-queries"); + if (iter != optMap.end()) + autoAllowQueries = iter->second.asBool(); + + iter = optMap.find("allow-methods"); + if (iter != optMap.end()) + autoAllowMethods = iter->second.asBool(); + + iter = optMap.find("max-subscriptions"); + if (iter != optMap.end()) + maxSubscriptions = iter->second.asUint32(); + + iter = optMap.find("min-sub-interval"); + if (iter != optMap.end()) + minSubInterval = iter->second.asUint32(); + + iter = optMap.find("sub-lifetime"); + if (iter != optMap.end()) + subLifetime = iter->second.asUint32(); + + iter = optMap.find("public-events"); + if (iter != optMap.end()) + publicEvents = iter->second.asBool(); + + iter = optMap.find("listen-on-direct"); + if (iter != optMap.end()) + listenOnDirect = iter->second.asBool(); + + iter = optMap.find("strict-security"); + if (iter != optMap.end()) + strictSecurity = iter->second.asBool(); + } +} + + +AgentSessionImpl::~AgentSessionImpl() +{ + if (opened) + close(); +} + + +void AgentSessionImpl::open() +{ + if (opened) + throw QmfException("The session is already open"); + + const string addrArgs(";{create:never,node:{type:topic}}"); + const string routableAddr("direct-agent.route." + qpid::types::Uuid(true).str()); + attributes["_direct_subject"] = routableAddr; + + // Establish messaging addresses + setAgentName(); + directBase = "qmf." + domain + ".direct"; + topicBase = "qmf." + domain + ".topic"; + + // Create AMQP session, receivers, and senders + session = connection.createSession(); + Receiver directRx; + Receiver routableDirectRx = session.createReceiver(topicBase + "/" + routableAddr + addrArgs); + Receiver topicRx = session.createReceiver(topicBase + "/console.#" + addrArgs); + + if (listenOnDirect && !strictSecurity) { + directRx = session.createReceiver(directBase + "/" + agentName + addrArgs); + directRx.setCapacity(64); + } + + routableDirectRx.setCapacity(64); + topicRx.setCapacity(64); + + if (!strictSecurity) + directSender = session.createSender(directBase + addrArgs); + topicSender = session.createSender(topicBase + addrArgs); + + // Start the receiver thread + threadCanceled = false; + opened = true; + thread = new qpid::sys::Thread(*this); + + // Send an initial agent heartbeat message + sendHeartbeat(); +} + + +void AgentSessionImpl::close() +{ + if (!opened) + return; + + // Stop and join the receiver thread + threadCanceled = true; + thread->join(); + delete thread; + + // Close the AMQP session + session.close(); + opened = false; +} + + +bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout) +{ + uint64_t milliseconds = timeout.getMilliseconds(); + qpid::sys::Mutex::ScopedLock l(lock); + + if (eventQueue.empty() && milliseconds > 0) + cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(), + qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC))); + + if (!eventQueue.empty()) { + event = eventQueue.front(); + eventQueue.pop(); + return true; + } + + return false; +} + + +int AgentSessionImpl::pendingEvents() const +{ + qpid::sys::Mutex::ScopedLock l(lock); + return eventQueue.size(); +} + + +void AgentSessionImpl::registerSchema(Schema& schema) +{ + if (!schema.isFinalized()) + schema.finalize(); + const SchemaId& schemaId(schema.getSchemaId()); + + qpid::sys::Mutex::ScopedLock l(lock); + schemata[schemaId] = schema; + schemaIndex[schemaId] = DataIndex(); + + // + // Get the news out at the next periodic interval that there is new schema information. + // + schemaUpdateTime = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now())); + forceHeartbeat = true; +} + + +DataAddr AgentSessionImpl::addData(Data& data, const string& name, bool persistent) +{ + if (externalStorage) + throw QmfException("addData() must not be called when the 'external' option is enabled."); + + string dataName; + if (name.empty()) + dataName = qpid::types::Uuid(true).str(); + else + dataName = name; + + DataAddr addr(dataName, agentName, persistent ? 0 : bootSequence); + data.setAddr(addr); + + { + qpid::sys::Mutex::ScopedLock l(lock); + DataIndex::const_iterator iter = globalIndex.find(addr); + if (iter != globalIndex.end()) + throw QmfException("Duplicate Data Address"); + + globalIndex[addr] = data; + if (data.hasSchema()) + schemaIndex[data.getSchemaId()][addr] = data; + } + + // + // TODO: Survey active subscriptions to see if they need to hear about this new data. + // + + return addr; +} + + +void AgentSessionImpl::delData(const DataAddr& addr) +{ + { + qpid::sys::Mutex::ScopedLock l(lock); + DataIndex::iterator iter = globalIndex.find(addr); + if (iter == globalIndex.end()) + return; + if (iter->second.hasSchema()) { + const SchemaId& schemaId(iter->second.getSchemaId()); + schemaIndex[schemaId].erase(addr); + } + globalIndex.erase(iter); + } + + // + // TODO: Survey active subscriptions to see if they need to hear about this deleted data. + // +} + + +void AgentSessionImpl::authAccept(AgentEvent& authEvent) +{ + auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_QUERY)); + eventImpl->setQuery(authEvent.getQuery()); + eventImpl->setUserId(authEvent.getUserId()); + eventImpl->setReplyTo(AgentEventImplAccess::get(authEvent).getReplyTo()); + eventImpl->setCorrelationId(AgentEventImplAccess::get(authEvent).getCorrelationId()); + AgentEvent event(eventImpl.release()); + + if (externalStorage) { + enqueueEvent(event); + return; + } + + const Query& query(authEvent.getQuery()); + if (query.getDataAddr().isValid()) { + { + qpid::sys::Mutex::ScopedLock l(lock); + DataIndex::const_iterator iter = globalIndex.find(query.getDataAddr()); + if (iter != globalIndex.end()) + response(event, iter->second); + } + complete(event); + return; + } + + if (query.getSchemaId().isValid()) { + { + qpid::sys::Mutex::ScopedLock l(lock); + map<SchemaId, DataIndex>::const_iterator iter = schemaIndex.find(query.getSchemaId()); + if (iter != schemaIndex.end()) + for (DataIndex::const_iterator dIter = iter->second.begin(); dIter != iter->second.end(); dIter++) + if (query.matchesPredicate(dIter->second.getProperties())) + response(event, dIter->second); + } + complete(event); + return; + } + + raiseException(event, "Query is Invalid"); +} + + +void AgentSessionImpl::authReject(AgentEvent& event, const string& error) +{ + raiseException(event, "Action Forbidden - " + error); +} + + +void AgentSessionImpl::raiseException(AgentEvent& event, const string& error) +{ + Data exception(new DataImpl()); + exception.setProperty("error_text", error); + raiseException(event, exception); +} + + +void AgentSessionImpl::raiseException(AgentEvent& event, const Data& data) +{ + Message msg; + Variant::Map map; + Variant::Map& headers(msg.getProperties()); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_EXCEPTION; + headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_DATA; + headers[protocol::HEADER_KEY_AGENT] = agentName; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + + AgentEventImpl& eventImpl(AgentEventImplAccess::get(event)); + const DataImpl& dataImpl(DataImplAccess::get(data)); + + msg.setCorrelationId(eventImpl.getCorrelationId()); + encode(dataImpl.asMap(), msg); + send(msg, eventImpl.getReplyTo()); + + QPID_LOG(trace, "SENT Exception to=" << eventImpl.getReplyTo()); +} + + +void AgentSessionImpl::response(AgentEvent& event, const Data& data) +{ + AgentEventImpl& impl(AgentEventImplAccess::get(event)); + uint32_t count = impl.enqueueData(data); + if (count >= 8) + flushResponses(event, false); +} + + +void AgentSessionImpl::complete(AgentEvent& event) +{ + flushResponses(event, true); +} + + +void AgentSessionImpl::methodSuccess(AgentEvent& event) +{ + Message msg; + Variant::Map map; + Variant::Map& headers(msg.getProperties()); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_METHOD_RESPONSE; + headers[protocol::HEADER_KEY_AGENT] = agentName; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + + AgentEventImpl& eventImpl(AgentEventImplAccess::get(event)); + + const Variant::Map& outArgs(eventImpl.getReturnArguments()); + const Variant::Map& outSubtypes(eventImpl.getReturnArgumentSubtypes()); + + map["_arguments"] = outArgs; + if (!outSubtypes.empty()) + map["_subtypes"] = outSubtypes; + + msg.setCorrelationId(eventImpl.getCorrelationId()); + encode(map, msg); + send(msg, eventImpl.getReplyTo()); + + QPID_LOG(trace, "SENT MethodResponse to=" << eventImpl.getReplyTo()); +} + + +void AgentSessionImpl::raiseEvent(const Data& data) +{ + int severity(SEV_NOTICE); + if (data.hasSchema()) { + const Schema& schema(DataImplAccess::get(data).getSchema()); + if (schema.isValid()) + severity = schema.getDefaultSeverity(); + } + + raiseEvent(data, severity); +} + + +void AgentSessionImpl::raiseEvent(const Data& data, int severity) +{ + Message msg; + Variant::Map map; + Variant::Map& headers(msg.getProperties()); + string subject("agent.ind.event"); + + if (data.hasSchema()) { + const SchemaId& schemaId(data.getSchemaId()); + if (schemaId.getType() != SCHEMA_TYPE_EVENT) + throw QmfException("Cannot call raiseEvent on data that is not an Event"); + subject = subject + "." + schemaId.getPackageName() + "." + schemaId.getName(); + } + + if (severity < SEV_EMERG || severity > SEV_DEBUG) + throw QmfException("Invalid severity value"); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_DATA_INDICATION; + headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_EVENT; + headers[protocol::HEADER_KEY_AGENT] = agentName; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + msg.setSubject(subject); + + Variant::List list; + Variant::Map dataAsMap(DataImplAccess::get(data).asMap()); + dataAsMap["_severity"] = severity; + dataAsMap["_timestamp"] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now())); + list.push_back(dataAsMap); + encode(list, msg); + topicSender.send(msg); + + QPID_LOG(trace, "SENT EventIndication to=" << topicSender.getName() << "/" << subject); +} + + +void AgentSessionImpl::checkOpen() +{ + if (opened) + throw QmfException("Operation must be performed before calling open()"); +} + + +void AgentSessionImpl::enqueueEvent(const AgentEvent& event) +{ + qpid::sys::Mutex::ScopedLock l(lock); + bool notify = eventQueue.empty(); + eventQueue.push(event); + if (notify) + cond.notify(); +} + + +void AgentSessionImpl::setAgentName() +{ + Variant::Map::iterator iter; + string vendor; + string product; + string instance; + + iter = attributes.find("_vendor"); + if (iter == attributes.end()) + attributes["_vendor"] = vendor; + else + vendor = iter->second.asString(); + + iter = attributes.find("_product"); + if (iter == attributes.end()) + attributes["_product"] = product; + else + product = iter->second.asString(); + + iter = attributes.find("_instance"); + if (iter == attributes.end()) { + instance = qpid::types::Uuid(true).str(); + attributes["_instance"] = instance; + } else + instance = iter->second.asString(); + + agentName = vendor + ":" + product + ":" + instance; + attributes["_name"] = agentName; +} + + +void AgentSessionImpl::handleLocateRequest(const Variant::List& predicate, const Message& msg) +{ + QPID_LOG(trace, "RCVD AgentLocateRequest from=" << msg.getReplyTo()); + + if (!predicate.empty()) { + Query agentQuery(QUERY_OBJECT); + agentQuery.setPredicate(predicate); + if (!agentQuery.matchesPredicate(attributes)) { + QPID_LOG(trace, "AgentLocate predicate does not match this agent, ignoring"); + return; + } + } + + Message reply; + Variant::Map map; + Variant::Map& headers(reply.getProperties()); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE; + headers[protocol::HEADER_KEY_AGENT] = agentName; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + + map["_values"] = attributes; + map["_values"].asMap()[protocol::AGENT_ATTR_TIMESTAMP] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now())); + map["_values"].asMap()[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = interval; + map["_values"].asMap()[protocol::AGENT_ATTR_EPOCH] = bootSequence; + map["_values"].asMap()[protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP] = schemaUpdateTime; + + encode(map, reply); + send(reply, msg.getReplyTo()); + QPID_LOG(trace, "SENT AgentLocateResponse to=" << msg.getReplyTo()); +} + + +void AgentSessionImpl::handleMethodRequest(const Variant::Map& content, const Message& msg) +{ + QPID_LOG(trace, "RCVD MethodRequest map=" << content << " from=" << msg.getReplyTo() << " cid=" << msg.getCorrelationId()); + + // + // Construct an AgentEvent to be sent to the application. + // + auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_METHOD)); + eventImpl->setUserId(msg.getUserId()); + eventImpl->setReplyTo(msg.getReplyTo()); + eventImpl->setCorrelationId(msg.getCorrelationId()); + + Variant::Map::const_iterator iter; + + iter = content.find("_method_name"); + if (iter == content.end()) { + AgentEvent event(eventImpl.release()); + raiseException(event, "Malformed MethodRequest: missing _method_name field"); + return; + } + eventImpl->setMethodName(iter->second.asString()); + + iter = content.find("_arguments"); + if (iter != content.end()) + eventImpl->setArguments(iter->second.asMap()); + + iter = content.find("_subtypes"); + if (iter != content.end()) + eventImpl->setArgumentSubtypes(iter->second.asMap()); + + iter = content.find("_object_id"); + if (iter != content.end()) { + DataAddr addr(new DataAddrImpl(iter->second.asMap())); + eventImpl->setDataAddr(addr); + DataIndex::const_iterator iter(globalIndex.find(addr)); + if (iter == globalIndex.end()) { + AgentEvent event(eventImpl.release()); + raiseException(event, "No data object found with the specified address"); + return; + } + + const Schema& schema(DataImplAccess::get(iter->second).getSchema()); + if (schema.isValid()) { + eventImpl->setSchema(schema); + for (Variant::Map::const_iterator aIter = eventImpl->getArguments().begin(); + aIter != eventImpl->getArguments().end(); aIter++) { + const Schema& schema(DataImplAccess::get(iter->second).getSchema()); + if (!SchemaImplAccess::get(schema).isValidMethodInArg(eventImpl->getMethodName(), aIter->first, aIter->second)) { + AgentEvent event(eventImpl.release()); + raiseException(event, "Invalid argument: " + aIter->first); + return; + } + } + } + } + + enqueueEvent(AgentEvent(eventImpl.release())); +} + + +void AgentSessionImpl::handleQueryRequest(const Variant::Map& content, const Message& msg) +{ + QPID_LOG(trace, "RCVD QueryRequest query=" << content << " from=" << msg.getReplyTo() << " cid=" << msg.getCorrelationId()); + + // + // Construct an AgentEvent to be sent to the application or directly handled by the agent. + // + auto_ptr<QueryImpl> queryImpl(new QueryImpl(content)); + auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_AUTH_QUERY)); + eventImpl->setUserId(msg.getUserId()); + eventImpl->setReplyTo(msg.getReplyTo()); + eventImpl->setCorrelationId(msg.getCorrelationId()); + eventImpl->setQuery(queryImpl.release()); + AgentEvent ae(eventImpl.release()); + + if (ae.getQuery().getTarget() == QUERY_SCHEMA_ID || ae.getQuery().getTarget() == QUERY_SCHEMA) { + handleSchemaRequest(ae); + return; + } + + if (autoAllowQueries) + authAccept(ae); + else + enqueueEvent(ae); +} + + +void AgentSessionImpl::handleSchemaRequest(AgentEvent& event) +{ + SchemaMap::const_iterator iter; + string error; + const Query& query(event.getQuery()); + + Message msg; + Variant::List content; + Variant::Map map; + Variant::Map& headers(msg.getProperties()); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_RESPONSE; + headers[protocol::HEADER_KEY_AGENT] = agentName; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + + { + qpid::sys::Mutex::ScopedLock l(lock); + if (query.getTarget() == QUERY_SCHEMA_ID) { + headers[protocol::HEADER_KEY_CONTENT] = "_schema_id"; + for (iter = schemata.begin(); iter != schemata.end(); iter++) + content.push_back(SchemaIdImplAccess::get(iter->first).asMap()); + } else if (query.getSchemaId().isValid()) { + headers[protocol::HEADER_KEY_CONTENT] = "_schema"; + iter = schemata.find(query.getSchemaId()); + if (iter != schemata.end()) + content.push_back(SchemaImplAccess::get(iter->second).asMap()); + } else { + error = "Invalid Schema Query: Requests for SCHEMA must supply a valid schema ID."; + } + } + + if (!error.empty()) { + raiseException(event, error); + return; + } + + AgentEventImpl& eventImpl(AgentEventImplAccess::get(event)); + + msg.setCorrelationId(eventImpl.getCorrelationId()); + encode(content, msg); + send(msg, eventImpl.getReplyTo()); + + QPID_LOG(trace, "SENT QueryResponse(Schema) to=" << eventImpl.getReplyTo()); +} + + +void AgentSessionImpl::handleV1SchemaRequest(qpid::management::Buffer& buffer, uint32_t seq, const Message& msg) +{ + string packageName; + string className; + uint8_t hashBits[16]; + + buffer.getShortString(packageName); + buffer.getShortString(className); + buffer.getBin128(hashBits); + + QPID_LOG(trace, "RCVD QMFv1 SchemaRequest for " << packageName << ":" << className); + + qpid::types::Uuid hash(hashBits); + map<SchemaId, Schema>::const_iterator iter; + string replyContent; + + SchemaId dataId(SCHEMA_TYPE_DATA, packageName, className); + dataId.setHash(hash); + + { + qpid::sys::Mutex::ScopedLock l(lock); + iter = schemata.find(dataId); + if (iter != schemata.end()) + replyContent = SchemaImplAccess::get(iter->second).asV1Content(seq); + else { + SchemaId eventId(SCHEMA_TYPE_EVENT, packageName, className); + eventId.setHash(hash); + iter = schemata.find(dataId); + if (iter != schemata.end()) + replyContent = SchemaImplAccess::get(iter->second).asV1Content(seq); + else + return; + } + } + + Message reply; + Variant::Map& headers(reply.getProperties()); + + headers[protocol::HEADER_KEY_AGENT] = agentName; + reply.setContent(replyContent); + + send(reply, msg.getReplyTo()); + QPID_LOG(trace, "SENT QMFv1 SchemaResponse to=" << msg.getReplyTo()); +} + + +void AgentSessionImpl::dispatch(Message msg) +{ + const Variant::Map& properties(msg.getProperties()); + Variant::Map::const_iterator iter; + + // + // If strict-security is enabled, make sure that reply-to address complies with the + // strict-security addressing pattern (i.e. start with 'qmf.<domain>.topic/direct-console.'). + // + if (strictSecurity && msg.getReplyTo()) { + if (msg.getReplyTo().getName() != topicBase || msg.getReplyTo().getSubject().find("direct-console.") != 0) { + QPID_LOG(warning, "Reply-to violates strict-security policy: " << msg.getReplyTo().str()); + return; + } + } + + iter = properties.find(protocol::HEADER_KEY_APP_ID); + if (iter != properties.end() && iter->second.asString() == protocol::HEADER_APP_ID_QMF) { + // + // Dispatch a QMFv2 formatted message + // + iter = properties.find(protocol::HEADER_KEY_OPCODE); + if (iter == properties.end()) { + QPID_LOG(trace, "Message received with no 'qmf.opcode' header"); + return; + } + + const string& opcode = iter->second.asString(); + + if (msg.getContentType() == "amqp/list") { + Variant::List content; + decode(msg, content); + + if (opcode == protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST) handleLocateRequest(content, msg); + else { + QPID_LOG(trace, "Unexpected QMFv2 opcode with 'amqp/list' content: " << opcode); + } + + } else if (msg.getContentType() == "amqp/map") { + Variant::Map content; + decode(msg, content); + + if (opcode == protocol::HEADER_OPCODE_METHOD_REQUEST) handleMethodRequest(content, msg); + else if (opcode == protocol::HEADER_OPCODE_QUERY_REQUEST) handleQueryRequest(content, msg); + else { + QPID_LOG(trace, "Unexpected QMFv2 opcode with 'amqp/map' content: " << opcode); + } + } else { + QPID_LOG(trace, "Unexpected QMFv2 content type. Expected amqp/list or amqp/map"); + } + + } else { + // + // Dispatch a QMFv1 formatted message + // + const string& body(msg.getContent()); + if (body.size() < 8) + return; + qpid::management::Buffer buffer(const_cast<char*>(body.c_str()), body.size()); + + if (buffer.getOctet() != 'A') return; + if (buffer.getOctet() != 'M') return; + if (buffer.getOctet() != '2') return; + char v1Opcode(buffer.getOctet()); + uint32_t seq(buffer.getLong()); + + if (v1Opcode == 'S') handleV1SchemaRequest(buffer, seq, msg); + else { + QPID_LOG(trace, "Unknown or Unsupported QMFv1 opcode: " << v1Opcode); + } + } +} + + +void AgentSessionImpl::sendHeartbeat() +{ + Message msg; + Variant::Map map; + Variant::Map& headers(msg.getProperties()); + std::stringstream address; + + address << "agent.ind.heartbeat"; + + // append .<vendor>.<product> to address key if present. + Variant::Map::const_iterator v; + if ((v = attributes.find("_vendor")) != attributes.end() && !v->second.getString().empty()) { + address << "." << v->second.getString(); + if ((v = attributes.find("_product")) != attributes.end() && !v->second.getString().empty()) { + address << "." << v->second.getString(); + } + } + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_INDICATION; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION; + headers[protocol::HEADER_KEY_AGENT] = agentName; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + msg.setSubject(address.str()); + + map["_values"] = attributes; + map["_values"].asMap()[protocol::AGENT_ATTR_TIMESTAMP] = uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now())); + map["_values"].asMap()[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = interval; + map["_values"].asMap()[protocol::AGENT_ATTR_EPOCH] = bootSequence; + map["_values"].asMap()[protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP] = schemaUpdateTime; + + encode(map, msg); + topicSender.send(msg); + QPID_LOG(trace, "SENT AgentHeartbeat name=" << agentName); +} + + +void AgentSessionImpl::send(Message msg, const Address& to) +{ + Sender sender; + + if (strictSecurity && to.getName() != topicBase) { + QPID_LOG(warning, "Address violates strict-security policy: " << to); + return; + } + + if (to.getName() == directBase) { + msg.setSubject(to.getSubject()); + sender = directSender; + } else if (to.getName() == topicBase) { + msg.setSubject(to.getSubject()); + sender = topicSender; + } else + sender = session.createSender(to); + + sender.send(msg); +} + + +void AgentSessionImpl::flushResponses(AgentEvent& event, bool final) +{ + Message msg; + Variant::Map map; + Variant::Map& headers(msg.getProperties()); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_RESPONSE; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_QUERY_RESPONSE; + headers[protocol::HEADER_KEY_CONTENT] = protocol::HEADER_CONTENT_DATA; + headers[protocol::HEADER_KEY_AGENT] = agentName; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + if (!final) + headers[protocol::HEADER_KEY_PARTIAL] = Variant(); + + Variant::List body; + AgentEventImpl& eventImpl(AgentEventImplAccess::get(event)); + Data data(eventImpl.dequeueData()); + while (data.isValid()) { + DataImpl& dataImpl(DataImplAccess::get(data)); + body.push_back(dataImpl.asMap()); + data = eventImpl.dequeueData(); + } + + msg.setCorrelationId(eventImpl.getCorrelationId()); + encode(body, msg); + send(msg, eventImpl.getReplyTo()); + + QPID_LOG(trace, "SENT QueryResponse to=" << eventImpl.getReplyTo()); +} + + +void AgentSessionImpl::periodicProcessing(uint64_t seconds) +{ + // + // The granularity of this timer is seconds. Don't waste time looking for work if + // it's been less than a second since we last visited. + // + if (seconds == lastVisit) + return; + //uint64_t thisInterval(seconds - lastVisit); + lastVisit = seconds; + + // + // First time through, set lastHeartbeat to the current time. + // + if (lastHeartbeat == 0) + lastHeartbeat = seconds; + + // + // If the hearbeat interval has elapsed, send a heartbeat. + // + if (forceHeartbeat || (seconds - lastHeartbeat >= interval)) { + lastHeartbeat = seconds; + forceHeartbeat = false; + sendHeartbeat(); + } + + // + // TODO: process any active subscriptions on their intervals. + // +} + + +void AgentSessionImpl::run() +{ + QPID_LOG(debug, "AgentSession thread started for agent " << agentName); + + try { + while (!threadCanceled) { + periodicProcessing((uint64_t) qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()) / qpid::sys::TIME_SEC); + + Receiver rx; + bool valid = session.nextReceiver(rx, Duration::SECOND); + if (threadCanceled) + break; + if (valid) { + try { + dispatch(rx.fetch()); + } catch (qpid::types::Exception& e) { + QPID_LOG(error, "Exception caught in message dispatch: " << e.what()); + } + session.acknowledge(); + } + } + } catch (qpid::types::Exception& e) { + QPID_LOG(error, "Exception caught in message thread - exiting: " << e.what()); + enqueueEvent(AgentEvent(new AgentEventImpl(AGENT_THREAD_FAILED))); + } + + QPID_LOG(debug, "AgentSession thread exiting for agent " << agentName); +} + diff --git a/qpid/cpp/src/qmf/AgentSubscription.cpp b/qpid/cpp/src/qmf/AgentSubscription.cpp new file mode 100644 index 0000000000..4dc5cb74a4 --- /dev/null +++ b/qpid/cpp/src/qmf/AgentSubscription.cpp @@ -0,0 +1,51 @@ +/* + * + * 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 "qmf/AgentSubscription.h" + +using namespace qmf; + +AgentSubscription::AgentSubscription(uint64_t _id, uint64_t _interval, uint64_t _life, + const std::string& _replyTo, const std::string& _cid, Query _query) : + id(_id), interval(_interval), lifetime(_life), timeSincePublish(0), timeSinceKeepalive(0), + replyTo(_replyTo), cid(_cid), query(_query) +{ +} + + +AgentSubscription::~AgentSubscription() +{ +} + + +bool AgentSubscription::tick(uint64_t seconds) +{ + timeSinceKeepalive += seconds; + if (timeSinceKeepalive >= lifetime) + return false; + + timeSincePublish += seconds; + if (timeSincePublish >= interval) { + } + + return true; +} + diff --git a/qpid/cpp/src/qmf/AgentSubscription.h b/qpid/cpp/src/qmf/AgentSubscription.h new file mode 100644 index 0000000000..01e8f43e9f --- /dev/null +++ b/qpid/cpp/src/qmf/AgentSubscription.h @@ -0,0 +1,52 @@ +#ifndef _QMF_AGENT_SUBSCRIPTION_H_ +#define _QMF_AGENT_SUBSCRIPTION_H_ +/* + * + * 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 "qpid/sys/IntegerTypes.h" +#include "qpid/types/Variant.h" +#include "qmf/Query.h" +#include "qmf/Data.h" +#include <boost/shared_ptr.hpp> + +namespace qmf { + class AgentSubscription { + public: + AgentSubscription(uint64_t _id, uint64_t _interval, uint64_t _life, + const std::string& _replyTo, const std::string& _cid, Query _query); + ~AgentSubscription(); + bool tick(uint64_t seconds); + void keepalive() { timeSinceKeepalive = 0; } + + private: + uint64_t id; + uint64_t interval; + uint64_t lifetime; + uint64_t timeSincePublish; + uint64_t timeSinceKeepalive; + const std::string replyTo; + const std::string cid; + Query query; + }; + +} + +#endif diff --git a/qpid/cpp/src/qmf/ConsoleEvent.cpp b/qpid/cpp/src/qmf/ConsoleEvent.cpp new file mode 100644 index 0000000000..b2a5e321c7 --- /dev/null +++ b/qpid/cpp/src/qmf/ConsoleEvent.cpp @@ -0,0 +1,82 @@ +/* + * + * 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 "qmf/ConsoleEventImpl.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/exceptions.h" + +using namespace std; +using namespace qmf; +using qpid::types::Variant; + +typedef PrivateImplRef<ConsoleEvent> PI; + +ConsoleEvent::ConsoleEvent(ConsoleEventImpl* impl) { PI::ctor(*this, impl); } +ConsoleEvent::ConsoleEvent(const ConsoleEvent& s) : qmf::Handle<ConsoleEventImpl>() { PI::copy(*this, s); } +ConsoleEvent::~ConsoleEvent() { PI::dtor(*this); } +ConsoleEvent& ConsoleEvent::operator=(const ConsoleEvent& s) { return PI::assign(*this, s); } + +ConsoleEventCode ConsoleEvent::getType() const { return impl->getType(); } +uint32_t ConsoleEvent::getCorrelator() const { return impl->getCorrelator(); } +Agent ConsoleEvent::getAgent() const { return impl->getAgent(); } +AgentDelReason ConsoleEvent::getAgentDelReason() const { return impl->getAgentDelReason(); } +uint32_t ConsoleEvent::getSchemaIdCount() const { return impl->getSchemaIdCount(); } +SchemaId ConsoleEvent::getSchemaId(uint32_t i) const { return impl->getSchemaId(i); } +uint32_t ConsoleEvent::getDataCount() const { return impl->getDataCount(); } +Data ConsoleEvent::getData(uint32_t i) const { return impl->getData(i); } +bool ConsoleEvent::isFinal() const { return impl->isFinal(); } +const Variant::Map& ConsoleEvent::getArguments() const { return impl->getArguments(); } +int ConsoleEvent::getSeverity() const { return impl->getSeverity(); } +uint64_t ConsoleEvent::getTimestamp() const { return impl->getTimestamp(); } + + +SchemaId ConsoleEventImpl::getSchemaId(uint32_t i) const +{ + uint32_t count = 0; + for (list<SchemaId>::const_iterator iter = newSchemaIds.begin(); iter != newSchemaIds.end(); iter++) { + if (count++ == i) + return *iter; + } + throw IndexOutOfRange(); +} + + +Data ConsoleEventImpl::getData(uint32_t i) const +{ + uint32_t count = 0; + for (list<Data>::const_iterator iter = dataList.begin(); iter != dataList.end(); iter++) { + if (count++ == i) + return *iter; + } + throw IndexOutOfRange(); +} + + +ConsoleEventImpl& ConsoleEventImplAccess::get(ConsoleEvent& item) +{ + return *item.impl; +} + + +const ConsoleEventImpl& ConsoleEventImplAccess::get(const ConsoleEvent& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/ConsoleEventImpl.h b/qpid/cpp/src/qmf/ConsoleEventImpl.h new file mode 100644 index 0000000000..9843971456 --- /dev/null +++ b/qpid/cpp/src/qmf/ConsoleEventImpl.h @@ -0,0 +1,84 @@ +#ifndef _QMF_CONSOLE_EVENT_IMPL_H_ +#define _QMF_CONSOLE_EVENT_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/ConsoleEvent.h" +#include "qmf/Agent.h" +#include "qmf/Data.h" +#include "qpid/types/Variant.h" +#include <list> + +namespace qmf { + class ConsoleEventImpl : public virtual qpid::RefCounted { + public: + // + // Impl-only methods + // + ConsoleEventImpl(ConsoleEventCode e, AgentDelReason r = AGENT_DEL_AGED) : + eventType(e), delReason(r), correlator(0), final(false) {} + void setCorrelator(uint32_t c) { correlator = c; } + void setAgent(const Agent& a) { agent = a; } + void addData(const Data& d) { dataList.push_back(Data(d)); } + void addSchemaId(const SchemaId& s) { newSchemaIds.push_back(SchemaId(s)); } + void setFinal() { final = true; } + void setArguments(const qpid::types::Variant::Map& a) { arguments = a; } + void setSeverity(int s) { severity = s; } + void setTimestamp(uint64_t t) { timestamp = t; } + + // + // Methods from API handle + // + ConsoleEventCode getType() const { return eventType; } + uint32_t getCorrelator() const { return correlator; } + Agent getAgent() const { return agent; } + AgentDelReason getAgentDelReason() const { return delReason; } + uint32_t getSchemaIdCount() const { return newSchemaIds.size(); } + SchemaId getSchemaId(uint32_t) const; + uint32_t getDataCount() const { return dataList.size(); } + Data getData(uint32_t i) const; + bool isFinal() const { return final; } + const qpid::types::Variant::Map& getArguments() const { return arguments; } + int getSeverity() const { return severity; } + uint64_t getTimestamp() const { return timestamp; } + + private: + const ConsoleEventCode eventType; + const AgentDelReason delReason; + uint32_t correlator; + Agent agent; + bool final; + std::list<Data> dataList; + std::list<SchemaId> newSchemaIds; + qpid::types::Variant::Map arguments; + int severity; + uint64_t timestamp; + }; + + struct ConsoleEventImplAccess + { + static ConsoleEventImpl& get(ConsoleEvent&); + static const ConsoleEventImpl& get(const ConsoleEvent&); + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/ConsoleSession.cpp b/qpid/cpp/src/qmf/ConsoleSession.cpp new file mode 100644 index 0000000000..7b839930e1 --- /dev/null +++ b/qpid/cpp/src/qmf/ConsoleSession.cpp @@ -0,0 +1,618 @@ +/* + * + * 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 "qmf/PrivateImplRef.h" +#include "qmf/ConsoleSessionImpl.h" +#include "qmf/AgentImpl.h" +#include "qmf/SchemaId.h" +#include "qmf/SchemaImpl.h" +#include "qmf/ConsoleEventImpl.h" +#include "qmf/constants.h" +#include "qpid/log/Statement.h" +#include "qpid/messaging/AddressParser.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Receiver.h" + +using namespace std; +using namespace qmf; +using qpid::messaging::Address; +using qpid::messaging::Connection; +using qpid::messaging::Receiver; +using qpid::messaging::Sender; +using qpid::messaging::Duration; +using qpid::messaging::Message; +using qpid::types::Variant; + +typedef qmf::PrivateImplRef<ConsoleSession> PI; + +ConsoleSession::ConsoleSession(ConsoleSessionImpl* impl) { PI::ctor(*this, impl); } +ConsoleSession::ConsoleSession(const ConsoleSession& s) : qmf::Handle<ConsoleSessionImpl>() { PI::copy(*this, s); } +ConsoleSession::~ConsoleSession() { PI::dtor(*this); } +ConsoleSession& ConsoleSession::operator=(const ConsoleSession& s) { return PI::assign(*this, s); } + +ConsoleSession::ConsoleSession(Connection& c, const string& o) { PI::ctor(*this, new ConsoleSessionImpl(c, o)); } +void ConsoleSession::setDomain(const string& d) { impl->setDomain(d); } +void ConsoleSession::setAgentFilter(const string& f) { impl->setAgentFilter(f); } +void ConsoleSession::open() { impl->open(); } +void ConsoleSession::close() { impl->close(); } +bool ConsoleSession::nextEvent(ConsoleEvent& e, Duration t) { return impl->nextEvent(e, t); } +int ConsoleSession::pendingEvents() const { return impl->pendingEvents(); } +uint32_t ConsoleSession::getAgentCount() const { return impl->getAgentCount(); } +Agent ConsoleSession::getAgent(uint32_t i) const { return impl->getAgent(i); } +Agent ConsoleSession::getConnectedBrokerAgent() const { return impl->getConnectedBrokerAgent(); } +Subscription ConsoleSession::subscribe(const Query& q, const string& f, const string& o) { return impl->subscribe(q, f, o); } +Subscription ConsoleSession::subscribe(const string& q, const string& f, const string& o) { return impl->subscribe(q, f, o); } + +//======================================================================================== +// Impl Method Bodies +//======================================================================================== + +ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) : + connection(c), domain("default"), maxAgentAgeMinutes(5), + opened(false), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0), + connectedBrokerInAgentList(false), schemaCache(new SchemaCache()) +{ + if (!options.empty()) { + qpid::messaging::AddressParser parser(options); + Variant::Map optMap; + Variant::Map::const_iterator iter; + + parser.parseMap(optMap); + + iter = optMap.find("domain"); + if (iter != optMap.end()) + domain = iter->second.asString(); + + iter = optMap.find("max-agent-age"); + if (iter != optMap.end()) + maxAgentAgeMinutes = iter->second.asUint32(); + + iter = optMap.find("listen-on-direct"); + if (iter != optMap.end()) + listenOnDirect = iter->second.asBool(); + + iter = optMap.find("strict-security"); + if (iter != optMap.end()) + strictSecurity = iter->second.asBool(); + } +} + + +ConsoleSessionImpl::~ConsoleSessionImpl() +{ + if (opened) + close(); +} + + +void ConsoleSessionImpl::setAgentFilter(const string& predicate) +{ + agentQuery = Query(QUERY_OBJECT, predicate); + + // + // Purge the agent list of any agents that don't match the filter. + // + { + qpid::sys::Mutex::ScopedLock l(lock); + map<string, Agent> toDelete; + for (map<string, Agent>::iterator iter = agents.begin(); iter != agents.end(); iter++) + if (!agentQuery.matchesPredicate(iter->second.getAttributes())) { + toDelete[iter->first] = iter->second; + if (iter->second.getName() == connectedBrokerAgent.getName()) + connectedBrokerInAgentList = false; + } + + for (map<string, Agent>::iterator iter = toDelete.begin(); iter != toDelete.end(); iter++) { + agents.erase(iter->first); + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_DEL, AGENT_DEL_FILTER)); + eventImpl->setAgent(iter->second); + enqueueEventLH(eventImpl.release()); + } + + if (!connectedBrokerInAgentList && connectedBrokerAgent.isValid() && + agentQuery.matchesPredicate(connectedBrokerAgent.getAttributes())) { + agents[connectedBrokerAgent.getName()] = connectedBrokerAgent; + connectedBrokerInAgentList = true; + + // + // Enqueue a notification of the new agent. + // + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_ADD)); + eventImpl->setAgent(connectedBrokerAgent); + enqueueEventLH(ConsoleEvent(eventImpl.release())); + } + } + + // + // Broadcast an agent locate request with our new criteria. + // + if (opened) + sendAgentLocate(); +} + + +void ConsoleSessionImpl::open() +{ + if (opened) + throw QmfException("The session is already open"); + + // Establish messaging addresses + directBase = "qmf." + domain + ".direct"; + topicBase = "qmf." + domain + ".topic"; + + string myKey("direct-console." + qpid::types::Uuid(true).str()); + + replyAddress = Address(topicBase + "/" + myKey + ";{node:{type:topic}}"); + + // Create AMQP session, receivers, and senders + session = connection.createSession(); + Receiver directRx = session.createReceiver(replyAddress); + Receiver topicRx = session.createReceiver(topicBase + "/agent.#"); // TODO: be more discriminating + if (!strictSecurity) { + Receiver legacyRx = session.createReceiver("amq.direct/" + myKey + ";{node:{type:topic}}"); + legacyRx.setCapacity(64); + directSender = session.createSender(directBase + ";{create:never,node:{type:topic}}"); + directSender.setCapacity(128); + } + + directRx.setCapacity(64); + topicRx.setCapacity(128); + + topicSender = session.createSender(topicBase + ";{create:never,node:{type:topic}}"); + + topicSender.setCapacity(128); + + // Start the receiver thread + threadCanceled = false; + thread = new qpid::sys::Thread(*this); + + // Send an agent_locate to direct address 'broker' to identify the connected-broker-agent. + sendBrokerLocate(); + if (agentQuery) + sendAgentLocate(); + + opened = true; +} + + +void ConsoleSessionImpl::close() +{ + if (!opened) + throw QmfException("The session is already closed"); + + // Stop and join the receiver thread + threadCanceled = true; + thread->join(); + delete thread; + + // Close the AMQP session + session.close(); + opened = false; +} + + +bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout) +{ + uint64_t milliseconds = timeout.getMilliseconds(); + qpid::sys::Mutex::ScopedLock l(lock); + + if (eventQueue.empty() && milliseconds > 0) + cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(), + qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC))); + + if (!eventQueue.empty()) { + event = eventQueue.front(); + eventQueue.pop(); + return true; + } + + return false; +} + + +int ConsoleSessionImpl::pendingEvents() const +{ + qpid::sys::Mutex::ScopedLock l(lock); + return eventQueue.size(); +} + + +uint32_t ConsoleSessionImpl::getAgentCount() const +{ + qpid::sys::Mutex::ScopedLock l(lock); + return agents.size(); +} + + +Agent ConsoleSessionImpl::getAgent(uint32_t i) const +{ + qpid::sys::Mutex::ScopedLock l(lock); + uint32_t count = 0; + for (map<string, Agent>::const_iterator iter = agents.begin(); iter != agents.end(); iter++) + if (count++ == i) + return iter->second; + throw IndexOutOfRange(); +} + + +Subscription ConsoleSessionImpl::subscribe(const Query&, const string&, const string&) +{ + return Subscription(); +} + + +Subscription ConsoleSessionImpl::subscribe(const string&, const string&, const string&) +{ + return Subscription(); +} + + +void ConsoleSessionImpl::enqueueEvent(const ConsoleEvent& event) +{ + qpid::sys::Mutex::ScopedLock l(lock); + enqueueEventLH(event); +} + + +void ConsoleSessionImpl::enqueueEventLH(const ConsoleEvent& event) +{ + bool notify = eventQueue.empty(); + eventQueue.push(event); + if (notify) + cond.notify(); +} + + +void ConsoleSessionImpl::dispatch(Message msg) +{ + const Variant::Map& properties(msg.getProperties()); + Variant::Map::const_iterator iter; + Variant::Map::const_iterator oiter; + + oiter = properties.find(protocol::HEADER_KEY_OPCODE); + iter = properties.find(protocol::HEADER_KEY_APP_ID); + if (iter == properties.end()) + iter = properties.find("app_id"); + if (iter != properties.end() && iter->second.asString() == protocol::HEADER_APP_ID_QMF && oiter != properties.end()) { + // + // Dispatch a QMFv2 formatted message + // + const string& opcode = oiter->second.asString(); + + iter = properties.find(protocol::HEADER_KEY_AGENT); + if (iter == properties.end()) { + QPID_LOG(trace, "Message received with no 'qmf.agent' header"); + return; + } + const string& agentName = iter->second.asString(); + + Agent agent; + { + qpid::sys::Mutex::ScopedLock l(lock); + map<string, Agent>::iterator aIter = agents.find(agentName); + if (aIter != agents.end()) { + agent = aIter->second; + AgentImplAccess::get(agent).touch(); + } + } + + if (msg.getContentType() == "amqp/map" && + (opcode == protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION || opcode == protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE)) { + // + // This is the one case where it's ok (necessary actually) to receive a QMFv2 + // message from an unknown agent (how else are they going to get known?) + // + Variant::Map content; + decode(msg, content); + handleAgentUpdate(agentName, content, msg); + return; + } + + if (!agent.isValid()) + return; + + AgentImpl& agentImpl(AgentImplAccess::get(agent)); + + if (msg.getContentType() == "amqp/map") { + Variant::Map content; + decode(msg, content); + + if (opcode == protocol::HEADER_OPCODE_EXCEPTION) agentImpl.handleException(content, msg); + else if (opcode == protocol::HEADER_OPCODE_METHOD_RESPONSE) agentImpl.handleMethodResponse(content, msg); + else + QPID_LOG(error, "Received a map-formatted QMFv2 message with opcode=" << opcode); + + return; + } + + if (msg.getContentType() == "amqp/list") { + Variant::List content; + decode(msg, content); + + if (opcode == protocol::HEADER_OPCODE_QUERY_RESPONSE) agentImpl.handleQueryResponse(content, msg); + else if (opcode == protocol::HEADER_OPCODE_DATA_INDICATION) agentImpl.handleDataIndication(content, msg); + else + QPID_LOG(error, "Received a list-formatted QMFv2 message with opcode=" << opcode); + + return; + } + } else { + // + // Dispatch a QMFv1 formatted message + // + const string& body(msg.getContent()); + if (body.size() < 8) + return; + qpid::management::Buffer buffer(const_cast<char*>(body.c_str()), body.size()); + + if (buffer.getOctet() != 'A') return; + if (buffer.getOctet() != 'M') return; + if (buffer.getOctet() != '2') return; + char v1Opcode(buffer.getOctet()); + uint32_t seq(buffer.getLong()); + + if (v1Opcode == 's') handleV1SchemaResponse(buffer, seq, msg); + else { + QPID_LOG(trace, "Unknown or Unsupported QMFv1 opcode: " << v1Opcode); + } + } +} + + +void ConsoleSessionImpl::sendBrokerLocate() +{ + Message msg; + Variant::Map& headers(msg.getProperties()); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + + msg.setReplyTo(replyAddress); + msg.setCorrelationId("broker-locate"); + msg.setSubject("broker"); + + Sender sender = session.createSender(directBase + ";{create:never,node:{type:topic}}"); + sender.send(msg); + sender.close(); + + QPID_LOG(trace, "SENT AgentLocate to broker"); +} + + +void ConsoleSessionImpl::sendAgentLocate() +{ + Message msg; + Variant::Map& headers(msg.getProperties()); + static const string subject("console.request.agent_locate"); + + headers[protocol::HEADER_KEY_METHOD] = protocol::HEADER_METHOD_REQUEST; + headers[protocol::HEADER_KEY_OPCODE] = protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST; + headers[protocol::HEADER_KEY_APP_ID] = protocol::HEADER_APP_ID_QMF; + + msg.setReplyTo(replyAddress); + msg.setCorrelationId("agent-locate"); + msg.setSubject(subject); + encode(agentQuery.getPredicate(), msg); + + topicSender.send(msg); + + QPID_LOG(trace, "SENT AgentLocate to=" << topicSender.getName() << "/" << subject); +} + + +void ConsoleSessionImpl::handleAgentUpdate(const string& agentName, const Variant::Map& content, const Message& msg) +{ + Variant::Map::const_iterator iter; + Agent agent; + uint32_t epoch(0); + string cid(msg.getCorrelationId()); + + iter = content.find("_values"); + if (iter == content.end()) + return; + const Variant::Map& in_attrs(iter->second.asMap()); + Variant::Map attrs; + + // + // Copy the map from the message to "attrs". Translate any old-style + // keys to their new key values in the process. + // + for (iter = in_attrs.begin(); iter != in_attrs.end(); iter++) { + if (iter->first == "epoch") + attrs[protocol::AGENT_ATTR_EPOCH] = iter->second; + else if (iter->first == "timestamp") + attrs[protocol::AGENT_ATTR_TIMESTAMP] = iter->second; + else if (iter->first == "heartbeat_interval") + attrs[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = iter->second; + else + attrs[iter->first] = iter->second; + } + + iter = attrs.find(protocol::AGENT_ATTR_EPOCH); + if (iter != attrs.end()) + epoch = iter->second.asUint32(); + + if (cid == "broker-locate") { + qpid::sys::Mutex::ScopedLock l(lock); + auto_ptr<AgentImpl> impl(new AgentImpl(agentName, epoch, *this)); + for (iter = attrs.begin(); iter != attrs.end(); iter++) + if (iter->first != protocol::AGENT_ATTR_EPOCH) + impl->setAttribute(iter->first, iter->second); + agent = Agent(impl.release()); + connectedBrokerAgent = agent; + if (!agentQuery || agentQuery.matchesPredicate(attrs)) { + connectedBrokerInAgentList = true; + agents[agentName] = agent; + + // + // Enqueue a notification of the new agent. + // + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_ADD)); + eventImpl->setAgent(agent); + enqueueEventLH(ConsoleEvent(eventImpl.release())); + } + return; + } + + // + // Check this agent against the agent filter. Exit if it doesn't match. + // (only if this isn't the connected broker agent) + // + if (agentQuery && (!agentQuery.matchesPredicate(attrs))) + return; + + QPID_LOG(trace, "RCVD AgentHeartbeat from an agent matching our filter: " << agentName); + + { + qpid::sys::Mutex::ScopedLock l(lock); + map<string, Agent>::iterator aIter = agents.find(agentName); + if (aIter == agents.end()) { + // + // This is a new agent. We have no current record of its existence. + // + auto_ptr<AgentImpl> impl(new AgentImpl(agentName, epoch, *this)); + for (iter = attrs.begin(); iter != attrs.end(); iter++) + if (iter->first != protocol::AGENT_ATTR_EPOCH) + impl->setAttribute(iter->first, iter->second); + agent = Agent(impl.release()); + agents[agentName] = agent; + + // + // Enqueue a notification of the new agent. + // + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_ADD)); + eventImpl->setAgent(agent); + enqueueEventLH(ConsoleEvent(eventImpl.release())); + } else { + // + // This is a refresh of an agent we are already tracking. + // + bool detectedRestart(false); + agent = aIter->second; + AgentImpl& impl(AgentImplAccess::get(agent)); + impl.touch(); + if (impl.getEpoch() != epoch) { + // + // The agent has restarted since the last time we heard from it. + // Enqueue a notification. + // + impl.setEpoch(epoch); + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_RESTART)); + eventImpl->setAgent(agent); + enqueueEventLH(ConsoleEvent(eventImpl.release())); + detectedRestart = true; + } + + iter = attrs.find(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP); + if (iter != attrs.end()) { + uint64_t ts(iter->second.asUint64()); + if (ts > impl.getAttribute(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP).asUint64()) { + // + // The agent has added new schema entries since we last heard from it. + // Update the attribute and, if this doesn't accompany a restart, enqueue a notification. + // + if (!detectedRestart) { + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_SCHEMA_UPDATE)); + eventImpl->setAgent(agent); + enqueueEventLH(ConsoleEvent(eventImpl.release())); + } + impl.setAttribute(protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP, iter->second); + } + } + } + } +} + + +void ConsoleSessionImpl::handleV1SchemaResponse(qpid::management::Buffer& buffer, uint32_t, const Message&) +{ + QPID_LOG(trace, "RCVD V1SchemaResponse"); + Schema schema(new SchemaImpl(buffer)); + schemaCache->declareSchema(schema); +} + + +void ConsoleSessionImpl::periodicProcessing(uint64_t seconds) +{ + // + // The granularity of this timer is seconds. Don't waste time looking for work if + // it's been less than a second since we last visited. + // + if (seconds == lastVisit) + return; + lastVisit = seconds; + + // + // Handle the aging of agent records + // + if (lastAgePass == 0) + lastAgePass = seconds; + if (seconds - lastAgePass >= 60) { + lastAgePass = seconds; + map<string, Agent> toDelete; + qpid::sys::Mutex::ScopedLock l(lock); + + for (map<string, Agent>::iterator iter = agents.begin(); iter != agents.end(); iter++) + if ((iter->second.getName() != connectedBrokerAgent.getName()) && + (AgentImplAccess::get(iter->second).age() > maxAgentAgeMinutes)) + toDelete[iter->first] = iter->second; + + for (map<string, Agent>::iterator iter = toDelete.begin(); iter != toDelete.end(); iter++) { + agents.erase(iter->first); + auto_ptr<ConsoleEventImpl> eventImpl(new ConsoleEventImpl(CONSOLE_AGENT_DEL, AGENT_DEL_AGED)); + eventImpl->setAgent(iter->second); + enqueueEventLH(eventImpl.release()); + } + } +} + + +void ConsoleSessionImpl::run() +{ + QPID_LOG(debug, "ConsoleSession thread started"); + + try { + while (!threadCanceled) { + periodicProcessing((uint64_t) qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()) / + qpid::sys::TIME_SEC); + + Receiver rx; + bool valid = session.nextReceiver(rx, Duration::SECOND); + if (threadCanceled) + break; + if (valid) { + try { + dispatch(rx.fetch()); + } catch (qpid::types::Exception& e) { + QPID_LOG(error, "Exception caught in message dispatch: " << e.what()); + } + session.acknowledge(); + } + } + } catch (qpid::types::Exception& e) { + QPID_LOG(error, "Exception caught in message thread - exiting: " << e.what()); + enqueueEvent(ConsoleEvent(new ConsoleEventImpl(CONSOLE_THREAD_FAILED))); + } + + QPID_LOG(debug, "ConsoleSession thread exiting"); +} + diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h new file mode 100644 index 0000000000..411b3f016a --- /dev/null +++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h @@ -0,0 +1,108 @@ +#ifndef _QMF_CONSOLE_SESSION_IMPL_H_ +#define _QMF_CONSOLE_SESSION_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/ConsoleSession.h" +#include "qmf/AgentImpl.h" +#include "qmf/SchemaId.h" +#include "qmf/Schema.h" +#include "qmf/ConsoleEventImpl.h" +#include "qmf/SchemaCache.h" +#include "qmf/Query.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Condition.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/log/Statement.h" +#include "qpid/messaging/Message.h" +#include "qpid/messaging/Connection.h" +#include "qpid/messaging/Session.h" +#include "qpid/messaging/Sender.h" +#include "qpid/messaging/Address.h" +#include "qpid/management/Buffer.h" +#include "qpid/types/Variant.h" +#include <map> +#include <queue> + +namespace qmf { + class ConsoleSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable { + public: + ~ConsoleSessionImpl(); + + // + // Methods from API handle + // + ConsoleSessionImpl(qpid::messaging::Connection& c, const std::string& o); + void setDomain(const std::string& d) { domain = d; } + void setAgentFilter(const std::string& f); + void open(); + void close(); + bool nextEvent(ConsoleEvent& e, qpid::messaging::Duration t); + int pendingEvents() const; + uint32_t getAgentCount() const; + Agent getAgent(uint32_t i) const; + Agent getConnectedBrokerAgent() const { return connectedBrokerAgent; } + Subscription subscribe(const Query&, const std::string& agentFilter, const std::string& options); + Subscription subscribe(const std::string&, const std::string& agentFilter, const std::string& options); + + protected: + mutable qpid::sys::Mutex lock; + qpid::sys::Condition cond; + qpid::messaging::Connection connection; + qpid::messaging::Session session; + qpid::messaging::Sender directSender; + qpid::messaging::Sender topicSender; + std::string domain; + uint32_t maxAgentAgeMinutes; + bool listenOnDirect; + bool strictSecurity; + Query agentQuery; + bool opened; + std::queue<ConsoleEvent> eventQueue; + qpid::sys::Thread* thread; + bool threadCanceled; + uint64_t lastVisit; + uint64_t lastAgePass; + std::map<std::string, Agent> agents; + Agent connectedBrokerAgent; + bool connectedBrokerInAgentList; + qpid::messaging::Address replyAddress; + std::string directBase; + std::string topicBase; + boost::shared_ptr<SchemaCache> schemaCache; + + void enqueueEvent(const ConsoleEvent&); + void enqueueEventLH(const ConsoleEvent&); + void dispatch(qpid::messaging::Message); + void sendBrokerLocate(); + void sendAgentLocate(); + void handleAgentUpdate(const std::string&, const qpid::types::Variant::Map&, const qpid::messaging::Message&); + void handleV1SchemaResponse(qpid::management::Buffer&, uint32_t, const qpid::messaging::Message&); + void periodicProcessing(uint64_t); + void run(); + + friend class AgentImpl; + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/Data.cpp b/qpid/cpp/src/qmf/Data.cpp new file mode 100644 index 0000000000..c503bab445 --- /dev/null +++ b/qpid/cpp/src/qmf/Data.cpp @@ -0,0 +1,130 @@ +/* + * + * 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 "qmf/DataImpl.h" +#include "qmf/DataAddrImpl.h" +#include "qmf/SchemaImpl.h" +#include "qmf/SchemaIdImpl.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/SchemaProperty.h" + +using namespace std; +using namespace qmf; +using qpid::types::Variant; + +typedef PrivateImplRef<Data> PI; + +Data::Data(DataImpl* impl) { PI::ctor(*this, impl); } +Data::Data(const Data& s) : qmf::Handle<DataImpl>() { PI::copy(*this, s); } +Data::~Data() { PI::dtor(*this); } +Data& Data::operator=(const Data& s) { return PI::assign(*this, s); } + +Data::Data(const Schema& s) { PI::ctor(*this, new DataImpl(s)); } +void Data::setAddr(const DataAddr& a) { impl->setAddr(a); } +void Data::setProperty(const string& k, const qpid::types::Variant& v) { impl->setProperty(k, v); } +void Data::overwriteProperties(const qpid::types::Variant::Map& m) { impl->overwriteProperties(m); } +bool Data::hasSchema() const { return impl->hasSchema(); } +bool Data::hasAddr() const { return impl->hasAddr(); } +const SchemaId& Data::getSchemaId() const { return impl->getSchemaId(); } +const DataAddr& Data::getAddr() const { return impl->getAddr(); } +const Variant& Data::getProperty(const string& k) const { return impl->getProperty(k); } +const Variant::Map& Data::getProperties() const { return impl->getProperties(); } +bool Data::hasAgent() const { return impl->hasAgent(); } +const Agent& Data::getAgent() const { return impl->getAgent(); } + + +void DataImpl::overwriteProperties(const Variant::Map& m) { + for (Variant::Map::const_iterator iter = m.begin(); iter != m.end(); iter++) + properties[iter->first] = iter->second; +} + +const Variant& DataImpl::getProperty(const string& k) const { + Variant::Map::const_iterator iter = properties.find(k); + if (iter == properties.end()) + throw KeyNotFound(k); + return iter->second; +} + + +DataImpl::DataImpl(const qpid::types::Variant::Map& map, const Agent& a) +{ + Variant::Map::const_iterator iter; + + agent = a; + + iter = map.find("_values"); + if (iter != map.end()) + properties = iter->second.asMap(); + + iter = map.find("_object_id"); + if (iter != map.end()) + dataAddr = DataAddr(new DataAddrImpl(iter->second.asMap())); + + iter = map.find("_schema_id"); + if (iter != map.end()) + schemaId = SchemaId(new SchemaIdImpl(iter->second.asMap())); +} + + +Variant::Map DataImpl::asMap() const +{ + Variant::Map result; + + result["_values"] = properties; + + if (hasAddr()) { + const DataAddrImpl& aImpl(DataAddrImplAccess::get(getAddr())); + result["_object_id"] = aImpl.asMap(); + } + + if (hasSchema()) { + const SchemaIdImpl& sImpl(SchemaIdImplAccess::get(getSchemaId())); + result["_schema_id"] = sImpl.asMap(); + } + + return result; +} + + +void DataImpl::setProperty(const std::string& k, const qpid::types::Variant& v) +{ + if (schema.isValid()) { + // + // If we have a valid schema, make sure that the property is included in the + // schema and that the variant type is compatible with the schema type. + // + if (!SchemaImplAccess::get(schema).isValidProperty(k, v)) + throw QmfException("Property '" + k + "' either not in the schema or value is of incompatible type"); + } + properties[k] = v; +} + + +DataImpl& DataImplAccess::get(Data& item) +{ + return *item.impl; +} + + +const DataImpl& DataImplAccess::get(const Data& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/DataAddr.cpp b/qpid/cpp/src/qmf/DataAddr.cpp new file mode 100644 index 0000000000..fb51d5787f --- /dev/null +++ b/qpid/cpp/src/qmf/DataAddr.cpp @@ -0,0 +1,106 @@ +/* + * + * 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 "qmf/DataAddrImpl.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/DataAddr.h" +#include <iostream> + +using namespace std; +using namespace qmf; +using qpid::types::Variant; + +typedef PrivateImplRef<DataAddr> PI; + +DataAddr::DataAddr(DataAddrImpl* impl) { PI::ctor(*this, impl); } +DataAddr::DataAddr(const DataAddr& s) : qmf::Handle<DataAddrImpl>() { PI::copy(*this, s); } +DataAddr::~DataAddr() { PI::dtor(*this); } +DataAddr& DataAddr::operator=(const DataAddr& s) { return PI::assign(*this, s); } + +bool DataAddr::operator==(const DataAddr& o) { return *impl == *o.impl; } +bool DataAddr::operator<(const DataAddr& o) { return *impl < *o.impl; } + +DataAddr::DataAddr(const qpid::types::Variant::Map& m) { PI::ctor(*this, new DataAddrImpl(m)); } +DataAddr::DataAddr(const string& n, const string& a, uint32_t e) { PI::ctor(*this, new DataAddrImpl(n, a, e)); } +const string& DataAddr::getName() const { return impl->getName(); } +const string& DataAddr::getAgentName() const { return impl->getAgentName(); } +uint32_t DataAddr::getAgentEpoch() const { return impl->getAgentEpoch(); } +Variant::Map DataAddr::asMap() const { return impl->asMap(); } + +bool DataAddrImpl::operator==(const DataAddrImpl& other) +{ + return + agentName == other.agentName && + name == other.name && + agentEpoch == other.agentEpoch; +} + + +bool DataAddrImpl::operator<(const DataAddrImpl& other) +{ + if (agentName < other.agentName) return true; + if (agentName > other.agentName) return false; + if (name < other.name) return true; + if (name > other.name) return false; + return agentEpoch < other.agentEpoch; +} + + +DataAddrImpl::DataAddrImpl(const Variant::Map& map) +{ + Variant::Map::const_iterator iter; + + iter = map.find("_agent_name"); + if (iter != map.end()) + agentName = iter->second.asString(); + + iter = map.find("_object_name"); + if (iter != map.end()) + name = iter->second.asString(); + + iter = map.find("_agent_epoch"); + if (iter != map.end()) + agentEpoch = (uint32_t) iter->second.asUint64(); +} + + +Variant::Map DataAddrImpl::asMap() const +{ + Variant::Map result; + + result["_agent_name"] = agentName; + result["_object_name"] = name; + if (agentEpoch > 0) + result["_agent_epoch"] = agentEpoch; + return result; +} + + +DataAddrImpl& DataAddrImplAccess::get(DataAddr& item) +{ + return *item.impl; +} + + +const DataAddrImpl& DataAddrImplAccess::get(const DataAddr& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/DataAddrImpl.h b/qpid/cpp/src/qmf/DataAddrImpl.h new file mode 100644 index 0000000000..3f9cae9453 --- /dev/null +++ b/qpid/cpp/src/qmf/DataAddrImpl.h @@ -0,0 +1,73 @@ +#ifndef _QMF_DATA_ADDR_IMPL_H_ +#define _QMF_DATA_ADDR_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qpid/sys/IntegerTypes.h" +#include "qpid/types/Variant.h" +#include "qmf/DataAddr.h" + +namespace qmf { + class DataAddrImpl : public virtual qpid::RefCounted { + public: + // + // Impl-only methods + // + void setName(const std::string& n) { name = n; } + void setAgent(const std::string& n, uint32_t e=0) { agentName = n; agentEpoch = e; } + + // + // Methods from API handle + // + bool operator==(const DataAddrImpl&); + bool operator<(const DataAddrImpl&); + DataAddrImpl(const qpid::types::Variant::Map&); + DataAddrImpl(const std::string& _name, const std::string& _agentName, uint32_t _agentEpoch=0) : + agentName(_agentName), name(_name), agentEpoch(_agentEpoch) {} + const std::string& getName() const { return name; } + const std::string& getAgentName() const { return agentName; } + uint32_t getAgentEpoch() const { return agentEpoch; } + qpid::types::Variant::Map asMap() const; + + private: + std::string agentName; + std::string name; + uint32_t agentEpoch; + }; + + struct DataAddrImplAccess + { + static DataAddrImpl& get(DataAddr&); + static const DataAddrImpl& get(const DataAddr&); + }; + + struct DataAddrCompare { + bool operator() (const DataAddr& lhs, const DataAddr& rhs) const + { + if (lhs.getName() != rhs.getName()) + return lhs.getName() < rhs.getName(); + return lhs.getAgentName() < rhs.getAgentName(); + } + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/DataImpl.h b/qpid/cpp/src/qmf/DataImpl.h new file mode 100644 index 0000000000..4ac3197da0 --- /dev/null +++ b/qpid/cpp/src/qmf/DataImpl.h @@ -0,0 +1,84 @@ +#ifndef _QMF_DATA_IMPL_H_ +#define _QMF_DATA_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/Data.h" +#include "qmf/SchemaId.h" +#include "qmf/Schema.h" +#include "qmf/DataAddr.h" +#include "qmf/Agent.h" +#include "qmf/AgentSubscription.h" +#include "qpid/types/Variant.h" + +namespace qmf { + class DataImpl : public virtual qpid::RefCounted { + public: + // + // Public impl-only methods + // + DataImpl(const qpid::types::Variant::Map&, const Agent&); + qpid::types::Variant::Map asMap() const; + DataImpl() {} + void addSubscription(boost::shared_ptr<AgentSubscription>); + void delSubscription(uint64_t); + qpid::types::Variant::Map publishSubscription(uint64_t); + const Schema& getSchema() const { return schema; } + + // + // Methods from API handle + // + DataImpl(const Schema& s) : schema(s) {} + void setAddr(const DataAddr& a) { dataAddr = a; } + void setProperty(const std::string& k, const qpid::types::Variant& v); + void overwriteProperties(const qpid::types::Variant::Map& m); + bool hasSchema() const { return schemaId.isValid() || schema.isValid(); } + bool hasAddr() const { return dataAddr.isValid(); } + const SchemaId& getSchemaId() const { if (schema.isValid()) return schema.getSchemaId(); else return schemaId; } + const DataAddr& getAddr() const { return dataAddr; } + const qpid::types::Variant& getProperty(const std::string& k) const; + const qpid::types::Variant::Map& getProperties() const { return properties; } + bool hasAgent() const { return agent.isValid(); } + const Agent& getAgent() const { return agent; } + + private: + struct Subscr { + boost::shared_ptr<AgentSubscription> subscription; + qpid::types::Variant::Map deltas; + }; + std::map<uint64_t, boost::shared_ptr<Subscr> > subscriptions; + + SchemaId schemaId; + Schema schema; + DataAddr dataAddr; + qpid::types::Variant::Map properties; + Agent agent; + }; + + struct DataImplAccess + { + static DataImpl& get(Data&); + static const DataImpl& get(const Data&); + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/Expression.cpp b/qpid/cpp/src/qmf/Expression.cpp new file mode 100644 index 0000000000..7d48678c15 --- /dev/null +++ b/qpid/cpp/src/qmf/Expression.cpp @@ -0,0 +1,441 @@ +/* + * + * 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 "qmf/exceptions.h" +#include "qmf/Expression.h" +#include <iostream> + +using namespace std; +using namespace qmf; +using namespace qpid::types; + +Expression::Expression(const Variant::List& expr) +{ + static int level(0); + level++; + Variant::List::const_iterator iter(expr.begin()); + string op(iter->asString()); + iter++; + + if (op == "not") logicalOp = LOGICAL_NOT; + else if (op == "and") logicalOp = LOGICAL_AND; + else if (op == "or") logicalOp = LOGICAL_OR; + else { + logicalOp = LOGICAL_ID; + if (op == "eq") boolOp = BOOL_EQ; + else if (op == "ne") boolOp = BOOL_NE; + else if (op == "lt") boolOp = BOOL_LT; + else if (op == "le") boolOp = BOOL_LE; + else if (op == "gt") boolOp = BOOL_GT; + else if (op == "ge") boolOp = BOOL_GE; + else if (op == "re_match") boolOp = BOOL_RE_MATCH; + else if (op == "exists") boolOp = BOOL_EXISTS; + else if (op == "true") boolOp = BOOL_TRUE; + else if (op == "false") boolOp = BOOL_FALSE; + else + throw QmfException("Invalid operator in predicate expression"); + } + + if (logicalOp == LOGICAL_ID) { + switch (boolOp) { + case BOOL_EQ: + case BOOL_NE: + case BOOL_LT: + case BOOL_LE: + case BOOL_GT: + case BOOL_GE: + case BOOL_RE_MATCH: + // + // Binary operator: get two operands. + // + operandCount = 2; + break; + + case BOOL_EXISTS: + // + // Unary operator: get one operand. + // + operandCount = 1; + break; + + case BOOL_TRUE: + case BOOL_FALSE: + // + // Literal operator: no operands. + // + operandCount = 0; + break; + } + + for (int idx = 0; idx < operandCount; idx++) { + if (iter == expr.end()) + throw QmfException("Too few operands for operation: " + op); + if (iter->getType() == VAR_STRING) { + quoted[idx] = false; + operands[idx] = *iter; + } else if (iter->getType() == VAR_LIST) { + const Variant::List& sublist(iter->asList()); + Variant::List::const_iterator subIter(sublist.begin()); + if (subIter != sublist.end() && subIter->asString() == "quote") { + quoted[idx] = true; + subIter++; + if (subIter != sublist.end()) { + operands[idx] = *subIter; + subIter++; + if (subIter != sublist.end()) + throw QmfException("Extra tokens at end of 'quote'"); + } + } else + throw QmfException("Expected '[quote, <token>]'"); + } else + throw QmfException("Expected string or list as operand for: " + op); + iter++; + } + + if (iter != expr.end()) + throw QmfException("Too many operands for operation: " + op); + + } else { + // + // This is a logical expression, collect sub-expressions + // + while (iter != expr.end()) { + if (iter->getType() != VAR_LIST) + throw QmfException("Operands of " + op + " must be lists"); + expressionList.push_back(boost::shared_ptr<Expression>(new Expression(iter->asList()))); + iter++; + } + } + level--; +} + + +bool Expression::evaluate(const Variant::Map& data) const +{ + list<boost::shared_ptr<Expression> >::const_iterator iter; + + switch (logicalOp) { + case LOGICAL_ID: + return boolEval(data); + + case LOGICAL_NOT: + for (iter = expressionList.begin(); iter != expressionList.end(); iter++) + if ((*iter)->evaluate(data)) + return false; + return true; + + case LOGICAL_AND: + for (iter = expressionList.begin(); iter != expressionList.end(); iter++) + if (!(*iter)->evaluate(data)) + return false; + return true; + + case LOGICAL_OR: + for (iter = expressionList.begin(); iter != expressionList.end(); iter++) + if ((*iter)->evaluate(data)) + return true; + return false; + } + + return false; +} + + +bool Expression::boolEval(const Variant::Map& data) const +{ + Variant val[2]; + bool exists[2]; + + for (int idx = 0; idx < operandCount; idx++) { + if (quoted[idx]) { + exists[idx] = true; + val[idx] = operands[idx]; + } else { + Variant::Map::const_iterator mIter(data.find(operands[idx].asString())); + if (mIter == data.end()) { + exists[idx] = false; + } else { + exists[idx] = true; + val[idx] = mIter->second; + } + } + } + + switch (boolOp) { + case BOOL_EQ: return (exists[0] && exists[1] && (val[0].asString() == val[1].asString())); + case BOOL_NE: return (exists[0] && exists[1] && (val[0].asString() != val[1].asString())); + case BOOL_LT: return (exists[0] && exists[1] && lessThan(val[0], val[1])); + case BOOL_LE: return (exists[0] && exists[1] && lessEqual(val[0], val[1])); + case BOOL_GT: return (exists[0] && exists[1] && greaterThan(val[0], val[1])); + case BOOL_GE: return (exists[0] && exists[1] && greaterEqual(val[0], val[1])); + case BOOL_RE_MATCH: return false; // TODO + case BOOL_EXISTS: return exists[0]; + case BOOL_TRUE: return true; + case BOOL_FALSE: return false; + } + + return false; +} + +bool Expression::lessThan(const Variant& left, const Variant& right) const +{ + switch (left.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + switch (right.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + return left.asInt64() < right.asInt64(); + case VAR_STRING: + try { + return left.asInt64() < right.asInt64(); + } catch (std::exception&) {} + break; + default: + break; + } + break; + + case VAR_FLOAT: case VAR_DOUBLE: + switch (right.getType()) { + case VAR_FLOAT: case VAR_DOUBLE: + return left.asDouble() < right.asDouble(); + case VAR_STRING: + try { + return left.asDouble() < right.asDouble(); + } catch (std::exception&) {} + break; + default: + break; + } + break; + + case VAR_STRING: + switch (right.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + try { + return left.asInt64() < right.asInt64(); + } catch (std::exception&) {} + break; + + case VAR_FLOAT: case VAR_DOUBLE: + try { + return left.asDouble() < right.asDouble(); + } catch (std::exception&) {} + break; + + case VAR_STRING: + return left.asString() < right.asString(); + default: + break; + } + default: + break; + } + + return false; +} + + +bool Expression::lessEqual(const Variant& left, const Variant& right) const +{ + switch (left.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + switch (right.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + return left.asInt64() <= right.asInt64(); + case VAR_STRING: + try { + return left.asInt64() <= right.asInt64(); + } catch (std::exception&) {} + break; + default: + break; + } + break; + + case VAR_FLOAT: case VAR_DOUBLE: + switch (right.getType()) { + case VAR_FLOAT: case VAR_DOUBLE: + return left.asDouble() <= right.asDouble(); + case VAR_STRING: + try { + return left.asDouble() <= right.asDouble(); + } catch (std::exception&) {} + break; + default: + break; + } + break; + + case VAR_STRING: + switch (right.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + try { + return left.asInt64() <= right.asInt64(); + } catch (std::exception&) {} + break; + + case VAR_FLOAT: case VAR_DOUBLE: + try { + return left.asDouble() <= right.asDouble(); + } catch (std::exception&) {} + break; + + case VAR_STRING: + return left.asString() <= right.asString(); + default: + break; + } + default: + break; + } + + return false; +} + + +bool Expression::greaterThan(const Variant& left, const Variant& right) const +{ + switch (left.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + switch (right.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + return left.asInt64() > right.asInt64(); + case VAR_STRING: + try { + return left.asInt64() > right.asInt64(); + } catch (std::exception&) {} + break; + default: + break; + } + break; + + case VAR_FLOAT: case VAR_DOUBLE: + switch (right.getType()) { + case VAR_FLOAT: case VAR_DOUBLE: + return left.asDouble() > right.asDouble(); + case VAR_STRING: + try { + return left.asDouble() > right.asDouble(); + } catch (std::exception&) {} + break; + default: + break; + } + break; + + case VAR_STRING: + switch (right.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + try { + return left.asInt64() > right.asInt64(); + } catch (std::exception&) {} + break; + + case VAR_FLOAT: case VAR_DOUBLE: + try { + return left.asDouble() > right.asDouble(); + } catch (std::exception&) {} + break; + + case VAR_STRING: + return left.asString() > right.asString(); + default: + break; + } + default: + break; + } + + return false; +} + + +bool Expression::greaterEqual(const Variant& left, const Variant& right) const +{ + switch (left.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + switch (right.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + return left.asInt64() >= right.asInt64(); + case VAR_STRING: + try { + return left.asInt64() >= right.asInt64(); + } catch (std::exception&) {} + break; + default: + break; + } + break; + + case VAR_FLOAT: case VAR_DOUBLE: + switch (right.getType()) { + case VAR_FLOAT: case VAR_DOUBLE: + return left.asDouble() >= right.asDouble(); + case VAR_STRING: + try { + return left.asDouble() >= right.asDouble(); + } catch (std::exception&) {} + break; + default: + break; + } + break; + + case VAR_STRING: + switch (right.getType()) { + case VAR_UINT8: case VAR_UINT16: case VAR_UINT32: case VAR_UINT64: + case VAR_INT8: case VAR_INT16: case VAR_INT32: case VAR_INT64: + try { + return left.asInt64() >= right.asInt64(); + } catch (std::exception&) {} + break; + + case VAR_FLOAT: case VAR_DOUBLE: + try { + return left.asDouble() >= right.asDouble(); + } catch (std::exception&) {} + break; + + case VAR_STRING: + return left.asString() >= right.asString(); + default: + break; + } + default: + break; + } + + return false; +} + + diff --git a/qpid/cpp/src/qmf/Expression.h b/qpid/cpp/src/qmf/Expression.h new file mode 100644 index 0000000000..6fbfdbc4ba --- /dev/null +++ b/qpid/cpp/src/qmf/Expression.h @@ -0,0 +1,73 @@ +#ifndef _QMF_EXPRESSION_H_ +#define _QMF_EXPRESSION_H_ +/* + * + * 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 "qpid/types/Variant.h" +#include <string> +#include <list> +#include <boost/shared_ptr.hpp> + +namespace qmf { + + enum LogicalOp { + LOGICAL_ID = 1, + LOGICAL_NOT = 2, + LOGICAL_AND = 3, + LOGICAL_OR = 4 + }; + + enum BooleanOp { + BOOL_EQ = 1, + BOOL_NE = 2, + BOOL_LT = 3, + BOOL_LE = 4, + BOOL_GT = 5, + BOOL_GE = 6, + BOOL_RE_MATCH = 7, + BOOL_EXISTS = 8, + BOOL_TRUE = 9, + BOOL_FALSE = 10 + }; + + class Expression { + public: + Expression(const qpid::types::Variant::List& expr); + bool evaluate(const qpid::types::Variant::Map& data) const; + private: + LogicalOp logicalOp; + BooleanOp boolOp; + int operandCount; + qpid::types::Variant operands[2]; + bool quoted[2]; + std::list<boost::shared_ptr<Expression> > expressionList; + + bool boolEval(const qpid::types::Variant::Map& data) const; + bool lessThan(const qpid::types::Variant& left, const qpid::types::Variant& right) const; + bool lessEqual(const qpid::types::Variant& left, const qpid::types::Variant& right) const; + bool greaterThan(const qpid::types::Variant& left, const qpid::types::Variant& right) const; + bool greaterEqual(const qpid::types::Variant& left, const qpid::types::Variant& right) const; + }; + +} + +#endif + diff --git a/qpid/cpp/src/qmf/Hash.cpp b/qpid/cpp/src/qmf/Hash.cpp new file mode 100644 index 0000000000..86738dda2f --- /dev/null +++ b/qpid/cpp/src/qmf/Hash.cpp @@ -0,0 +1,45 @@ +/* + * + * 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 "qmf/Hash.h" + +using namespace qmf; + +Hash::Hash() +{ + data[0] = 0x5A5A5A5A5A5A5A5ALL; + data[1] = 0x5A5A5A5A5A5A5A5ALL; +} + +void Hash::update(const char* s, uint32_t len) +{ + uint64_t* first = &data[0]; + uint64_t* second = &data[1]; + + for (uint32_t idx = 0; idx < len; idx++) { + uint64_t recycle = ((*second & 0xff00000000000000LL) >> 56); + *second = *second << 8; + *second |= ((*first & 0xFF00000000000000LL) >> 56); + *first = *first << 8; + *first = *first + (uint64_t) s[idx] + recycle; + } +} + diff --git a/qpid/cpp/src/qmf/Hash.h b/qpid/cpp/src/qmf/Hash.h new file mode 100644 index 0000000000..e1eff84117 --- /dev/null +++ b/qpid/cpp/src/qmf/Hash.h @@ -0,0 +1,44 @@ +#ifndef QMF_HASH_H +#define QMF_HASH_H +/* + * + * 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 "qpid/sys/IntegerTypes.h" +#include "qpid/types/Uuid.h" +#include <string> + +namespace qmf { + class Hash { + public: + Hash(); + qpid::types::Uuid asUuid() const { return qpid::types::Uuid((unsigned char*) data); } + void update(const char* s, uint32_t len); + void update(uint8_t v) { update((char*) &v, sizeof(v)); } + void update(uint32_t v) { update((char*) &v, sizeof(v)); } + void update(const std::string& v) { update(const_cast<char*>(v.c_str()), v.size()); } + void update(bool v) { update(uint8_t(v ? 1 : 0)); } + + private: + uint64_t data[2]; + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/PrivateImplRef.h b/qpid/cpp/src/qmf/PrivateImplRef.h new file mode 100644 index 0000000000..960cbb2e09 --- /dev/null +++ b/qpid/cpp/src/qmf/PrivateImplRef.h @@ -0,0 +1,93 @@ +#ifndef QMF_PRIVATEIMPL_H +#define QMF_PRIVATEIMPL_H + +/* + * + * 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 "qmf/ImportExport.h" +#include "qpid/RefCounted.h" +#include <boost/intrusive_ptr.hpp> + +namespace qmf { + +/** + * Helper class to implement a class with a private, reference counted + * implementation and reference semantics. + * + * Such classes are used in the public API to hide implementation, they + * should. Example of use: + * + * === Foo.h + * + * template <class T> PrivateImplRef; + * class FooImpl; + * + * Foo : public Handle<FooImpl> { + * public: + * Foo(FooImpl* = 0); + * Foo(const Foo&); + * ~Foo(); + * Foo& operator=(const Foo&); + * + * int fooDo(); // and other Foo functions... + * + * private: + * typedef FooImpl Impl; + * Impl* impl; + * friend class PrivateImplRef<Foo>; + * + * === Foo.cpp + * + * typedef PrivateImplRef<Foo> PI; + * Foo::Foo(FooImpl* p) { PI::ctor(*this, p); } + * Foo::Foo(const Foo& c) : Handle<FooImpl>() { PI::copy(*this, c); } + * Foo::~Foo() { PI::dtor(*this); } + * Foo& Foo::operator=(const Foo& c) { return PI::assign(*this, c); } + * + * int foo::fooDo() { return impl->fooDo(); } + * + */ +template <class T> class PrivateImplRef { + public: + typedef typename T::Impl Impl; + typedef boost::intrusive_ptr<Impl> intrusive_ptr; + + /** Get the implementation pointer from a handle */ + static intrusive_ptr get(const T& t) { return intrusive_ptr(t.impl); } + + /** Set the implementation pointer in a handle */ + static void set(T& t, const intrusive_ptr& p) { + if (t.impl == p) return; + if (t.impl) boost::intrusive_ptr_release(t.impl); + t.impl = p.get(); + if (t.impl) boost::intrusive_ptr_add_ref(t.impl); + } + + // Helper functions to implement the ctor, dtor, copy, assign + static void ctor(T& t, Impl* p) { t.impl = p; if (p) boost::intrusive_ptr_add_ref(p); } + static void copy(T& t, const T& x) { if (&t == &x) return; t.impl = 0; assign(t, x); } + static void dtor(T& t) { if(t.impl) boost::intrusive_ptr_release(t.impl); } + static T& assign(T& t, const T& x) { set(t, get(x)); return t;} +}; + +} // namespace qmf + +#endif /*!QMF_PRIVATEIMPL_H*/ diff --git a/qpid/cpp/src/qmf/Query.cpp b/qpid/cpp/src/qmf/Query.cpp new file mode 100644 index 0000000000..ee8ca38e59 --- /dev/null +++ b/qpid/cpp/src/qmf/Query.cpp @@ -0,0 +1,153 @@ +/* + * + * 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 "qmf/PrivateImplRef.h" +#include "qmf/exceptions.h" +#include "qmf/QueryImpl.h" +#include "qmf/DataAddrImpl.h" +#include "qmf/SchemaIdImpl.h" +#include "qpid/messaging/AddressParser.h" + +using namespace std; +using namespace qmf; +using qpid::types::Variant; + +typedef PrivateImplRef<Query> PI; + +Query::Query(QueryImpl* impl) { PI::ctor(*this, impl); } +Query::Query(const Query& s) : qmf::Handle<QueryImpl>() { PI::copy(*this, s); } +Query::~Query() { PI::dtor(*this); } +Query& Query::operator=(const Query& s) { return PI::assign(*this, s); } + +Query::Query(QueryTarget t, const string& pr) { PI::ctor(*this, new QueryImpl(t, pr)); } +Query::Query(QueryTarget t, const string& c, const string& p, const string& pr) { PI::ctor(*this, new QueryImpl(t, c, p, pr)); } +Query::Query(QueryTarget t, const SchemaId& s, const string& pr) { PI::ctor(*this, new QueryImpl(t, s, pr)); } +Query::Query(const DataAddr& a) { PI::ctor(*this, new QueryImpl(a)); } + +QueryTarget Query::getTarget() const { return impl->getTarget(); } +const DataAddr& Query::getDataAddr() const { return impl->getDataAddr(); } +const SchemaId& Query::getSchemaId() const { return impl->getSchemaId(); } +void Query::setPredicate(const Variant::List& pr) { impl->setPredicate(pr); } +const Variant::List& Query::getPredicate() const { return impl->getPredicate(); } +bool Query::matchesPredicate(const qpid::types::Variant::Map& map) const { return impl->matchesPredicate(map); } + + +QueryImpl::QueryImpl(const Variant::Map& map) : predicateCompiled(false) +{ + Variant::Map::const_iterator iter; + + iter = map.find("_what"); + if (iter == map.end()) + throw QmfException("Query missing _what element"); + + const string& targetString(iter->second.asString()); + if (targetString == "OBJECT") target = QUERY_OBJECT; + else if (targetString == "OBJECT_ID") target = QUERY_OBJECT_ID; + else if (targetString == "SCHEMA") target = QUERY_SCHEMA; + else if (targetString == "SCHEMA_ID") target = QUERY_SCHEMA_ID; + else + throw QmfException("Query with invalid _what value: " + targetString); + + iter = map.find("_object_id"); + if (iter != map.end()) { + auto_ptr<DataAddrImpl> addrImpl(new DataAddrImpl(iter->second.asMap())); + dataAddr = DataAddr(addrImpl.release()); + } + + iter = map.find("_schema_id"); + if (iter != map.end()) { + auto_ptr<SchemaIdImpl> sidImpl(new SchemaIdImpl(iter->second.asMap())); + schemaId = SchemaId(sidImpl.release()); + } + + iter = map.find("_where"); + if (iter != map.end()) + predicate = iter->second.asList(); +} + + +Variant::Map QueryImpl::asMap() const +{ + Variant::Map map; + string targetString; + + switch (target) { + case QUERY_OBJECT : targetString = "OBJECT"; break; + case QUERY_OBJECT_ID : targetString = "OBJECT_ID"; break; + case QUERY_SCHEMA : targetString = "SCHEMA"; break; + case QUERY_SCHEMA_ID : targetString = "SCHEMA_ID"; break; + } + + map["_what"] = targetString; + + if (dataAddr.isValid()) + map["_object_id"] = DataAddrImplAccess::get(dataAddr).asMap(); + + if (schemaId.isValid()) + map["_schema_id"] = SchemaIdImplAccess::get(schemaId).asMap(); + + if (!predicate.empty()) + map["_where"] = predicate; + + return map; +} + + +bool QueryImpl::matchesPredicate(const qpid::types::Variant::Map& data) const +{ + if (predicate.empty()) + return true; + + if (!predicateCompiled) { + expression.reset(new Expression(predicate)); + predicateCompiled = true; + } + + return expression->evaluate(data); +} + + +void QueryImpl::parsePredicate(const string& pred) +{ + if (pred.empty()) + return; + + if (pred[0] == '[') { + // + // Parse this as an AddressParser list. + // + qpid::messaging::AddressParser parser(pred); + parser.parseList(predicate); + } else + throw QmfException("Invalid predicate format"); +} + + +QueryImpl& QueryImplAccess::get(Query& item) +{ + return *item.impl; +} + + +const QueryImpl& QueryImplAccess::get(const Query& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/QueryImpl.h b/qpid/cpp/src/qmf/QueryImpl.h new file mode 100644 index 0000000000..27ec427684 --- /dev/null +++ b/qpid/cpp/src/qmf/QueryImpl.h @@ -0,0 +1,77 @@ +#ifndef _QMF_QUERY_IMPL_H_ +#define _QMF_QUERY_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/Query.h" +#include "qmf/DataAddr.h" +#include "qmf/SchemaId.h" +#include "qmf/Expression.h" +#include "qpid/types/Variant.h" +#include <boost/shared_ptr.hpp> + +namespace qmf { + class QueryImpl : public virtual qpid::RefCounted { + public: + // + // Public impl-only methods + // + QueryImpl(const qpid::types::Variant::Map&); + qpid::types::Variant::Map asMap() const; + + // + // Methods from API handle + // + QueryImpl(QueryTarget t, const std::string& pr) : target(t), predicateCompiled(false) { parsePredicate(pr); } + QueryImpl(QueryTarget t, const std::string& c, const std::string& p, const std::string& pr) : + target(t), schemaId(SCHEMA_TYPE_DATA, p, c), predicateCompiled(false) { parsePredicate(pr); } + QueryImpl(QueryTarget t, const SchemaId& s, const std::string& pr) : + target(t), schemaId(s), predicateCompiled(false) { parsePredicate(pr); } + QueryImpl(const DataAddr& a) : target(QUERY_OBJECT), dataAddr(a), predicateCompiled(false) {} + + QueryTarget getTarget() const { return target; } + const DataAddr& getDataAddr() const { return dataAddr; } + const SchemaId& getSchemaId() const { return schemaId; } + void setPredicate(const qpid::types::Variant::List& pr) { predicate = pr; } + const qpid::types::Variant::List& getPredicate() const { return predicate; } + bool matchesPredicate(const qpid::types::Variant::Map& map) const; + + private: + QueryTarget target; + SchemaId schemaId; + DataAddr dataAddr; + qpid::types::Variant::List predicate; + mutable bool predicateCompiled; + mutable boost::shared_ptr<Expression> expression; + + void parsePredicate(const std::string& s); + }; + + struct QueryImplAccess + { + static QueryImpl& get(Query&); + static const QueryImpl& get(const Query&); + }; +} + +#endif + diff --git a/qpid/cpp/src/qmf/Schema.cpp b/qpid/cpp/src/qmf/Schema.cpp new file mode 100644 index 0000000000..872aad724c --- /dev/null +++ b/qpid/cpp/src/qmf/Schema.cpp @@ -0,0 +1,358 @@ +/* + * + * 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 "qmf/SchemaImpl.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/exceptions.h" +#include "qmf/SchemaTypes.h" +#include "qmf/SchemaIdImpl.h" +#include "qmf/SchemaPropertyImpl.h" +#include "qmf/SchemaMethodImpl.h" +#include "qmf/Hash.h" +#include "qpid/log/Statement.h" +#include "qpid/management/Buffer.h" +#include <list> + +using namespace std; +using qpid::types::Variant; +using namespace qmf; + +typedef PrivateImplRef<Schema> PI; + +Schema::Schema(SchemaImpl* impl) { PI::ctor(*this, impl); } +Schema::Schema(const Schema& s) : qmf::Handle<SchemaImpl>() { PI::copy(*this, s); } +Schema::~Schema() { PI::dtor(*this); } +Schema& Schema::operator=(const Schema& s) { return PI::assign(*this, s); } + +Schema::Schema(int t, const string& p, const string& c) { PI::ctor(*this, new SchemaImpl(t, p, c)); } +const SchemaId& Schema::getSchemaId() const { return impl->getSchemaId(); } +void Schema::finalize() { impl->finalize(); } +bool Schema::isFinalized() const { return impl->isFinalized(); } +void Schema::addProperty(const SchemaProperty& p) { impl->addProperty(p); } +void Schema::addMethod(const SchemaMethod& m) { impl->addMethod(m); } +void Schema::setDesc(const string& d) { impl->setDesc(d); } +const string& Schema::getDesc() const { return impl->getDesc(); } +void Schema::setDefaultSeverity(int s) { impl->setDefaultSeverity(s); } +int Schema::getDefaultSeverity() const { return impl->getDefaultSeverity(); } +uint32_t Schema::getPropertyCount() const { return impl->getPropertyCount(); } +SchemaProperty Schema::getProperty(uint32_t i) const { return impl->getProperty(i); } +uint32_t Schema::getMethodCount() const { return impl->getMethodCount(); } +SchemaMethod Schema::getMethod(uint32_t i) const { return impl->getMethod(i); } + +//======================================================================================== +// Impl Method Bodies +//======================================================================================== + +SchemaImpl::SchemaImpl(const Variant::Map& map) : finalized(false) +{ + Variant::Map::const_iterator iter; + Variant::List::const_iterator lIter; + + iter = map.find("_schema_id"); + if (iter == map.end()) + throw QmfException("Schema map missing _schema_id element"); + schemaId = SchemaId(new SchemaIdImpl(iter->second.asMap())); + + iter = map.find("_desc"); + if (iter != map.end()) + description = iter->second.asString(); + + iter = map.find("_default_severity"); + if (iter != map.end()) + defaultSeverity = int(iter->second.asUint32()); + + iter = map.find("_properties"); + if (iter != map.end()) { + const Variant::List& props(iter->second.asList()); + for (lIter = props.begin(); lIter != props.end(); lIter++) + addProperty(SchemaProperty(new SchemaPropertyImpl(lIter->asMap()))); + } + + iter = map.find("_methods"); + if (iter != map.end()) { + const Variant::List& meths(iter->second.asList()); + for (lIter = meths.begin(); lIter != meths.end(); lIter++) + addMethod(SchemaMethod(new SchemaMethodImpl(lIter->asMap()))); + } + + finalized = true; +} + + +Variant::Map SchemaImpl::asMap() const +{ + Variant::Map map; + Variant::List propList; + Variant::List methList; + + checkNotFinal(); + + map["_schema_id"] = SchemaIdImplAccess::get(schemaId).asMap(); + if (!description.empty()) + map["_desc"] = description; + if (schemaId.getType() == SCHEMA_TYPE_EVENT) + map["_default_severity"] = uint32_t(defaultSeverity); + + for (list<SchemaProperty>::const_iterator pIter = properties.begin(); pIter != properties.end(); pIter++) + propList.push_back(SchemaPropertyImplAccess::get(*pIter).asMap()); + + for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) + methList.push_back(SchemaMethodImplAccess::get(*mIter).asMap()); + + map["_properties"] = propList; + map["_methods"] = methList; + return map; +} + + +SchemaImpl::SchemaImpl(qpid::management::Buffer& buffer) : finalized(false) +{ + int schemaType; + string packageName; + string className; + uint8_t hash[16]; + + schemaType = int(buffer.getOctet()); + buffer.getShortString(packageName); + buffer.getShortString(className); + buffer.getBin128(hash); + schemaId = SchemaId(schemaType, packageName, className); + schemaId.setHash(qpid::types::Uuid(hash)); + + if (schemaType == SCHEMA_TYPE_DATA) { + uint16_t propCount(buffer.getShort()); + uint16_t statCount(buffer.getShort()); + uint16_t methCount(buffer.getShort()); + for (uint16_t idx = 0; idx < propCount + statCount; idx++) + addProperty(new SchemaPropertyImpl(buffer)); + for (uint16_t idx = 0; idx < methCount; idx++) + addMethod(new SchemaMethodImpl(buffer)); + } + + finalized = true; +} + + +string SchemaImpl::asV1Content(uint32_t sequence) const +{ +#define RAW_BUF_SIZE 65536 + char rawBuf[RAW_BUF_SIZE]; + qpid::management::Buffer buffer(rawBuf, RAW_BUF_SIZE); + + // + // Encode the QMFv1 Header + // + buffer.putOctet('A'); + buffer.putOctet('M'); + buffer.putOctet('2'); + buffer.putOctet('s'); + buffer.putLong(sequence); + + // + // Encode the common schema information + // + buffer.putOctet(uint8_t(schemaId.getType())); + buffer.putShortString(schemaId.getPackageName()); + buffer.putShortString(schemaId.getName()); + buffer.putBin128(schemaId.getHash().data()); + + if (schemaId.getType() == SCHEMA_TYPE_DATA) { + buffer.putShort(properties.size()); + buffer.putShort(0); + buffer.putShort(methods.size()); + for (list<SchemaProperty>::const_iterator pIter = properties.begin(); pIter != properties.end(); pIter++) + SchemaPropertyImplAccess::get(*pIter).encodeV1(buffer, false, false); + for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) + SchemaMethodImplAccess::get(*mIter).encodeV1(buffer); + } else { + buffer.putShort(properties.size()); + for (list<SchemaProperty>::const_iterator pIter = properties.begin(); pIter != properties.end(); pIter++) + SchemaPropertyImplAccess::get(*pIter).encodeV1(buffer, true, false); + } + + return string(rawBuf, buffer.getPosition()); +} + + +bool SchemaImpl::isValidProperty(const std::string& k, const Variant& v) const +{ + for (list<SchemaProperty>::const_iterator iter = properties.begin(); iter != properties.end(); iter++) + if (iter->getName() == k) + return (isCompatibleType(iter->getType(), v.getType())); + return false; +} + + +bool SchemaImpl::isValidMethodInArg(const std::string& m, const std::string& k, const Variant& v) const +{ + for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) { + if (mIter->getName() == m) { + uint32_t count(mIter->getArgumentCount()); + for (uint32_t i = 0; i < count; i++) { + const SchemaProperty prop(mIter->getArgument(i)); + if (prop.getName() == k) { + if (prop.getDirection() == DIR_IN || prop.getDirection() == DIR_IN_OUT) + return (isCompatibleType(prop.getType(), v.getType())); + else + return false; + } + } + } + } + return false; +} + + +bool SchemaImpl::isValidMethodOutArg(const std::string& m, const std::string& k, const Variant& v) const +{ + for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) { + if (mIter->getName() == m) { + uint32_t count(mIter->getArgumentCount()); + for (uint32_t i = 0; i < count; i++) { + const SchemaProperty prop(mIter->getArgument(i)); + if (prop.getName() == k) { + if (prop.getDirection() == DIR_OUT || prop.getDirection() == DIR_IN_OUT) + return (isCompatibleType(prop.getType(), v.getType())); + else + return false; + } + } + } + } + return false; +} + + +void SchemaImpl::finalize() +{ + Hash hash; + + hash.update((uint8_t) schemaId.getType()); + hash.update(schemaId.getPackageName()); + hash.update(schemaId.getName()); + + for (list<SchemaProperty>::const_iterator pIter = properties.begin(); pIter != properties.end(); pIter++) + SchemaPropertyImplAccess::get(*pIter).updateHash(hash); + for (list<SchemaMethod>::const_iterator mIter = methods.begin(); mIter != methods.end(); mIter++) + SchemaMethodImplAccess::get(*mIter).updateHash(hash); + + schemaId.setHash(hash.asUuid()); + QPID_LOG(debug, "Schema Finalized: " << schemaId.getPackageName() << ":" << schemaId.getName() << ":" << + schemaId.getHash()); + + finalized = true; +} + + +SchemaProperty SchemaImpl::getProperty(uint32_t i) const +{ + uint32_t count = 0; + for (list<SchemaProperty>::const_iterator iter = properties.begin(); iter != properties.end(); iter++) + if (count++ == i) + return *iter; + throw IndexOutOfRange(); +} + + +SchemaMethod SchemaImpl::getMethod(uint32_t i) const +{ + uint32_t count = 0; + for (list<SchemaMethod>::const_iterator iter = methods.begin(); iter != methods.end(); iter++) + if (count++ == i) + return *iter; + throw IndexOutOfRange(); +} + +void SchemaImpl::checkFinal() const +{ + if (finalized) + throw QmfException("Modification of a finalized schema is forbidden"); +} + + +void SchemaImpl::checkNotFinal() const +{ + if (!finalized) + throw QmfException("Schema is not yet finalized/registered"); +} + + +bool SchemaImpl::isCompatibleType(int qmfType, qpid::types::VariantType qpidType) const +{ + bool typeValid(false); + + switch (qpidType) { + case qpid::types::VAR_VOID: + if (qmfType == SCHEMA_DATA_VOID) + typeValid = true; + break; + case qpid::types::VAR_BOOL: + if (qmfType == SCHEMA_DATA_BOOL) + typeValid = true; + break; + case qpid::types::VAR_UINT8: + case qpid::types::VAR_UINT16: + case qpid::types::VAR_UINT32: + case qpid::types::VAR_UINT64: + case qpid::types::VAR_INT8: + case qpid::types::VAR_INT16: + case qpid::types::VAR_INT32: + case qpid::types::VAR_INT64: + if (qmfType == SCHEMA_DATA_INT) + typeValid = true; + break; + case qpid::types::VAR_FLOAT: + case qpid::types::VAR_DOUBLE: + if (qmfType == SCHEMA_DATA_FLOAT) + typeValid = true; + break; + case qpid::types::VAR_STRING: + if (qmfType == SCHEMA_DATA_STRING) + typeValid = true; + break; + case qpid::types::VAR_MAP: + if (qmfType == SCHEMA_DATA_MAP) + typeValid = true; + break; + case qpid::types::VAR_LIST: + if (qmfType == SCHEMA_DATA_LIST) + typeValid = true; + break; + case qpid::types::VAR_UUID: + if (qmfType == SCHEMA_DATA_UUID) + typeValid = true; + break; + } + + return typeValid; +} + + +SchemaImpl& SchemaImplAccess::get(Schema& item) +{ + return *item.impl; +} + + +const SchemaImpl& SchemaImplAccess::get(const Schema& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/SchemaCache.cpp b/qpid/cpp/src/qmf/SchemaCache.cpp new file mode 100644 index 0000000000..74ca4044fd --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaCache.cpp @@ -0,0 +1,91 @@ +/* + * + * 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 "qmf/SchemaCache.h" +#include "qmf/exceptions.h" + +using namespace std; +using namespace qmf; + +bool SchemaCache::declareSchemaId(const SchemaId& id) +{ + qpid::sys::Mutex::ScopedLock l(lock); + SchemaMap::const_iterator iter = schemata.find(id); + if (iter == schemata.end()) { + schemata[id] = Schema(); + return false; + } + return true; +} + + +void SchemaCache::declareSchema(const Schema& schema) +{ + qpid::sys::Mutex::ScopedLock l(lock); + SchemaMap::const_iterator iter = schemata.find(schema.getSchemaId()); + if (iter == schemata.end() || !iter->second.isValid()) { + schemata[schema.getSchemaId()] = schema; + + // + // If there are any threads blocking in SchemaCache::getSchema waiting for + // this schema, unblock them all now. + // + CondMap::iterator cIter = conditions.find(schema.getSchemaId()); + if (cIter != conditions.end()) + cIter->second->notifyAll(); + } +} + + +bool SchemaCache::haveSchema(const SchemaId& id) const +{ + qpid::sys::Mutex::ScopedLock l(lock); + SchemaMap::const_iterator iter = schemata.find(id); + return iter != schemata.end() && iter->second.isValid(); +} + + +const Schema& SchemaCache::getSchema(const SchemaId& id, qpid::messaging::Duration timeout) const +{ + qpid::sys::Mutex::ScopedLock l(lock); + SchemaMap::const_iterator iter = schemata.find(id); + if (iter != schemata.end() && iter->second.isValid()) + return iter->second; + + // + // The desired schema is not in the cache. Assume that the caller knows this and has + // sent a schema request to the remote agent and now wishes to wait until the schema + // information arrives. + // + CondMap::iterator cIter = conditions.find(id); + if (cIter == conditions.end()) + conditions[id] = boost::shared_ptr<qpid::sys::Condition>(new qpid::sys::Condition()); + + uint64_t milliseconds = timeout.getMilliseconds(); + conditions[id]->wait(lock, qpid::sys::AbsTime(qpid::sys::now(), + qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC))); + iter = schemata.find(id); + if (iter != schemata.end() && iter->second.isValid()) + return iter->second; + + throw QmfException("Schema lookup timed out"); +} + diff --git a/qpid/cpp/src/qmf/SchemaCache.h b/qpid/cpp/src/qmf/SchemaCache.h new file mode 100644 index 0000000000..a1f104233f --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaCache.h @@ -0,0 +1,56 @@ +#ifndef QMF_SCHEMA_CACHE_H +#define QMF_SCHEMA_CACHE_H +/* + * + * 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 "qmf/SchemaIdImpl.h" +#include "qmf/Schema.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Condition.h" +#include "qpid/messaging/Duration.h" +#include <string> +#include <map> +#include <boost/shared_ptr.hpp> + +namespace qmf { + + class SchemaCache { + public: + SchemaCache() {} + ~SchemaCache() {} + + bool declareSchemaId(const SchemaId&); + void declareSchema(const Schema&); + bool haveSchema(const SchemaId&) const; + const Schema& getSchema(const SchemaId&, qpid::messaging::Duration) const; + + private: + mutable qpid::sys::Mutex lock; + typedef std::map<SchemaId, Schema, SchemaIdCompare> SchemaMap; + typedef std::map<SchemaId, boost::shared_ptr<qpid::sys::Condition>, SchemaIdCompare> CondMap; + SchemaMap schemata; + mutable CondMap conditions; + }; + +} + +#endif + diff --git a/qpid/cpp/src/qmf/SchemaId.cpp b/qpid/cpp/src/qmf/SchemaId.cpp new file mode 100644 index 0000000000..25fa9915ae --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaId.cpp @@ -0,0 +1,96 @@ +/* + * + * 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 "qmf/SchemaIdImpl.h" +#include "qmf/PrivateImplRef.h" + +using namespace std; +using namespace qmf; +using qpid::types::Variant; + +typedef PrivateImplRef<SchemaId> PI; + +SchemaId::SchemaId(SchemaIdImpl* impl) { PI::ctor(*this, impl); } +SchemaId::SchemaId(const SchemaId& s) : qmf::Handle<SchemaIdImpl>() { PI::copy(*this, s); } +SchemaId::~SchemaId() { PI::dtor(*this); } +SchemaId& SchemaId::operator=(const SchemaId& s) { return PI::assign(*this, s); } + +SchemaId::SchemaId(int t, const string& p, const string& n) { PI::ctor(*this, new SchemaIdImpl(t, p, n)); } +void SchemaId::setHash(const qpid::types::Uuid& h) { impl->setHash(h); } +int SchemaId::getType() const { return impl->getType(); } +const string& SchemaId::getPackageName() const { return impl->getPackageName(); } +const string& SchemaId::getName() const { return impl->getName(); } +const qpid::types::Uuid& SchemaId::getHash() const { return impl->getHash(); } + + +SchemaIdImpl::SchemaIdImpl(const Variant::Map& map) +{ + Variant::Map::const_iterator iter; + + iter = map.find("_package_name"); + if (iter != map.end()) + package = iter->second.asString(); + + iter = map.find("_class_name"); + if (iter != map.end()) + name = iter->second.asString(); + + iter = map.find("_type"); + if (iter != map.end()) { + const string& stype = iter->second.asString(); + if (stype == "_data") + sType = SCHEMA_TYPE_DATA; + else if (stype == "_event") + sType = SCHEMA_TYPE_EVENT; + } + + iter = map.find("_hash"); + if (iter != map.end()) + hash = iter->second.asUuid(); +} + + +Variant::Map SchemaIdImpl::asMap() const +{ + Variant::Map result; + + result["_package_name"] = package; + result["_class_name"] = name; + if (sType == SCHEMA_TYPE_DATA) + result["_type"] = "_data"; + else + result["_type"] = "_event"; + if (!hash.isNull()) + result["_hash"] = hash; + return result; +} + + +SchemaIdImpl& SchemaIdImplAccess::get(SchemaId& item) +{ + return *item.impl; +} + + +const SchemaIdImpl& SchemaIdImplAccess::get(const SchemaId& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/SchemaIdImpl.h b/qpid/cpp/src/qmf/SchemaIdImpl.h new file mode 100644 index 0000000000..ae1a3d8d3b --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaIdImpl.h @@ -0,0 +1,83 @@ +#ifndef _QMF_SCHEMA_ID_IMPL_H_ +#define _QMF_SCHEMA_ID_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/SchemaId.h" +#include "qpid/types/Variant.h" +#include "qpid/types/Uuid.h" +#include <string> + +namespace qmf { + class SchemaIdImpl : public virtual qpid::RefCounted { + public: + // + // Public impl-only methods + // + SchemaIdImpl(const qpid::types::Variant::Map&); + qpid::types::Variant::Map asMap() const; + + // + // Methods from API handle + // + SchemaIdImpl(int t, const std::string& p, const std::string& n) : sType(t), package(p), name(n) {} + void setHash(const qpid::types::Uuid& h) { hash = h; } + int getType() const { return sType; } + const std::string& getPackageName() const { return package; } + const std::string& getName() const { return name; } + const qpid::types::Uuid& getHash() const { return hash; } + + private: + int sType; + std::string package; + std::string name; + qpid::types::Uuid hash; + }; + + struct SchemaIdImplAccess + { + static SchemaIdImpl& get(SchemaId&); + static const SchemaIdImpl& get(const SchemaId&); + }; + + struct SchemaIdCompare { + bool operator() (const SchemaId& lhs, const SchemaId& rhs) const + { + if (lhs.getName() != rhs.getName()) + return lhs.getName() < rhs.getName(); + if (lhs.getPackageName() != rhs.getPackageName()) + return lhs.getPackageName() < rhs.getPackageName(); + return lhs.getHash() < rhs.getHash(); + } + }; + + struct SchemaIdCompareNoHash { + bool operator() (const SchemaId& lhs, const SchemaId& rhs) const + { + if (lhs.getName() != rhs.getName()) + return lhs.getName() < rhs.getName(); + return lhs.getPackageName() < rhs.getPackageName(); + } + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/SchemaImpl.h b/qpid/cpp/src/qmf/SchemaImpl.h new file mode 100644 index 0000000000..1c88f87808 --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaImpl.h @@ -0,0 +1,95 @@ +#ifndef _QMF_SCHEMAIMPL_H_ +#define _QMF_SCHEMAIMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/exceptions.h" +#include "qmf/SchemaTypes.h" +#include "qmf/SchemaId.h" +#include "qmf/Schema.h" +#include "qmf/SchemaProperty.h" +#include "qmf/SchemaMethod.h" +#include <list> + +namespace qpid { +namespace management { + class Buffer; +}} + +namespace qmf { + class SchemaImpl : public virtual qpid::RefCounted { + public: + // + // Impl-only public methods + // + SchemaImpl(const qpid::types::Variant::Map& m); + qpid::types::Variant::Map asMap() const; + SchemaImpl(qpid::management::Buffer& v1Buffer); + std::string asV1Content(uint32_t sequence) const; + bool isValidProperty(const std::string& k, const qpid::types::Variant& v) const; + bool isValidMethodInArg(const std::string& m, const std::string& k, const qpid::types::Variant& v) const; + bool isValidMethodOutArg(const std::string& m, const std::string& k, const qpid::types::Variant& v) const; + + // + // Methods from API handle + // + SchemaImpl(int t, const std::string& p, const std::string& c) : schemaId(t, p, c), finalized(false) {} + const SchemaId& getSchemaId() const { checkNotFinal(); return schemaId; } + + void finalize(); + bool isFinalized() const { return finalized; } + void addProperty(const SchemaProperty& p) { checkFinal(); properties.push_back(p); } + void addMethod(const SchemaMethod& m) { checkFinal(); methods.push_back(m); } + + void setDesc(const std::string& d) { description = d; } + const std::string& getDesc() const { return description; } + + void setDefaultSeverity(int s) { checkFinal(); defaultSeverity = s; } + int getDefaultSeverity() const { return defaultSeverity; } + + uint32_t getPropertyCount() const { return properties.size(); } + SchemaProperty getProperty(uint32_t i) const; + + uint32_t getMethodCount() const { return methods.size(); } + SchemaMethod getMethod(uint32_t i) const; + private: + SchemaId schemaId; + int defaultSeverity; + std::string description; + bool finalized; + std::list<SchemaProperty> properties; + std::list<SchemaMethod> methods; + + void checkFinal() const; + void checkNotFinal() const; + bool isCompatibleType(int qmfType, qpid::types::VariantType qpidType) const; + }; + + struct SchemaImplAccess + { + static SchemaImpl& get(Schema&); + static const SchemaImpl& get(const Schema&); + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/SchemaMethod.cpp b/qpid/cpp/src/qmf/SchemaMethod.cpp new file mode 100644 index 0000000000..e267878238 --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaMethod.cpp @@ -0,0 +1,186 @@ +/* + * + * 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 "qmf/SchemaMethodImpl.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/exceptions.h" +#include "qmf/Hash.h" +#include "qpid/messaging/AddressParser.h" +#include "qpid/management/Buffer.h" + +using namespace std; +using qpid::types::Variant; +using namespace qmf; + +typedef PrivateImplRef<SchemaMethod> PI; + +SchemaMethod::SchemaMethod(SchemaMethodImpl* impl) { PI::ctor(*this, impl); } +SchemaMethod::SchemaMethod(const SchemaMethod& s) : qmf::Handle<SchemaMethodImpl>() { PI::copy(*this, s); } +SchemaMethod::~SchemaMethod() { PI::dtor(*this); } +SchemaMethod& SchemaMethod::operator=(const SchemaMethod& s) { return PI::assign(*this, s); } + +SchemaMethod::SchemaMethod(const string& n, const string& o) { PI::ctor(*this, new SchemaMethodImpl(n, o)); } +void SchemaMethod::setDesc(const string& d) { impl->setDesc(d); } +void SchemaMethod::addArgument(const SchemaProperty& p) { impl->addArgument(p); } +const string& SchemaMethod::getName() const { return impl->getName(); } +const string& SchemaMethod::getDesc() const { return impl->getDesc(); } +uint32_t SchemaMethod::getArgumentCount() const { return impl->getArgumentCount(); } +SchemaProperty SchemaMethod::getArgument(uint32_t i) const { return impl->getArgument(i); } + +//======================================================================================== +// Impl Method Bodies +//======================================================================================== + +SchemaMethodImpl::SchemaMethodImpl(const string& n, const string& options) : name(n) +{ + if (!options.empty()) { + qpid::messaging::AddressParser parser = qpid::messaging::AddressParser(options); + Variant::Map optMap; + Variant::Map::iterator iter; + + parser.parseMap(optMap); + iter = optMap.find("desc"); + if (iter != optMap.end()) { + desc = iter->second.asString(); + optMap.erase(iter); + } + + if (!optMap.empty()) + throw QmfException("Unrecognized option: " + optMap.begin()->first); + } +} + + +SchemaMethodImpl::SchemaMethodImpl(const qpid::types::Variant::Map& map) +{ + Variant::Map::const_iterator iter; + Variant::List::const_iterator lIter; + + iter = map.find("_name"); + if (iter == map.end()) + throw QmfException("SchemaMethod without a _name element"); + name = iter->second.asString(); + + iter = map.find("_desc"); + if (iter != map.end()) + desc = iter->second.asString(); + + iter = map.find("_arguments"); + if (iter != map.end()) { + const Variant::List& argList(iter->second.asList()); + for (lIter = argList.begin(); lIter != argList.end(); lIter++) + addArgument(SchemaProperty(new SchemaPropertyImpl(lIter->asMap()))); + } +} + + +Variant::Map SchemaMethodImpl::asMap() const +{ + Variant::Map map; + Variant::List argList; + + map["_name"] = name; + + if (!desc.empty()) + map["_desc"] = desc; + + for (list<SchemaProperty>::const_iterator iter = arguments.begin(); iter != arguments.end(); iter++) + argList.push_back(SchemaPropertyImplAccess::get(*iter).asMap()); + map["_arguments"] = argList; + + return map; +} + + +SchemaMethodImpl::SchemaMethodImpl(qpid::management::Buffer& buffer) +{ + Variant::Map::const_iterator iter; + Variant::Map argMap; + + buffer.getMap(argMap); + + iter = argMap.find("name"); + if (iter == argMap.end()) + throw QmfException("Received V1 Method without a name"); + name = iter->second.asString(); + + iter = argMap.find("desc"); + if (iter != argMap.end()) + desc = iter->second.asString(); + + iter = argMap.find("argCount"); + if (iter == argMap.end()) + throw QmfException("Received V1 Method without argCount"); + + int64_t count = iter->second.asInt64(); + for (int idx = 0; idx < count; idx++) { + SchemaProperty arg(new SchemaPropertyImpl(buffer)); + addArgument(arg); + } +} + + +SchemaProperty SchemaMethodImpl::getArgument(uint32_t i) const +{ + uint32_t count = 0; + for (list<SchemaProperty>::const_iterator iter = arguments.begin(); iter != arguments.end(); iter++) + if (count++ == i) + return *iter; + + throw IndexOutOfRange(); +} + + +void SchemaMethodImpl::updateHash(Hash& hash) const +{ + hash.update(name); + hash.update(desc); + for (list<SchemaProperty>::const_iterator iter = arguments.begin(); iter != arguments.end(); iter++) + SchemaPropertyImplAccess::get(*iter).updateHash(hash); +} + + +void SchemaMethodImpl::encodeV1(qpid::management::Buffer& buffer) const +{ + Variant::Map map; + + map["name"] = name; + map["argCount"] = (uint64_t) arguments.size(); + if (!desc.empty()) + map["desc"] = desc; + + buffer.putMap(map); + + for (list<SchemaProperty>::const_iterator iter = arguments.begin(); iter != arguments.end(); iter++) + SchemaPropertyImplAccess::get(*iter).encodeV1(buffer, true, true); +} + + +SchemaMethodImpl& SchemaMethodImplAccess::get(SchemaMethod& item) +{ + return *item.impl; +} + + +const SchemaMethodImpl& SchemaMethodImplAccess::get(const SchemaMethod& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/SchemaMethodImpl.h b/qpid/cpp/src/qmf/SchemaMethodImpl.h new file mode 100644 index 0000000000..930d48509c --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaMethodImpl.h @@ -0,0 +1,75 @@ +#ifndef _QMF_SCHEMA_METHOD_IMPL_H_ +#define _QMF_SCHEMA_METHOD_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/SchemaTypes.h" +#include "qmf/SchemaMethod.h" +#include "qmf/SchemaPropertyImpl.h" +#include "qpid/management/Buffer.h" +#include <list> +#include <string> + +namespace qpid { +namespace management { + class Buffer; +}} + +namespace qmf { + class Hash; + class SchemaMethodImpl : public virtual qpid::RefCounted { + public: + // + // Public impl-only methods + // + SchemaMethodImpl(const qpid::types::Variant::Map& m); + SchemaMethodImpl(qpid::management::Buffer& v1Buffer); + qpid::types::Variant::Map asMap() const; + void updateHash(Hash&) const; + void encodeV1(qpid::management::Buffer&) const; + + // + // Methods from API handle + // + SchemaMethodImpl(const std::string& n, const std::string& options); + + void setDesc(const std::string& d) { desc = d; } + void addArgument(const SchemaProperty& p) { arguments.push_back(p); } + const std::string& getName() const { return name; } + const std::string& getDesc() const { return desc; } + uint32_t getArgumentCount() const { return arguments.size(); } + SchemaProperty getArgument(uint32_t i) const; + + private: + std::string name; + std::string desc; + std::list<SchemaProperty> arguments; + }; + + struct SchemaMethodImplAccess + { + static SchemaMethodImpl& get(SchemaMethod&); + static const SchemaMethodImpl& get(const SchemaMethod&); + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/SchemaProperty.cpp b/qpid/cpp/src/qmf/SchemaProperty.cpp new file mode 100644 index 0000000000..106127261b --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaProperty.cpp @@ -0,0 +1,434 @@ +/* + * + * 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 "qmf/SchemaPropertyImpl.h" +#include "qmf/PrivateImplRef.h" +#include "qmf/exceptions.h" +#include "qmf/SchemaTypes.h" +#include "qmf/SchemaProperty.h" +#include "qmf/Hash.h" +#include "qpid/messaging/AddressParser.h" +#include <list> +#include <iostream> + +using namespace std; +using qpid::types::Variant; +using namespace qmf; + +typedef PrivateImplRef<SchemaProperty> PI; + +SchemaProperty::SchemaProperty(SchemaPropertyImpl* impl) { PI::ctor(*this, impl); } +SchemaProperty::SchemaProperty(const SchemaProperty& s) : qmf::Handle<SchemaPropertyImpl>() { PI::copy(*this, s); } +SchemaProperty::~SchemaProperty() { PI::dtor(*this); } +SchemaProperty& SchemaProperty::operator=(const SchemaProperty& s) { return PI::assign(*this, s); } + +SchemaProperty::SchemaProperty(const string& n, int t, const string& o) { PI::ctor(*this, new SchemaPropertyImpl(n, t, o)); } + +void SchemaProperty::setAccess(int a) { impl->setAccess(a); } +void SchemaProperty::setIndex(bool i) { impl->setIndex(i); } +void SchemaProperty::setOptional(bool o) { impl->setOptional(o); } +void SchemaProperty::setUnit(const string& u) { impl->setUnit(u); } +void SchemaProperty::setDesc(const string& d) { impl->setDesc(d); } +void SchemaProperty::setSubtype(const string& s) { impl->setSubtype(s); } +void SchemaProperty::setDirection(int d) { impl->setDirection(d); } + +const string& SchemaProperty::getName() const { return impl->getName(); } +int SchemaProperty::getType() const { return impl->getType(); } +int SchemaProperty::getAccess() const { return impl->getAccess(); } +bool SchemaProperty::isIndex() const { return impl->isIndex(); } +bool SchemaProperty::isOptional() const { return impl->isOptional(); } +const string& SchemaProperty::getUnit() const { return impl->getUnit(); } +const string& SchemaProperty::getDesc() const { return impl->getDesc(); } +const string& SchemaProperty::getSubtype() const { return impl->getSubtype(); } +int SchemaProperty::getDirection() const { return impl->getDirection(); } + +//======================================================================================== +// Impl Method Bodies +//======================================================================================== + +SchemaPropertyImpl::SchemaPropertyImpl(const string& n, int t, const string options) : + name(n), dataType(t), access(ACCESS_READ_ONLY), index(false), + optional(false), direction(DIR_IN) +{ + if (!options.empty()) { + qpid::messaging::AddressParser parser = qpid::messaging::AddressParser(options); + Variant::Map optMap; + Variant::Map::iterator iter; + + parser.parseMap(optMap); + + iter = optMap.find("access"); + if (iter != optMap.end()) { + const string& v(iter->second.asString()); + if (v == "RC") access = ACCESS_READ_CREATE; + else if (v == "RO") access = ACCESS_READ_ONLY; + else if (v == "RW") access = ACCESS_READ_WRITE; + else + throw QmfException("Invalid value for 'access' option. Expected RC, RO, or RW"); + optMap.erase(iter); + } + + iter = optMap.find("index"); + if (iter != optMap.end()) { + index = iter->second.asBool(); + optMap.erase(iter); + } + + iter = optMap.find("optional"); + if (iter != optMap.end()) { + optional = iter->second.asBool(); + optMap.erase(iter); + } + + iter = optMap.find("unit"); + if (iter != optMap.end()) { + unit = iter->second.asString(); + optMap.erase(iter); + } + + iter = optMap.find("desc"); + if (iter != optMap.end()) { + desc = iter->second.asString(); + optMap.erase(iter); + } + + iter = optMap.find("subtype"); + if (iter != optMap.end()) { + subtype = iter->second.asString(); + optMap.erase(iter); + } + + iter = optMap.find("dir"); + if (iter != optMap.end()) { + const string& v(iter->second.asString()); + if (v == "IN") direction = DIR_IN; + else if (v == "OUT") direction = DIR_OUT; + else if (v == "INOUT") direction = DIR_IN_OUT; + else + throw QmfException("Invalid value for 'dir' option. Expected IN, OUT, or INOUT"); + optMap.erase(iter); + } + + if (!optMap.empty()) + throw QmfException("Unexpected option: " + optMap.begin()->first); + } +} + + +SchemaPropertyImpl::SchemaPropertyImpl(const Variant::Map& map) : + access(ACCESS_READ_ONLY), index(false), optional(false), direction(DIR_IN) +{ + Variant::Map::const_iterator iter; + + iter = map.find("_name"); + if (iter == map.end()) + throw QmfException("SchemaProperty without a _name element"); + name = iter->second.asString(); + + iter = map.find("_type"); + if (iter == map.end()) + throw QmfException("SchemaProperty without a _type element"); + const string& ts(iter->second.asString()); + if (ts == "TYPE_VOID") dataType = SCHEMA_DATA_VOID; + else if (ts == "TYPE_BOOL") dataType = SCHEMA_DATA_BOOL; + else if (ts == "TYPE_INT") dataType = SCHEMA_DATA_INT; + else if (ts == "TYPE_FLOAT") dataType = SCHEMA_DATA_FLOAT; + else if (ts == "TYPE_STRING") dataType = SCHEMA_DATA_STRING; + else if (ts == "TYPE_MAP") dataType = SCHEMA_DATA_MAP; + else if (ts == "TYPE_LIST") dataType = SCHEMA_DATA_LIST; + else if (ts == "TYPE_UUID") dataType = SCHEMA_DATA_UUID; + else + throw QmfException("SchemaProperty with an invalid type code: " + ts); + + iter = map.find("_access"); + if (iter != map.end()) { + const string& as(iter->second.asString()); + if (as == "RO") access = ACCESS_READ_ONLY; + else if (as == "RC") access = ACCESS_READ_CREATE; + else if (as == "RW") access = ACCESS_READ_WRITE; + else + throw QmfException("SchemaProperty with an invalid access code: " + as); + } + + iter = map.find("_unit"); + if (iter != map.end()) + unit = iter->second.asString(); + + iter = map.find("_dir"); + if (iter != map.end()) { + const string& ds(iter->second.asString()); + if (ds == "I") direction = DIR_IN; + else if (ds == "O") direction = DIR_OUT; + else if (ds == "IO") direction = DIR_IN_OUT; + else + throw QmfException("SchemaProperty with an invalid direction code: " + ds); + } + + iter = map.find("_desc"); + if (iter != map.end()) + desc = iter->second.asString(); + + iter = map.find("_index"); + if (iter != map.end()) + index = iter->second.asBool(); + + iter = map.find("_subtype"); + if (iter != map.end()) + subtype = iter->second.asString(); +} + + +Variant::Map SchemaPropertyImpl::asMap() const +{ + Variant::Map map; + string ts; + + map["_name"] = name; + + switch (dataType) { + case SCHEMA_DATA_VOID: ts = "TYPE_VOID"; break; + case SCHEMA_DATA_BOOL: ts = "TYPE_BOOL"; break; + case SCHEMA_DATA_INT: ts = "TYPE_INT"; break; + case SCHEMA_DATA_FLOAT: ts = "TYPE_FLOAT"; break; + case SCHEMA_DATA_STRING: ts = "TYPE_STRING"; break; + case SCHEMA_DATA_MAP: ts = "TYPE_MAP"; break; + case SCHEMA_DATA_LIST: ts = "TYPE_LIST"; break; + case SCHEMA_DATA_UUID: ts = "TYPE_UUID"; break; + } + map["_type"] = ts; + + switch (access) { + case ACCESS_READ_ONLY: ts = "RO"; break; + case ACCESS_READ_CREATE: ts = "RC"; break; + case ACCESS_READ_WRITE: ts = "RW"; break; + } + map["_access"] = ts; + + if (!unit.empty()) + map["_unit"] = unit; + + switch (direction) { + case DIR_IN: ts = "I"; break; + case DIR_OUT: ts = "O"; break; + case DIR_IN_OUT: ts = "IO"; break; + } + map["_dir"] = ts; + + if (!desc.empty()) + map["_desc"] = desc; + + if (index) + map["_index"] = true; + + if (!subtype.empty()) + map["_subtype"] = subtype; + + return map; +} + + +SchemaPropertyImpl::SchemaPropertyImpl(qpid::management::Buffer& buffer) : + access(ACCESS_READ_ONLY), index(false), optional(false), direction(DIR_IN) +{ + Variant::Map::const_iterator iter; + Variant::Map pmap; + + buffer.getMap(pmap); + iter = pmap.find("name"); + if (iter == pmap.end()) + throw QmfException("Received V1 Schema property without a name"); + name = iter->second.asString(); + + iter = pmap.find("type"); + if (iter == pmap.end()) + throw QmfException("Received V1 Schema property without a type"); + fromV1TypeCode(iter->second.asInt8()); + + iter = pmap.find("unit"); + if (iter != pmap.end()) + unit = iter->second.asString(); + + iter = pmap.find("desc"); + if (iter != pmap.end()) + desc = iter->second.asString(); + + iter = pmap.find("access"); + if (iter != pmap.end()) { + int8_t val = iter->second.asInt8(); + if (val < 1 || val > 3) + throw QmfException("Received V1 Schema property with invalid 'access' code"); + access = val; + } + + iter = pmap.find("index"); + if (iter != pmap.end()) + index = iter->second.asInt64() != 0; + + iter = pmap.find("optional"); + if (iter != pmap.end()) + optional = iter->second.asInt64() != 0; + + iter = pmap.find("dir"); + if (iter != pmap.end()) { + string dirStr(iter->second.asString()); + if (dirStr == "I") direction = DIR_IN; + else if (dirStr == "O") direction = DIR_OUT; + else if (dirStr == "IO") direction = DIR_IN_OUT; + else + throw QmfException("Received V1 Schema property with invalid 'dir' code"); + } +} + + +void SchemaPropertyImpl::updateHash(Hash& hash) const +{ + hash.update(name); + hash.update((uint8_t) dataType); + hash.update(subtype); + hash.update((uint8_t) access); + hash.update(index); + hash.update(optional); + hash.update(unit); + hash.update(desc); + hash.update((uint8_t) direction); +} + + +void SchemaPropertyImpl::encodeV1(qpid::management::Buffer& buffer, bool isArg, bool isMethodArg) const +{ + Variant::Map pmap; + + pmap["name"] = name; + pmap["type"] = v1TypeCode(); + if (!unit.empty()) + pmap["unit"] = unit; + if (!desc.empty()) + pmap["desc"] = desc; + if (!isArg) { + pmap["access"] = access; + pmap["index"] = index ? 1 : 0; + pmap["optional"] = optional ? 1 : 0; + } else { + if (isMethodArg) { + string dirStr; + switch (direction) { + case DIR_IN : dirStr = "I"; break; + case DIR_OUT : dirStr = "O"; break; + case DIR_IN_OUT : dirStr = "IO"; break; + } + pmap["dir"] = dirStr; + } + } + + buffer.putMap(pmap); +} + + +uint8_t SchemaPropertyImpl::v1TypeCode() const +{ + switch (dataType) { + case SCHEMA_DATA_VOID: return 1; + case SCHEMA_DATA_BOOL: return 11; + case SCHEMA_DATA_INT: + if (subtype == "timestamp") return 8; + if (subtype == "duration") return 9; + return 19; + case SCHEMA_DATA_FLOAT: return 13; + case SCHEMA_DATA_STRING: return 7; + case SCHEMA_DATA_LIST: return 21; + case SCHEMA_DATA_UUID: return 14; + case SCHEMA_DATA_MAP: + if (subtype == "reference") return 10; + if (subtype == "data") return 20; + return 15; + } + + return 1; +} + +void SchemaPropertyImpl::fromV1TypeCode(int8_t code) +{ + switch (code) { + case 1: // U8 + case 2: // U16 + case 3: // U32 + case 4: // U64 + dataType = SCHEMA_DATA_INT; + break; + case 6: // SSTR + case 7: // LSTR + dataType = SCHEMA_DATA_STRING; + break; + case 8: // ABSTIME + dataType = SCHEMA_DATA_INT; + subtype = "timestamp"; + break; + case 9: // DELTATIME + dataType = SCHEMA_DATA_INT; + subtype = "duration"; + break; + case 10: // REF + dataType = SCHEMA_DATA_MAP; + subtype = "reference"; + break; + case 11: // BOOL + dataType = SCHEMA_DATA_BOOL; + break; + case 12: // FLOAT + case 13: // DOUBLE + dataType = SCHEMA_DATA_FLOAT; + break; + case 14: // UUID + dataType = SCHEMA_DATA_UUID; + break; + case 15: // FTABLE + dataType = SCHEMA_DATA_MAP; + break; + case 16: // S8 + case 17: // S16 + case 18: // S32 + case 19: // S64 + dataType = SCHEMA_DATA_INT; + break; + case 20: // OBJECT + dataType = SCHEMA_DATA_MAP; + subtype = "data"; + break; + case 21: // LIST + case 22: // ARRAY + dataType = SCHEMA_DATA_LIST; + break; + default: + throw QmfException("Received V1 schema with an unknown data type"); + } +} + + +SchemaPropertyImpl& SchemaPropertyImplAccess::get(SchemaProperty& item) +{ + return *item.impl; +} + + +const SchemaPropertyImpl& SchemaPropertyImplAccess::get(const SchemaProperty& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/SchemaPropertyImpl.h b/qpid/cpp/src/qmf/SchemaPropertyImpl.h new file mode 100644 index 0000000000..cdfc29066f --- /dev/null +++ b/qpid/cpp/src/qmf/SchemaPropertyImpl.h @@ -0,0 +1,93 @@ +#ifndef _QMF_SCHEMA_PROPERTY_IMPL_H_ +#define _QMF_SCHEMA_PROPERTY_IMPL_H_ + +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/SchemaTypes.h" +#include "qmf/SchemaProperty.h" +#include "qpid/types/Variant.h" +#include "qpid/management/Buffer.h" + +namespace qpid { +namespace management { + class Buffer; +}} + +namespace qmf { + class Hash; + class SchemaPropertyImpl : public virtual qpid::RefCounted { + public: + // + // Public impl-only methods + // + SchemaPropertyImpl(const qpid::types::Variant::Map& m); + SchemaPropertyImpl(qpid::management::Buffer& v1Buffer); + qpid::types::Variant::Map asMap() const; + void updateHash(Hash&) const; + void encodeV1(qpid::management::Buffer&, bool isArg, bool isMethodArg) const; + + // + // Methods from API handle + // + SchemaPropertyImpl(const std::string& n, int t, const std::string o); + void setAccess(int a) { access = a; } + void setIndex(bool i) { index = i; } + void setOptional(bool o) { optional = o; } + void setUnit(const std::string& u) { unit = u; } + void setDesc(const std::string& d) { desc = d; } + void setSubtype(const std::string& s) { subtype = s; } + void setDirection(int d) { direction = d; } + + const std::string& getName() const { return name; } + int getType() const { return dataType; } + int getAccess() const { return access; } + bool isIndex() const { return index; } + bool isOptional() const { return optional; } + const std::string& getUnit() const { return unit; } + const std::string& getDesc() const { return desc; } + const std::string& getSubtype() const { return subtype; } + int getDirection() const { return direction; } + + private: + std::string name; + int dataType; + std::string subtype; + int access; + bool index; + bool optional; + std::string unit; + std::string desc; + int direction; + + uint8_t v1TypeCode() const; + void fromV1TypeCode(int8_t); + }; + + struct SchemaPropertyImplAccess + { + static SchemaPropertyImpl& get(SchemaProperty&); + static const SchemaPropertyImpl& get(const SchemaProperty&); + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/Subscription.cpp b/qpid/cpp/src/qmf/Subscription.cpp new file mode 100644 index 0000000000..73afc8c79d --- /dev/null +++ b/qpid/cpp/src/qmf/Subscription.cpp @@ -0,0 +1,88 @@ +/* + * + * 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 "qmf/PrivateImplRef.h" +#include "qmf/exceptions.h" +#include "qmf/SubscriptionImpl.h" +#include "qmf/DataImpl.h" + +using namespace std; +using namespace qmf; +using qpid::types::Variant; + +typedef PrivateImplRef<Subscription> PI; + +Subscription::Subscription(SubscriptionImpl* impl) { PI::ctor(*this, impl); } +Subscription::Subscription(const Subscription& s) : qmf::Handle<SubscriptionImpl>() { PI::copy(*this, s); } +Subscription::~Subscription() { PI::dtor(*this); } +Subscription& Subscription::operator=(const Subscription& s) { return PI::assign(*this, s); } + +void Subscription::cancel() { impl->cancel(); } +bool Subscription::isActive() const { return impl->isActive(); } +void Subscription::lock() { impl->lock(); } +void Subscription::unlock() { impl->unlock(); } +uint32_t Subscription::getDataCount() const { return impl->getDataCount(); } +Data Subscription::getData(uint32_t i) const { return impl->getData(i); } + + +void SubscriptionImpl::cancel() +{ +} + + +bool SubscriptionImpl::isActive() const +{ + return false; +} + + +void SubscriptionImpl::lock() +{ +} + + +void SubscriptionImpl::unlock() +{ +} + + +uint32_t SubscriptionImpl::getDataCount() const +{ + return 0; +} + + +Data SubscriptionImpl::getData(uint32_t) const +{ + return Data(); +} + + +SubscriptionImpl& SubscriptionImplAccess::get(Subscription& item) +{ + return *item.impl; +} + + +const SubscriptionImpl& SubscriptionImplAccess::get(const Subscription& item) +{ + return *item.impl; +} diff --git a/qpid/cpp/src/qmf/SubscriptionImpl.h b/qpid/cpp/src/qmf/SubscriptionImpl.h new file mode 100644 index 0000000000..053e3cd00e --- /dev/null +++ b/qpid/cpp/src/qmf/SubscriptionImpl.h @@ -0,0 +1,57 @@ +#ifndef _QMF_SUBSCRIPTION_IMPL_H_ +#define _QMF_SUBSCRIPTION_IMPL_H_ +/* + * + * 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 "qpid/RefCounted.h" +#include "qmf/Subscription.h" + +namespace qmf { + class SubscriptionImpl : public virtual qpid::RefCounted { + public: + // + // Public impl-only methods + // + SubscriptionImpl(int p) : placeholder(p) {} + ~SubscriptionImpl(); + + // + // Methods from API handle + // + void cancel(); + bool isActive() const; + void lock(); + void unlock(); + uint32_t getDataCount() const; + Data getData(uint32_t) const; + + private: + int placeholder; + }; + + struct SubscriptionImplAccess + { + static SubscriptionImpl& get(Subscription&); + static const SubscriptionImpl& get(const Subscription&); + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/agentCapability.h b/qpid/cpp/src/qmf/agentCapability.h new file mode 100644 index 0000000000..6a3f6f8534 --- /dev/null +++ b/qpid/cpp/src/qmf/agentCapability.h @@ -0,0 +1,39 @@ +#ifndef QMF_AGENT_CAPABILITY_H +#define QMF_AGENT_CAPABILITY_H +/* + * + * 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. + * + */ + +namespace qmf { + + /** + * Legacy (Qpid 0.7 C++ Agent, 0.7 Broker Agent) capabilities + */ + const uint32_t AGENT_CAPABILITY_LEGACY = 0; + + /** + * Qpid 0.8 QMFv2 capabilities + */ + const uint32_t AGENT_CAPABILITY_0_8 = 1; + const uint32_t AGENT_CAPABILITY_V2_SCHEMA = 1; + const uint32_t AGENT_CAPABILITY_AGENT_PREDICATE = 1; +} + +#endif diff --git a/qpid/cpp/src/qmf/constants.cpp b/qpid/cpp/src/qmf/constants.cpp new file mode 100644 index 0000000000..6e2fd935a9 --- /dev/null +++ b/qpid/cpp/src/qmf/constants.cpp @@ -0,0 +1,77 @@ +/* + * + * 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 "constants.h" + +using namespace std; +using namespace qmf; + +/** + * Header key strings + */ +const string protocol::HEADER_KEY_APP_ID = "x-amqp-0-10.app-id"; +const string protocol::HEADER_KEY_METHOD = "method"; +const string protocol::HEADER_KEY_OPCODE = "qmf.opcode"; +const string protocol::HEADER_KEY_AGENT = "qmf.agent"; +const string protocol::HEADER_KEY_CONTENT = "qmf.content"; +const string protocol::HEADER_KEY_PARTIAL = "partial"; + +/** + * Header values per-key + */ +const string protocol::HEADER_APP_ID_QMF = "qmf2"; + +const string protocol::HEADER_METHOD_REQUEST = "request"; +const string protocol::HEADER_METHOD_RESPONSE = "response"; +const string protocol::HEADER_METHOD_INDICATION = "indication"; + +const string protocol::HEADER_OPCODE_EXCEPTION = "_exception"; +const string protocol::HEADER_OPCODE_AGENT_LOCATE_REQUEST = "_agent_locate_request"; +const string protocol::HEADER_OPCODE_AGENT_LOCATE_RESPONSE = "_agent_locate_response"; +const string protocol::HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION = "_agent_heartbeat_indication"; +const string protocol::HEADER_OPCODE_QUERY_REQUEST = "_query_request"; +const string protocol::HEADER_OPCODE_QUERY_RESPONSE = "_query_response"; +const string protocol::HEADER_OPCODE_SUBSCRIBE_REQUEST = "_subscribe_request"; +const string protocol::HEADER_OPCODE_SUBSCRIBE_RESPONSE = "_subscribe_response"; +const string protocol::HEADER_OPCODE_SUBSCRIBE_CANCEL_INDICATION = "_subscribe_cancel_indication"; +const string protocol::HEADER_OPCODE_SUBSCRIBE_REFRESH_INDICATION = "_subscribe_refresh_indication"; +const string protocol::HEADER_OPCODE_DATA_INDICATION = "_data_indication"; +const string protocol::HEADER_OPCODE_METHOD_REQUEST = "_method_request"; +const string protocol::HEADER_OPCODE_METHOD_RESPONSE = "_method_response"; + +const string protocol::HEADER_CONTENT_SCHEMA_ID = "_schema_id"; +const string protocol::HEADER_CONTENT_SCHEMA_CLASS = "_schema_class"; +const string protocol::HEADER_CONTENT_OBJECT_ID = "_object_id"; +const string protocol::HEADER_CONTENT_DATA = "_data"; +const string protocol::HEADER_CONTENT_EVENT = "_event"; +const string protocol::HEADER_CONTENT_QUERY = "_query"; + +/** + * Keywords for Agent attributes + */ +const string protocol::AGENT_ATTR_VENDOR = "_vendor"; +const string protocol::AGENT_ATTR_PRODUCT = "_product"; +const string protocol::AGENT_ATTR_INSTANCE = "_instance"; +const string protocol::AGENT_ATTR_NAME = "_name"; +const string protocol::AGENT_ATTR_TIMESTAMP = "_timestamp"; +const string protocol::AGENT_ATTR_HEARTBEAT_INTERVAL = "_heartbeat_interval"; +const string protocol::AGENT_ATTR_EPOCH = "_epoch"; +const string protocol::AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP = "_schema_updated"; diff --git a/qpid/cpp/src/qmf/constants.h b/qpid/cpp/src/qmf/constants.h new file mode 100644 index 0000000000..79beaaf1ca --- /dev/null +++ b/qpid/cpp/src/qmf/constants.h @@ -0,0 +1,83 @@ +#ifndef QMF_CONSTANTS_H +#define QMF_CONSTANTS_H +/* + * + * 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 <string> + +namespace qmf { + + struct protocol { + /** + * Header key strings + */ + static const std::string HEADER_KEY_APP_ID; + static const std::string HEADER_KEY_METHOD; + static const std::string HEADER_KEY_OPCODE; + static const std::string HEADER_KEY_AGENT; + static const std::string HEADER_KEY_CONTENT; + static const std::string HEADER_KEY_PARTIAL; + + /** + * Header values per-key + */ + static const std::string HEADER_APP_ID_QMF; + + static const std::string HEADER_METHOD_REQUEST; + static const std::string HEADER_METHOD_RESPONSE; + static const std::string HEADER_METHOD_INDICATION; + + static const std::string HEADER_OPCODE_EXCEPTION; + static const std::string HEADER_OPCODE_AGENT_LOCATE_REQUEST; + static const std::string HEADER_OPCODE_AGENT_LOCATE_RESPONSE; + static const std::string HEADER_OPCODE_AGENT_HEARTBEAT_INDICATION; + static const std::string HEADER_OPCODE_QUERY_REQUEST; + static const std::string HEADER_OPCODE_QUERY_RESPONSE; + static const std::string HEADER_OPCODE_SUBSCRIBE_REQUEST; + static const std::string HEADER_OPCODE_SUBSCRIBE_RESPONSE; + static const std::string HEADER_OPCODE_SUBSCRIBE_CANCEL_INDICATION; + static const std::string HEADER_OPCODE_SUBSCRIBE_REFRESH_INDICATION; + static const std::string HEADER_OPCODE_DATA_INDICATION; + static const std::string HEADER_OPCODE_METHOD_REQUEST; + static const std::string HEADER_OPCODE_METHOD_RESPONSE; + + static const std::string HEADER_CONTENT_SCHEMA_ID; + static const std::string HEADER_CONTENT_SCHEMA_CLASS; + static const std::string HEADER_CONTENT_OBJECT_ID; + static const std::string HEADER_CONTENT_DATA; + static const std::string HEADER_CONTENT_EVENT; + static const std::string HEADER_CONTENT_QUERY; + + /** + * Keywords for Agent attributes + */ + static const std::string AGENT_ATTR_VENDOR; + static const std::string AGENT_ATTR_PRODUCT; + static const std::string AGENT_ATTR_INSTANCE; + static const std::string AGENT_ATTR_NAME; + static const std::string AGENT_ATTR_TIMESTAMP; + static const std::string AGENT_ATTR_HEARTBEAT_INTERVAL; + static const std::string AGENT_ATTR_EPOCH; + static const std::string AGENT_ATTR_SCHEMA_UPDATED_TIMESTAMP; + }; +} + +#endif diff --git a/qpid/cpp/src/qmf/engine/Agent.cpp b/qpid/cpp/src/qmf/engine/Agent.cpp new file mode 100644 index 0000000000..1f08dded94 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/Agent.cpp @@ -0,0 +1,915 @@ +/* + * 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 "qmf/engine/Agent.h" +#include "qmf/engine/MessageImpl.h" +#include "qmf/engine/SchemaImpl.h" +#include "qmf/engine/Typecode.h" +#include "qmf/engine/EventImpl.h" +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/ObjectIdImpl.h" +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/Protocol.h" +#include <qpid/framing/Buffer.h> +#include <qpid/framing/Uuid.h> +#include <qpid/framing/FieldTable.h> +#include <qpid/framing/FieldValue.h> +#include <qpid/sys/Mutex.h> +#include <qpid/log/Statement.h> +#include <qpid/sys/Time.h> +#include <string.h> +#include <string> +#include <deque> +#include <map> +#include <iostream> +#include <fstream> +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace qmf { +namespace engine { + + struct AgentEventImpl { + typedef boost::shared_ptr<AgentEventImpl> Ptr; + AgentEvent::EventKind kind; + uint32_t sequence; + string authUserId; + string authToken; + string name; + Object* object; + boost::shared_ptr<ObjectId> objectId; + boost::shared_ptr<Query> query; + boost::shared_ptr<Value> arguments; + string exchange; + string bindingKey; + const SchemaObjectClass* objectClass; + + AgentEventImpl(AgentEvent::EventKind k) : + kind(k), sequence(0), object(0), objectClass(0) {} + ~AgentEventImpl() {} + AgentEvent copy(); + }; + + struct AgentQueryContext { + typedef boost::shared_ptr<AgentQueryContext> Ptr; + uint32_t sequence; + string exchange; + string key; + const SchemaMethod* schemaMethod; + AgentQueryContext() : schemaMethod(0) {} + }; + + class AgentImpl : public boost::noncopyable { + public: + AgentImpl(char* label, bool internalStore); + ~AgentImpl(); + + void setStoreDir(const char* path); + void setTransferDir(const char* path); + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + bool getEvent(AgentEvent& event) const; + void popEvent(); + void newSession(); + void startProtocol(); + void heartbeat(); + void methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments); + void queryResponse(uint32_t sequence, Object& object, bool prop, bool stat); + void queryComplete(uint32_t sequence); + void registerClass(SchemaObjectClass* cls); + void registerClass(SchemaEventClass* cls); + const ObjectId* addObject(Object& obj, uint64_t persistId); + const ObjectId* allocObjectId(uint64_t persistId); + const ObjectId* allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi); + void raiseEvent(Event& event); + + private: + mutable Mutex lock; + Mutex addLock; + string label; + string queueName; + string storeDir; + string transferDir; + bool internalStore; + uint64_t nextTransientId; + Uuid systemId; + uint32_t requestedBrokerBank; + uint32_t requestedAgentBank; + uint32_t assignedBrokerBank; + uint32_t assignedAgentBank; + AgentAttachment attachment; + uint16_t bootSequence; + uint64_t nextObjectId; + uint32_t nextContextNum; + deque<AgentEventImpl::Ptr> eventQueue; + deque<MessageImpl::Ptr> xmtQueue; + map<uint32_t, AgentQueryContext::Ptr> contextMap; + bool attachComplete; + + static const char* QMF_EXCHANGE; + static const char* DIR_EXCHANGE; + static const char* BROKER_KEY; + static const uint32_t MERR_UNKNOWN_METHOD = 2; + static const uint32_t MERR_UNKNOWN_PACKAGE = 8; + static const uint32_t MERR_UNKNOWN_CLASS = 9; + static const uint32_t MERR_INTERNAL_ERROR = 10; +# define MA_BUFFER_SIZE 65536 + char outputBuffer[MA_BUFFER_SIZE]; + + struct AgentClassKey { + string name; + uint8_t hash[16]; + AgentClassKey(const string& n, const uint8_t* h) : name(n) { + memcpy(hash, h, 16); + } + AgentClassKey(Buffer& buffer) { + buffer.getShortString(name); + buffer.getBin128(hash); + } + string repr() { + return name; + } + }; + + struct AgentClassKeyComp { + bool operator() (const AgentClassKey& lhs, const AgentClassKey& rhs) const + { + if (lhs.name != rhs.name) + return lhs.name < rhs.name; + else + for (int i = 0; i < 16; i++) + if (lhs.hash[i] != rhs.hash[i]) + return lhs.hash[i] < rhs.hash[i]; + return false; + } + }; + + typedef map<AgentClassKey, SchemaObjectClass*, AgentClassKeyComp> ObjectClassMap; + typedef map<AgentClassKey, SchemaEventClass*, AgentClassKeyComp> EventClassMap; + + struct ClassMaps { + ObjectClassMap objectClasses; + EventClassMap eventClasses; + }; + + map<string, ClassMaps> packages; + + AgentEventImpl::Ptr eventDeclareQueue(const string& queueName); + AgentEventImpl::Ptr eventBind(const string& exchange, const string& queue, const string& key); + AgentEventImpl::Ptr eventSetupComplete(); + AgentEventImpl::Ptr eventQuery(uint32_t num, const string& userId, const string& package, const string& cls, + boost::shared_ptr<ObjectId> oid); + AgentEventImpl::Ptr eventMethod(uint32_t num, const string& userId, const string& method, + boost::shared_ptr<ObjectId> oid, boost::shared_ptr<Value> argMap, + const SchemaObjectClass* objectClass); + void sendBufferLH(Buffer& buf, const string& destination, const string& routingKey); + + void sendPackageIndicationLH(const string& packageName); + void sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key); + void sendCommandCompleteLH(const string& exchange, const string& key, uint32_t seq, + uint32_t code = 0, const string& text = "OK"); + void sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text=""); + void handleAttachResponse(Buffer& inBuffer); + void handlePackageRequest(Buffer& inBuffer); + void handleClassQuery(Buffer& inBuffer); + void handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, + const string& replyToExchange, const string& replyToKey); + void handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); + void handleMethodRequest(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId); + void handleConsoleAddedIndication(); + }; +} +} + +const char* AgentImpl::QMF_EXCHANGE = "qpid.management"; +const char* AgentImpl::DIR_EXCHANGE = "amq.direct"; +const char* AgentImpl::BROKER_KEY = "broker"; + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());} + +AgentEvent AgentEventImpl::copy() +{ + AgentEvent item; + + ::memset(&item, 0, sizeof(AgentEvent)); + item.kind = kind; + item.sequence = sequence; + item.object = object; + item.objectId = objectId.get(); + item.query = query.get(); + item.arguments = arguments.get(); + item.objectClass = objectClass; + + STRING_REF(authUserId); + STRING_REF(authToken); + STRING_REF(name); + STRING_REF(exchange); + STRING_REF(bindingKey); + + return item; +} + +AgentImpl::AgentImpl(char* _label, bool i) : + label(_label), queueName("qmfa-"), internalStore(i), nextTransientId(1), + requestedBrokerBank(0), requestedAgentBank(0), + assignedBrokerBank(0), assignedAgentBank(0), + bootSequence(1), nextObjectId(1), nextContextNum(1), attachComplete(false) +{ + queueName += Uuid(true).str(); +} + +AgentImpl::~AgentImpl() +{ +} + +void AgentImpl::setStoreDir(const char* path) +{ + Mutex::ScopedLock _lock(lock); + if (path) + storeDir = path; + else + storeDir.clear(); +} + +void AgentImpl::setTransferDir(const char* path) +{ + Mutex::ScopedLock _lock(lock); + if (path) + transferDir = path; + else + transferDir.clear(); +} + +void AgentImpl::handleRcvMessage(Message& message) +{ + Buffer inBuffer(message.body, message.length); + uint8_t opcode; + uint32_t sequence; + string replyToExchange(message.replyExchange ? message.replyExchange : ""); + string replyToKey(message.replyKey ? message.replyKey : ""); + string userId(message.userId ? message.userId : ""); + + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) { + if (opcode == Protocol::OP_ATTACH_RESPONSE) handleAttachResponse(inBuffer); + else if (opcode == Protocol::OP_SCHEMA_REQUEST) handleSchemaRequest(inBuffer, sequence, replyToExchange, replyToKey); + else if (opcode == Protocol::OP_CONSOLE_ADDED_INDICATION) handleConsoleAddedIndication(); + else if (opcode == Protocol::OP_GET_QUERY) handleGetQuery(inBuffer, sequence, replyToKey, userId); + else if (opcode == Protocol::OP_METHOD_REQUEST) handleMethodRequest(inBuffer, sequence, replyToKey, userId); + else { + QPID_LOG(error, "AgentImpl::handleRcvMessage invalid opcode=" << opcode); + break; + } + } +} + +bool AgentImpl::getXmtMessage(Message& item) const +{ + Mutex::ScopedLock _lock(lock); + if (xmtQueue.empty()) + return false; + item = xmtQueue.front()->copy(); + return true; +} + +void AgentImpl::popXmt() +{ + Mutex::ScopedLock _lock(lock); + if (!xmtQueue.empty()) + xmtQueue.pop_front(); +} + +bool AgentImpl::getEvent(AgentEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void AgentImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +void AgentImpl::newSession() +{ + Mutex::ScopedLock _lock(lock); + eventQueue.clear(); + xmtQueue.clear(); + eventQueue.push_back(eventDeclareQueue(queueName)); + eventQueue.push_back(eventBind("amq.direct", queueName, queueName)); + eventQueue.push_back(eventSetupComplete()); +} + +void AgentImpl::startProtocol() +{ + Mutex::ScopedLock _lock(lock); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + Protocol::encodeHeader(buffer, Protocol::OP_ATTACH_REQUEST); + buffer.putShortString(label); + systemId.encode(buffer); + buffer.putLong(requestedBrokerBank); + buffer.putLong(requestedAgentBank); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT AttachRequest: reqBroker=" << requestedBrokerBank << + " reqAgent=" << requestedAgentBank); +} + +void AgentImpl::heartbeat() +{ + Mutex::ScopedLock _lock(lock); + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + + Protocol::encodeHeader(buffer, Protocol::OP_HEARTBEAT_INDICATION); + buffer.putLongLong(uint64_t(Duration(EPOCH, now()))); + stringstream key; + key << "console.heartbeat." << assignedBrokerBank << "." << assignedAgentBank; + sendBufferLH(buffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT HeartbeatIndication"); +} + +void AgentImpl::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& argMap) +{ + Mutex::ScopedLock _lock(lock); + map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + AgentQueryContext::Ptr context = iter->second; + contextMap.erase(iter); + + char* buf(outputBuffer); + uint32_t bufLen(114 + strlen(text)); // header(8) + status(4) + mstring(2 + size) + margin(100) + bool allocated(false); + + if (status == 0) { + for (vector<const SchemaArgument*>::const_iterator aIter = context->schemaMethod->impl->arguments.begin(); + aIter != context->schemaMethod->impl->arguments.end(); aIter++) { + const SchemaArgument* schemaArg = *aIter; + if (schemaArg->getDirection() == DIR_OUT || schemaArg->getDirection() == DIR_IN_OUT) { + if (argMap.keyInMap(schemaArg->getName())) { + const Value* val = argMap.byKey(schemaArg->getName()); + bufLen += val->impl->encodedSize(); + } else { + Value val(schemaArg->getType()); + bufLen += val.impl->encodedSize(); + } + } + } + } + + if (bufLen > MA_BUFFER_SIZE) { + buf = (char*) malloc(bufLen); + allocated = true; + } + + Buffer buffer(buf, bufLen); + Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, context->sequence); + buffer.putLong(status); + buffer.putMediumString(text); + if (status == 0) { + for (vector<const SchemaArgument*>::const_iterator aIter = context->schemaMethod->impl->arguments.begin(); + aIter != context->schemaMethod->impl->arguments.end(); aIter++) { + const SchemaArgument* schemaArg = *aIter; + if (schemaArg->getDirection() == DIR_OUT || schemaArg->getDirection() == DIR_IN_OUT) { + if (argMap.keyInMap(schemaArg->getName())) { + const Value* val = argMap.byKey(schemaArg->getName()); + val->impl->encode(buffer); + } else { + Value val(schemaArg->getType()); + val.impl->encode(buffer); + } + } + } + } + sendBufferLH(buffer, context->exchange, context->key); + if (allocated) + free(buf); + QPID_LOG(trace, "SENT MethodResponse seq=" << context->sequence << " status=" << status << " text=" << text); +} + +void AgentImpl::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) +{ + Mutex::ScopedLock _lock(lock); + map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + AgentQueryContext::Ptr context = iter->second; + + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_OBJECT_INDICATION, context->sequence); + + object.impl->encodeSchemaKey(buffer); + object.impl->encodeManagedObjectData(buffer); + if (prop) + object.impl->encodeProperties(buffer); + if (stat) + object.impl->encodeStatistics(buffer); + + sendBufferLH(buffer, context->exchange, context->key); + QPID_LOG(trace, "SENT ContentIndication seq=" << context->sequence); +} + +void AgentImpl::queryComplete(uint32_t sequence) +{ + Mutex::ScopedLock _lock(lock); + map<uint32_t, AgentQueryContext::Ptr>::iterator iter = contextMap.find(sequence); + if (iter == contextMap.end()) + return; + + AgentQueryContext::Ptr context = iter->second; + contextMap.erase(iter); + sendCommandCompleteLH(context->exchange, context->key, context->sequence, 0, "OK"); +} + +void AgentImpl::registerClass(SchemaObjectClass* cls) +{ + Mutex::ScopedLock _lock(lock); + bool newPackage = false; + + map<string, ClassMaps>::iterator iter = packages.find(cls->getClassKey()->getPackageName()); + if (iter == packages.end()) { + packages[cls->getClassKey()->getPackageName()] = ClassMaps(); + iter = packages.find(cls->getClassKey()->getPackageName()); + newPackage = true; + } + + AgentClassKey key(cls->getClassKey()->getClassName(), cls->getClassKey()->getHash()); + iter->second.objectClasses[key] = cls; + + // Indicate this new schema if connected. + + if (attachComplete) { + + if (newPackage) { + sendPackageIndicationLH(iter->first); + } + sendClassIndicationLH(CLASS_OBJECT, iter->first, key); + } +} + +void AgentImpl::registerClass(SchemaEventClass* cls) +{ + Mutex::ScopedLock _lock(lock); + bool newPackage = false; + + map<string, ClassMaps>::iterator iter = packages.find(cls->getClassKey()->getPackageName()); + if (iter == packages.end()) { + packages[cls->getClassKey()->getPackageName()] = ClassMaps(); + iter = packages.find(cls->getClassKey()->getPackageName()); + newPackage = true; + } + + AgentClassKey key(cls->getClassKey()->getClassName(), cls->getClassKey()->getHash()); + iter->second.eventClasses[key] = cls; + + // Indicate this new schema if connected. + + if (attachComplete) { + + if (newPackage) { + sendPackageIndicationLH(iter->first); + } + sendClassIndicationLH(CLASS_EVENT, iter->first, key); + } +} + +const ObjectId* AgentImpl::addObject(Object&, uint64_t) +{ + Mutex::ScopedLock _lock(lock); + return 0; +} + +const ObjectId* AgentImpl::allocObjectId(uint64_t persistId) +{ + Mutex::ScopedLock _lock(lock); + uint16_t sequence = persistId ? 0 : bootSequence; + uint64_t objectNum = persistId ? persistId : nextObjectId++; + + ObjectId* oid = ObjectIdImpl::factory(&attachment, 0, sequence, objectNum); + return oid; +} + +const ObjectId* AgentImpl::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) +{ + return allocObjectId(((uint64_t) persistIdHi) << 32 | (uint64_t) persistIdLo); +} + +void AgentImpl::raiseEvent(Event& event) +{ + Mutex::ScopedLock _lock(lock); + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_EVENT_INDICATION); + + event.impl->encodeSchemaKey(buffer); + buffer.putLongLong(uint64_t(Duration(EPOCH, now()))); + event.impl->encode(buffer); + string key(event.impl->getRoutingKey(assignedBrokerBank, assignedAgentBank)); + + sendBufferLH(buffer, QMF_EXCHANGE, key); + QPID_LOG(trace, "SENT EventIndication"); +} + +AgentEventImpl::Ptr AgentImpl::eventDeclareQueue(const string& name) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::DECLARE_QUEUE)); + event->name = name; + + return event; +} + +AgentEventImpl::Ptr AgentImpl::eventBind(const string& exchange, const string& queue, + const string& key) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::BIND)); + event->name = queue; + event->exchange = exchange; + event->bindingKey = key; + + return event; +} + +AgentEventImpl::Ptr AgentImpl::eventSetupComplete() +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::SETUP_COMPLETE)); + return event; +} + +AgentEventImpl::Ptr AgentImpl::eventQuery(uint32_t num, const string& userId, const string& package, + const string& cls, boost::shared_ptr<ObjectId> oid) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::GET_QUERY)); + event->sequence = num; + event->authUserId = userId; + if (oid.get()) + event->query.reset(new Query(oid.get())); + else + event->query.reset(new Query(cls.c_str(), package.c_str())); + return event; +} + +AgentEventImpl::Ptr AgentImpl::eventMethod(uint32_t num, const string& userId, const string& method, + boost::shared_ptr<ObjectId> oid, boost::shared_ptr<Value> argMap, + const SchemaObjectClass* objectClass) +{ + AgentEventImpl::Ptr event(new AgentEventImpl(AgentEvent::METHOD_CALL)); + event->sequence = num; + event->authUserId = userId; + event->name = method; + event->objectId = oid; + event->arguments = argMap; + event->objectClass = objectClass; + return event; +} + +void AgentImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) +{ + uint32_t length = buf.getPosition(); + MessageImpl::Ptr message(new MessageImpl); + + buf.reset(); + buf.getRawData(message->body, length); + message->destination = destination; + message->routingKey = routingKey; + message->replyExchange = "amq.direct"; + message->replyKey = queueName; + + xmtQueue.push_back(message); +} + +void AgentImpl::sendPackageIndicationLH(const string& packageName) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_PACKAGE_INDICATION); + buffer.putShortString(packageName); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT PackageIndication: package_name=" << packageName); +} + +void AgentImpl::sendClassIndicationLH(ClassKind kind, const string& packageName, const AgentClassKey& key) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_CLASS_INDICATION); + buffer.putOctet((int) kind); + buffer.putShortString(packageName); + buffer.putShortString(key.name); + buffer.putBin128(const_cast<uint8_t*>(key.hash)); // const_cast needed for older Qpid libraries + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT ClassIndication: package_name=" << packageName << " class_name=" << key.name); +} + +void AgentImpl::sendCommandCompleteLH(const string& exchange, const string& replyToKey, + uint32_t sequence, uint32_t code, const string& text) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_COMMAND_COMPLETE, sequence); + buffer.putLong(code); + buffer.putShortString(text); + sendBufferLH(buffer, exchange, replyToKey); + QPID_LOG(trace, "SENT CommandComplete: seq=" << sequence << " code=" << code << " text=" << text); +} + +void AgentImpl::sendMethodErrorLH(uint32_t sequence, const string& key, uint32_t code, const string& text) +{ + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_METHOD_RESPONSE, sequence); + buffer.putLong(code); + + string fulltext; + switch (code) { + case MERR_UNKNOWN_PACKAGE: fulltext = "Unknown Package"; break; + case MERR_UNKNOWN_CLASS: fulltext = "Unknown Class"; break; + case MERR_UNKNOWN_METHOD: fulltext = "Unknown Method"; break; + case MERR_INTERNAL_ERROR: fulltext = "Internal Error"; break; + default: fulltext = "Unspecified Error"; break; + } + + if (!text.empty()) { + fulltext += " ("; + fulltext += text; + fulltext += ")"; + } + + buffer.putMediumString(fulltext); + sendBufferLH(buffer, DIR_EXCHANGE, key); + QPID_LOG(trace, "SENT MethodResponse: errorCode=" << code << " text=" << fulltext); +} + +void AgentImpl::handleAttachResponse(Buffer& inBuffer) +{ + Mutex::ScopedLock _lock(lock); + + assignedBrokerBank = inBuffer.getLong(); + assignedAgentBank = inBuffer.getLong(); + + QPID_LOG(trace, "RCVD AttachResponse: broker=" << assignedBrokerBank << " agent=" << assignedAgentBank); + + if ((assignedBrokerBank != requestedBrokerBank) || + (assignedAgentBank != requestedAgentBank)) { + if (requestedAgentBank == 0) { + QPID_LOG(notice, "Initial object-id bank assigned: " << assignedBrokerBank << "." << + assignedAgentBank); + } else { + QPID_LOG(warning, "Collision in object-id! New bank assigned: " << assignedBrokerBank << + "." << assignedAgentBank); + } + //storeData(); // TODO + requestedBrokerBank = assignedBrokerBank; + requestedAgentBank = assignedAgentBank; + } + + attachment.setBanks(assignedBrokerBank, assignedAgentBank); + + // Bind to qpid.management to receive commands + stringstream key; + key << "agent." << assignedBrokerBank << "." << assignedAgentBank; + eventQueue.push_back(eventBind(QMF_EXCHANGE, queueName, key.str())); + + // Send package indications for all local packages + for (map<string, ClassMaps>::iterator pIter = packages.begin(); + pIter != packages.end(); + pIter++) { + sendPackageIndicationLH(pIter->first); + + // Send class indications for all local classes + ClassMaps cMap = pIter->second; + for (ObjectClassMap::iterator cIter = cMap.objectClasses.begin(); + cIter != cMap.objectClasses.end(); cIter++) + sendClassIndicationLH(CLASS_OBJECT, pIter->first, cIter->first); + for (EventClassMap::iterator cIter = cMap.eventClasses.begin(); + cIter != cMap.eventClasses.end(); cIter++) + sendClassIndicationLH(CLASS_EVENT, pIter->first, cIter->first); + } + + attachComplete = true; +} + +void AgentImpl::handlePackageRequest(Buffer&) +{ + Mutex::ScopedLock _lock(lock); +} + +void AgentImpl::handleClassQuery(Buffer&) +{ + Mutex::ScopedLock _lock(lock); +} + +void AgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, + const string& replyExchange, const string& replyKey) +{ + Mutex::ScopedLock _lock(lock); + string rExchange(replyExchange); + string rKey(replyKey); + string packageName; + inBuffer.getShortString(packageName); + AgentClassKey key(inBuffer); + + if (rExchange.empty()) + rExchange = QMF_EXCHANGE; + if (rKey.empty()) + rKey = BROKER_KEY; + + QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name); + + map<string, ClassMaps>::iterator pIter = packages.find(packageName); + if (pIter == packages.end()) { + sendCommandCompleteLH(rExchange, rKey, sequence, 1, "package not found"); + return; + } + + ClassMaps cMap = pIter->second; + ObjectClassMap::iterator ocIter = cMap.objectClasses.find(key); + if (ocIter != cMap.objectClasses.end()) { + SchemaObjectClass* oImpl = ocIter->second; + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); + oImpl->impl->encode(buffer); + sendBufferLH(buffer, rExchange, rKey); + QPID_LOG(trace, "SENT SchemaResponse: (object) package=" << packageName << " class=" << key.name); + return; + } + + EventClassMap::iterator ecIter = cMap.eventClasses.find(key); + if (ecIter != cMap.eventClasses.end()) { + SchemaEventClass* eImpl = ecIter->second; + Buffer buffer(outputBuffer, MA_BUFFER_SIZE); + Protocol::encodeHeader(buffer, Protocol::OP_SCHEMA_RESPONSE, sequence); + eImpl->impl->encode(buffer); + sendBufferLH(buffer, rExchange, rKey); + QPID_LOG(trace, "SENT SchemaResponse: (event) package=" << packageName << " class=" << key.name); + return; + } + + sendCommandCompleteLH(rExchange, rKey, sequence, 1, "class not found"); +} + +void AgentImpl::handleGetQuery(Buffer& inBuffer, uint32_t sequence, const string& replyTo, const string& userId) +{ + Mutex::ScopedLock _lock(lock); + FieldTable ft; + FieldTable::ValuePtr value; + map<string, ClassMaps>::const_iterator pIter = packages.end(); + string pname; + string cname; + string oidRepr; + boost::shared_ptr<ObjectId> oid; + + ft.decode(inBuffer); + + QPID_LOG(trace, "RCVD GetQuery: seq=" << sequence << " map=" << ft); + + value = ft.get("_package"); + if (value.get() && value->convertsTo<string>()) { + pname = value->get<string>(); + pIter = packages.find(pname); + if (pIter == packages.end()) { + sendCommandCompleteLH(DIR_EXCHANGE, replyTo, sequence); + return; + } + } + + value = ft.get("_class"); + if (value.get() && value->convertsTo<string>()) { + cname = value->get<string>(); + // TODO - check for validity of class (in package or any package) + if (pIter == packages.end()) { + } else { + + } + } + + value = ft.get("_objectid"); + if (value.get() && value->convertsTo<string>()) { + oidRepr = value->get<string>(); + oid.reset(new ObjectId()); + oid->impl->fromString(oidRepr); + } + + AgentQueryContext::Ptr context(new AgentQueryContext); + uint32_t contextNum = nextContextNum++; + context->sequence = sequence; + context->exchange = DIR_EXCHANGE; + context->key = replyTo; + contextMap[contextNum] = context; + + eventQueue.push_back(eventQuery(contextNum, userId, pname, cname, oid)); +} + +void AgentImpl::handleMethodRequest(Buffer& buffer, uint32_t sequence, const string& replyTo, const string& userId) +{ + Mutex::ScopedLock _lock(lock); + string pname; + string method; + boost::shared_ptr<ObjectId> oid(ObjectIdImpl::factory(buffer)); + buffer.getShortString(pname); + AgentClassKey classKey(buffer); + buffer.getShortString(method); + + QPID_LOG(trace, "RCVD MethodRequest seq=" << sequence << " method=" << method); + + map<string, ClassMaps>::const_iterator pIter = packages.find(pname); + if (pIter == packages.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_PACKAGE, pname); + return; + } + + ObjectClassMap::const_iterator cIter = pIter->second.objectClasses.find(classKey); + if (cIter == pIter->second.objectClasses.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_CLASS, classKey.repr()); + return; + } + + const SchemaObjectClass* schema = cIter->second; + vector<const SchemaMethod*>::const_iterator mIter = schema->impl->methods.begin(); + for (; mIter != schema->impl->methods.end(); mIter++) { + if ((*mIter)->getName() == method) + break; + } + + if (mIter == schema->impl->methods.end()) { + sendMethodErrorLH(sequence, replyTo, MERR_UNKNOWN_METHOD, method); + return; + } + + const SchemaMethod* schemaMethod = *mIter; + boost::shared_ptr<Value> argMap(new Value(TYPE_MAP)); + Value* value; + for (vector<const SchemaArgument*>::const_iterator aIter = schemaMethod->impl->arguments.begin(); + aIter != schemaMethod->impl->arguments.end(); aIter++) { + const SchemaArgument* schemaArg = *aIter; + if (schemaArg->getDirection() == DIR_IN || schemaArg->getDirection() == DIR_IN_OUT) + value = ValueImpl::factory(schemaArg->getType(), buffer); + else + value = ValueImpl::factory(schemaArg->getType()); + argMap->insert(schemaArg->getName(), value); + } + + AgentQueryContext::Ptr context(new AgentQueryContext); + uint32_t contextNum = nextContextNum++; + context->sequence = sequence; + context->exchange = DIR_EXCHANGE; + context->key = replyTo; + context->schemaMethod = schemaMethod; + contextMap[contextNum] = context; + + eventQueue.push_back(eventMethod(contextNum, userId, method, oid, argMap, schema)); +} + +void AgentImpl::handleConsoleAddedIndication() +{ + Mutex::ScopedLock _lock(lock); +} + +//================================================================== +// Wrappers +//================================================================== + +Agent::Agent(char* label, bool internalStore) { impl = new AgentImpl(label, internalStore); } +Agent::~Agent() { delete impl; } +void Agent::setStoreDir(const char* path) { impl->setStoreDir(path); } +void Agent::setTransferDir(const char* path) { impl->setTransferDir(path); } +void Agent::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } +bool Agent::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } +void Agent::popXmt() { impl->popXmt(); } +bool Agent::getEvent(AgentEvent& event) const { return impl->getEvent(event); } +void Agent::popEvent() { impl->popEvent(); } +void Agent::newSession() { impl->newSession(); } +void Agent::startProtocol() { impl->startProtocol(); } +void Agent::heartbeat() { impl->heartbeat(); } +void Agent::methodResponse(uint32_t sequence, uint32_t status, char* text, const Value& arguments) { impl->methodResponse(sequence, status, text, arguments); } +void Agent::queryResponse(uint32_t sequence, Object& object, bool prop, bool stat) { impl->queryResponse(sequence, object, prop, stat); } +void Agent::queryComplete(uint32_t sequence) { impl->queryComplete(sequence); } +void Agent::registerClass(SchemaObjectClass* cls) { impl->registerClass(cls); } +void Agent::registerClass(SchemaEventClass* cls) { impl->registerClass(cls); } +const ObjectId* Agent::addObject(Object& obj, uint64_t persistId) { return impl->addObject(obj, persistId); } +const ObjectId* Agent::allocObjectId(uint64_t persistId) { return impl->allocObjectId(persistId); } +const ObjectId* Agent::allocObjectId(uint32_t persistIdLo, uint32_t persistIdHi) { return impl->allocObjectId(persistIdLo, persistIdHi); } +void Agent::raiseEvent(Event& event) { impl->raiseEvent(event); } + diff --git a/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp new file mode 100644 index 0000000000..5fc71979fd --- /dev/null +++ b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.cpp @@ -0,0 +1,827 @@ +/* + * 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 "qmf/engine/BrokerProxyImpl.h" +#include "qmf/engine/ConsoleImpl.h" +#include "qmf/engine/Protocol.h" +#include "qpid/Address.h" +#include "qpid/sys/SystemInfo.h" +#include <qpid/log/Statement.h> +#include <qpid/StringUtils.h> +#include <string.h> +#include <iostream> +#include <fstream> + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace { + const char* QMF_EXCHANGE = "qpid.management"; + const char* DIR_EXCHANGE = "amq.direct"; + const char* BROKER_KEY = "broker"; + const char* BROKER_PACKAGE = "org.apache.qpid.broker"; + const char* AGENT_CLASS = "agent"; + const char* BROKER_AGENT_KEY = "agent.1.0"; +} + +const Object* QueryResponseImpl::getObject(uint32_t idx) const +{ + vector<ObjectPtr>::const_iterator iter = results.begin(); + + while (idx > 0) { + if (iter == results.end()) + return 0; + iter++; + idx--; + } + + return iter->get(); +} + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());} + +BrokerEvent BrokerEventImpl::copy() +{ + BrokerEvent item; + + ::memset(&item, 0, sizeof(BrokerEvent)); + item.kind = kind; + + STRING_REF(name); + STRING_REF(exchange); + STRING_REF(bindingKey); + item.context = context; + item.queryResponse = queryResponse.get(); + item.methodResponse = methodResponse.get(); + + return item; +} + +BrokerProxyImpl::BrokerProxyImpl(BrokerProxy& pub, Console& _console) : publicObject(pub), console(_console) +{ + stringstream qn; + qpid::Address addr; + + SystemInfo::getLocalHostname(addr); + qn << "qmfc-" << SystemInfo::getProcessName() << "-" << addr << "-" << SystemInfo::getProcessId(); + queueName = qn.str(); + + seqMgr.setUnsolicitedContext(SequenceContext::Ptr(new StaticContext(*this))); +} + +void BrokerProxyImpl::sessionOpened(SessionHandle& /*sh*/) +{ + Mutex::ScopedLock _lock(lock); + agentList.clear(); + eventQueue.clear(); + xmtQueue.clear(); + eventQueue.push_back(eventDeclareQueue(queueName)); + eventQueue.push_back(eventBind(DIR_EXCHANGE, queueName, queueName)); + eventQueue.push_back(eventSetupComplete()); + + // TODO: Store session handle +} + +void BrokerProxyImpl::sessionClosed() +{ + Mutex::ScopedLock _lock(lock); + agentList.clear(); + eventQueue.clear(); + xmtQueue.clear(); +} + +void BrokerProxyImpl::startProtocol() +{ + AgentProxyPtr agent(AgentProxyImpl::factory(console, publicObject, 0, "Agent embedded in broker")); + { + Mutex::ScopedLock _lock(lock); + char rawbuffer[512]; + Buffer buffer(rawbuffer, 512); + + agentList[0] = agent; + + requestsOutstanding = 1; + topicBound = false; + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(buffer, Protocol::OP_BROKER_REQUEST, sequence); + sendBufferLH(buffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT BrokerRequest seq=" << sequence); + } + + console.impl->eventAgentAdded(agent); +} + +void BrokerProxyImpl::sendBufferLH(Buffer& buf, const string& destination, const string& routingKey) +{ + uint32_t length = buf.getPosition(); + MessageImpl::Ptr message(new MessageImpl); + + buf.reset(); + buf.getRawData(message->body, length); + message->destination = destination; + message->routingKey = routingKey; + message->replyExchange = DIR_EXCHANGE; + message->replyKey = queueName; + + xmtQueue.push_back(message); +} + +void BrokerProxyImpl::handleRcvMessage(Message& message) +{ + Buffer inBuffer(message.body, message.length); + uint8_t opcode; + uint32_t sequence; + + while (Protocol::checkHeader(inBuffer, &opcode, &sequence)) + seqMgr.dispatch(opcode, sequence, message.routingKey ? string(message.routingKey) : string(), inBuffer); +} + +bool BrokerProxyImpl::getXmtMessage(Message& item) const +{ + Mutex::ScopedLock _lock(lock); + if (xmtQueue.empty()) + return false; + item = xmtQueue.front()->copy(); + return true; +} + +void BrokerProxyImpl::popXmt() +{ + Mutex::ScopedLock _lock(lock); + if (!xmtQueue.empty()) + xmtQueue.pop_front(); +} + +bool BrokerProxyImpl::getEvent(BrokerEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void BrokerProxyImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +uint32_t BrokerProxyImpl::agentCount() const +{ + Mutex::ScopedLock _lock(lock); + return agentList.size(); +} + +const AgentProxy* BrokerProxyImpl::getAgent(uint32_t idx) const +{ + Mutex::ScopedLock _lock(lock); + for (map<uint32_t, AgentProxyPtr>::const_iterator iter = agentList.begin(); + iter != agentList.end(); iter++) + if (idx-- == 0) + return iter->second.get(); + return 0; +} + +void BrokerProxyImpl::sendQuery(const Query& query, void* context, const AgentProxy* agent) +{ + SequenceContext::Ptr queryContext(new QueryContext(*this, context)); + Mutex::ScopedLock _lock(lock); + bool sent = false; + if (agent != 0) { + if (sendGetRequestLH(queryContext, query, agent)) + sent = true; + } else { + // TODO (optimization) only send queries to agents that have the requested class+package + for (map<uint32_t, AgentProxyPtr>::const_iterator iter = agentList.begin(); + iter != agentList.end(); iter++) { + if (sendGetRequestLH(queryContext, query, iter->second.get())) + sent = true; + } + } + + if (!sent) { + queryContext->reserve(); + queryContext->release(); + } +} + +bool BrokerProxyImpl::sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxy* agent) +{ + if (query.impl->singleAgent()) { + if (query.impl->agentBank() != agent->getAgentBank()) + return false; + } + stringstream key; + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve(queryContext)); + agent->impl->addSequence(sequence); + + Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); + query.impl->encode(outBuffer); + key << "agent.1." << agent->impl->agentBank; + sendBufferLH(outBuffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << key.str()); + return true; +} + +string BrokerProxyImpl::encodeMethodArguments(const SchemaMethod* schema, const Value* argmap, Buffer& buffer) +{ + int argCount = schema->getArgumentCount(); + + if (argmap == 0 || !argmap->isMap()) + return string("Arguments must be in a map value"); + + for (int aIdx = 0; aIdx < argCount; aIdx++) { + const SchemaArgument* arg(schema->getArgument(aIdx)); + if (arg->getDirection() == DIR_IN || arg->getDirection() == DIR_IN_OUT) { + if (argmap->keyInMap(arg->getName())) { + const Value* argVal(argmap->byKey(arg->getName())); + if (argVal->getType() != arg->getType()) + return string("Argument is the wrong type: ") + arg->getName(); + argVal->impl->encode(buffer); + } else { + Value defaultValue(arg->getType()); + defaultValue.impl->encode(buffer); + } + } + } + + return string(); +} + +string BrokerProxyImpl::encodedSizeMethodArguments(const SchemaMethod* schema, const Value* argmap, uint32_t& size) +{ + int argCount = schema->getArgumentCount(); + + if (argmap == 0 || !argmap->isMap()) + return string("Arguments must be in a map value"); + + for (int aIdx = 0; aIdx < argCount; aIdx++) { + const SchemaArgument* arg(schema->getArgument(aIdx)); + if (arg->getDirection() == DIR_IN || arg->getDirection() == DIR_IN_OUT) { + if (argmap->keyInMap(arg->getName())) { + const Value* argVal(argmap->byKey(arg->getName())); + if (argVal->getType() != arg->getType()) + return string("Argument is the wrong type: ") + arg->getName(); + size += argVal->impl->encodedSize(); + } else { + Value defaultValue(arg->getType()); + size += defaultValue.impl->encodedSize(); + } + } + } + + return string(); +} + +void BrokerProxyImpl::sendMethodRequest(ObjectId* oid, const SchemaObjectClass* cls, + const string& methodName, const Value* args, void* userContext) +{ + int methodCount = cls->getMethodCount(); + int idx; + for (idx = 0; idx < methodCount; idx++) { + const SchemaMethod* method = cls->getMethod(idx); + if (string(method->getName()) == methodName) { + Mutex::ScopedLock _lock(lock); + SequenceContext::Ptr methodContext(new MethodContext(*this, userContext, method)); + stringstream key; + char* buf(outputBuffer); + uint32_t bufLen(1024); + bool allocated(false); + + string argErrorString = encodedSizeMethodArguments(method, args, bufLen); + if (!argErrorString.empty()) { + MethodResponsePtr argError(MethodResponseImpl::factory(1, argErrorString)); + eventQueue.push_back(eventMethodResponse(userContext, argError)); + return; + } + + if (bufLen > MA_BUFFER_SIZE) { + buf = (char*) malloc(bufLen); + allocated = true; + } + + Buffer outBuffer(buf, bufLen); + uint32_t sequence(seqMgr.reserve(methodContext)); + + Protocol::encodeHeader(outBuffer, Protocol::OP_METHOD_REQUEST, sequence); + oid->impl->encode(outBuffer); + cls->getClassKey()->impl->encode(outBuffer); + outBuffer.putShortString(methodName); + + encodeMethodArguments(method, args, outBuffer); + key << "agent.1." << oid->impl->getAgentBank(); + sendBufferLH(outBuffer, QMF_EXCHANGE, key.str()); + QPID_LOG(trace, "SENT MethodRequest seq=" << sequence << " method=" << methodName << " key=" << key.str()); + + if (allocated) + free(buf); + + return; + } + } + + MethodResponsePtr error(MethodResponseImpl::factory(1, string("Unknown method: ") + methodName)); + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(eventMethodResponse(userContext, error)); +} + +void BrokerProxyImpl::addBinding(const string& exchange, const string& key) +{ + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(eventBind(exchange, queueName, key)); +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventDeclareQueue(const string& queueName) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::DECLARE_QUEUE)); + event->name = queueName; + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventBind(const string& exchange, const string& queue, const string& key) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::BIND)); + event->name = queue; + event->exchange = exchange; + event->bindingKey = key; + + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventSetupComplete() +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::SETUP_COMPLETE)); + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventStable() +{ + QPID_LOG(trace, "Console Link to Broker Stable"); + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::STABLE)); + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventQueryComplete(void* context, QueryResponsePtr response) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::QUERY_COMPLETE)); + event->context = context; + event->queryResponse = response; + return event; +} + +BrokerEventImpl::Ptr BrokerProxyImpl::eventMethodResponse(void* context, MethodResponsePtr response) +{ + BrokerEventImpl::Ptr event(new BrokerEventImpl(BrokerEvent::METHOD_RESPONSE)); + event->context = context; + event->methodResponse = response; + return event; +} + +void BrokerProxyImpl::handleBrokerResponse(Buffer& inBuffer, uint32_t seq) +{ + brokerId.decode(inBuffer); + QPID_LOG(trace, "RCVD BrokerResponse seq=" << seq << " brokerId=" << brokerId); + Mutex::ScopedLock _lock(lock); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + incOutstandingLH(); + Protocol::encodeHeader(outBuffer, Protocol::OP_PACKAGE_REQUEST, sequence); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT PackageRequest seq=" << sequence); +} + +void BrokerProxyImpl::handlePackageIndication(Buffer& inBuffer, uint32_t seq) +{ + string package; + + inBuffer.getShortString(package); + QPID_LOG(trace, "RCVD PackageIndication seq=" << seq << " package=" << package); + console.impl->learnPackage(package); + + Mutex::ScopedLock _lock(lock); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + incOutstandingLH(); + Protocol::encodeHeader(outBuffer, Protocol::OP_CLASS_QUERY, sequence); + outBuffer.putShortString(package); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT ClassQuery seq=" << sequence << " package=" << package); +} + +void BrokerProxyImpl::handleCommandComplete(Buffer& inBuffer, uint32_t seq) +{ + string text; + uint32_t code = inBuffer.getLong(); + inBuffer.getShortString(text); + QPID_LOG(trace, "RCVD CommandComplete seq=" << seq << " code=" << code << " text=" << text); +} + +void BrokerProxyImpl::handleClassIndication(Buffer& inBuffer, uint32_t seq) +{ + uint8_t kind = inBuffer.getOctet(); + auto_ptr<SchemaClassKey> classKey(SchemaClassKeyImpl::factory(inBuffer)); + + QPID_LOG(trace, "RCVD ClassIndication seq=" << seq << " kind=" << (int) kind << " key=" << classKey->impl->str()); + + if (!console.impl->haveClass(classKey.get())) { + Mutex::ScopedLock _lock(lock); + incOutstandingLH(); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(outBuffer, Protocol::OP_SCHEMA_REQUEST, sequence); + classKey->impl->encode(outBuffer); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_KEY); + QPID_LOG(trace, "SENT SchemaRequest seq=" << sequence <<" key=" << classKey->impl->str()); + } +} + +MethodResponsePtr BrokerProxyImpl::handleMethodResponse(Buffer& inBuffer, uint32_t seq, const SchemaMethod* schema) +{ + MethodResponsePtr response(MethodResponseImpl::factory(inBuffer, schema)); + + QPID_LOG(trace, "RCVD MethodResponse seq=" << seq << " status=" << response->getStatus() << " text=" << + response->getException()->asString()); + + return response; +} + +void BrokerProxyImpl::handleHeartbeatIndication(Buffer& inBuffer, uint32_t seq, const string& routingKey) +{ + vector<string> tokens = qpid::split(routingKey, "."); + uint32_t agentBank; + uint64_t timestamp; + + if (routingKey.empty() || tokens.size() != 4) + agentBank = 0; + else + agentBank = ::atoi(tokens[3].c_str()); + + timestamp = inBuffer.getLongLong(); + map<uint32_t, AgentProxyPtr>::const_iterator iter = agentList.find(agentBank); + if (iter != agentList.end()) { + console.impl->eventAgentHeartbeat(iter->second, timestamp); + } + QPID_LOG(trace, "RCVD HeartbeatIndication seq=" << seq << " agentBank=" << agentBank); +} + +void BrokerProxyImpl::handleEventIndication(Buffer& inBuffer, uint32_t seq) +{ + auto_ptr<SchemaClassKey> classKey(SchemaClassKeyImpl::factory(inBuffer)); + const SchemaEventClass *schema = console.impl->getEventClass(classKey.get()); + if (schema == 0) { + QPID_LOG(trace, "No Schema Found for EventIndication. seq=" << seq << " key=" << classKey->impl->str()); + return; + } + + EventPtr eptr(EventImpl::factory(schema, inBuffer)); + + console.impl->eventEventReceived(eptr); + QPID_LOG(trace, "RCVD EventIndication seq=" << seq << " key=" << classKey->impl->str()); +} + +void BrokerProxyImpl::handleSchemaResponse(Buffer& inBuffer, uint32_t seq) +{ + SchemaObjectClass* oClassPtr; + SchemaEventClass* eClassPtr; + uint8_t kind = inBuffer.getOctet(); + const SchemaClassKey* key; + if (kind == CLASS_OBJECT) { + oClassPtr = SchemaObjectClassImpl::factory(inBuffer); + console.impl->learnClass(oClassPtr); + key = oClassPtr->getClassKey(); + QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=object key=" << key->impl->str()); + + // + // If we have just learned about the org.apache.qpid.broker:agent class, send a get + // request for the current list of agents so we can have it on-hand before we declare + // this session "stable". + // + if (key->impl->getClassName() == AGENT_CLASS && key->impl->getPackageName() == BROKER_PACKAGE) { + Mutex::ScopedLock _lock(lock); + incOutstandingLH(); + Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE); + uint32_t sequence(seqMgr.reserve()); + Protocol::encodeHeader(outBuffer, Protocol::OP_GET_QUERY, sequence); + FieldTable ft; + ft.setString("_class", AGENT_CLASS); + ft.setString("_package", BROKER_PACKAGE); + ft.encode(outBuffer); + sendBufferLH(outBuffer, QMF_EXCHANGE, BROKER_AGENT_KEY); + QPID_LOG(trace, "SENT GetQuery seq=" << sequence << " key=" << BROKER_AGENT_KEY); + } + } else if (kind == CLASS_EVENT) { + eClassPtr = SchemaEventClassImpl::factory(inBuffer); + console.impl->learnClass(eClassPtr); + key = eClassPtr->getClassKey(); + QPID_LOG(trace, "RCVD SchemaResponse seq=" << seq << " kind=event key=" << key->impl->str()); + } + else { + QPID_LOG(error, "BrokerProxyImpl::handleSchemaResponse received unknown class kind: " << (int) kind); + } +} + +ObjectPtr BrokerProxyImpl::handleObjectIndication(Buffer& inBuffer, uint32_t seq, bool prop, bool stat) +{ + auto_ptr<SchemaClassKey> classKey(SchemaClassKeyImpl::factory(inBuffer)); + QPID_LOG(trace, "RCVD ObjectIndication seq=" << seq << " key=" << classKey->impl->str()); + + SchemaObjectClass* schema = console.impl->getSchema(classKey.get()); + if (schema == 0) { + QPID_LOG(trace, "No Schema Found for ObjectIndication. seq=" << seq << " key=" << classKey->impl->str()); + return ObjectPtr(); + } + + ObjectPtr optr(ObjectImpl::factory(schema, this, inBuffer, prop, stat, true)); + if (prop && classKey->impl->getPackageName() == BROKER_PACKAGE && classKey->impl->getClassName() == AGENT_CLASS) { + // + // We've intercepted information about a remote agent... update the agent list accordingly + // + updateAgentList(optr); + } + return optr; +} + +void BrokerProxyImpl::updateAgentList(ObjectPtr obj) +{ + Value* value = obj->getValue("agentBank"); + Mutex::ScopedLock _lock(lock); + if (value != 0 && value->isUint()) { + uint32_t agentBank = value->asUint(); + if (obj->isDeleted()) { + map<uint32_t, AgentProxyPtr>::iterator iter = agentList.find(agentBank); + if (iter != agentList.end()) { + AgentProxyPtr agent(iter->second); + console.impl->eventAgentDeleted(agent); + agentList.erase(agentBank); + QPID_LOG(trace, "Agent at bank " << agentBank << " removed from agent list"); + + // + // Release all sequence numbers for requests in-flight to this agent. + // Since the agent is no longer connected, these requests would not + // otherwise complete. + // + agent->impl->releaseInFlight(seqMgr); + } + } else { + Value* str = obj->getValue("label"); + string label; + if (str != 0 && str->isString()) + label = str->asString(); + map<uint32_t, AgentProxyPtr>::const_iterator iter = agentList.find(agentBank); + if (iter == agentList.end()) { + AgentProxyPtr agent(AgentProxyImpl::factory(console, publicObject, agentBank, label)); + agentList[agentBank] = agent; + console.impl->eventAgentAdded(agent); + QPID_LOG(trace, "Agent '" << label << "' found at bank " << agentBank); + } + } + } +} + +void BrokerProxyImpl::incOutstandingLH() +{ + requestsOutstanding++; +} + +void BrokerProxyImpl::decOutstanding() +{ + Mutex::ScopedLock _lock(lock); + requestsOutstanding--; + if (requestsOutstanding == 0 && !topicBound) { + topicBound = true; + for (vector<pair<string, string> >::const_iterator iter = console.impl->bindingList.begin(); + iter != console.impl->bindingList.end(); iter++) { + string exchange(iter->first.empty() ? QMF_EXCHANGE : iter->first); + string key(iter->second); + eventQueue.push_back(eventBind(exchange, queueName, key)); + } + eventQueue.push_back(eventStable()); + } +} + +MethodResponseImpl::MethodResponseImpl(const MethodResponseImpl& from) : + status(from.status), schema(from.schema) +{ + if (from.exception.get()) + exception.reset(new Value(*(from.exception))); + if (from.arguments.get()) + arguments.reset(new Value(*(from.arguments))); +} + +MethodResponseImpl::MethodResponseImpl(Buffer& buf, const SchemaMethod* s) : schema(s) +{ + string text; + + status = buf.getLong(); + buf.getMediumString(text); + exception.reset(new Value(TYPE_LSTR)); + exception->setString(text.c_str()); + + if (status != 0) + return; + + arguments.reset(new Value(TYPE_MAP)); + int argCount(schema->getArgumentCount()); + for (int idx = 0; idx < argCount; idx++) { + const SchemaArgument* arg = schema->getArgument(idx); + if (arg->getDirection() == DIR_OUT || arg->getDirection() == DIR_IN_OUT) { + Value* value(ValueImpl::factory(arg->getType(), buf)); + arguments->insert(arg->getName(), value); + } + } +} + +MethodResponseImpl::MethodResponseImpl(uint32_t s, const string& text) : schema(0) +{ + status = s; + exception.reset(new Value(TYPE_LSTR)); + exception->setString(text.c_str()); +} + +MethodResponse* MethodResponseImpl::factory(Buffer& buf, const SchemaMethod* schema) +{ + MethodResponseImpl* impl(new MethodResponseImpl(buf, schema)); + return new MethodResponse(impl); +} + +MethodResponse* MethodResponseImpl::factory(uint32_t status, const std::string& text) +{ + MethodResponseImpl* impl(new MethodResponseImpl(status, text)); + return new MethodResponse(impl); +} + +bool StaticContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& routingKey, Buffer& buffer) +{ + ObjectPtr object; + bool completeContext = false; + + if (opcode == Protocol::OP_BROKER_RESPONSE) { + broker.handleBrokerResponse(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_COMMAND_COMPLETE) { + broker.handleCommandComplete(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_SCHEMA_RESPONSE) { + broker.handleSchemaResponse(buffer, sequence); + completeContext = true; + } + else if (opcode == Protocol::OP_PACKAGE_INDICATION) + broker.handlePackageIndication(buffer, sequence); + else if (opcode == Protocol::OP_CLASS_INDICATION) + broker.handleClassIndication(buffer, sequence); + else if (opcode == Protocol::OP_HEARTBEAT_INDICATION) + broker.handleHeartbeatIndication(buffer, sequence, routingKey); + else if (opcode == Protocol::OP_EVENT_INDICATION) + broker.handleEventIndication(buffer, sequence); + else if (opcode == Protocol::OP_PROPERTY_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, true, false); + broker.console.impl->eventObjectUpdate(object, true, false); + } + else if (opcode == Protocol::OP_STATISTIC_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, false, true); + broker.console.impl->eventObjectUpdate(object, false, true); + } + else if (opcode == Protocol::OP_OBJECT_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, true, true); + broker.console.impl->eventObjectUpdate(object, true, true); + } + else { + QPID_LOG(trace, "StaticContext::handleMessage invalid opcode: " << opcode); + completeContext = true; + } + + return completeContext; +} + +void QueryContext::reserve() +{ + Mutex::ScopedLock _lock(lock); + requestsOutstanding++; +} + +void QueryContext::release() +{ + { + Mutex::ScopedLock _lock(lock); + if (--requestsOutstanding > 0) + return; + } + + Mutex::ScopedLock _block(broker.lock); + broker.eventQueue.push_back(broker.eventQueryComplete(userContext, queryResponse)); +} + +bool QueryContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& /*routingKey*/, Buffer& buffer) +{ + bool completeContext = false; + ObjectPtr object; + + if (opcode == Protocol::OP_COMMAND_COMPLETE) { + broker.handleCommandComplete(buffer, sequence); + completeContext = true; + + // + // Visit each agent and remove the sequence from that agent's in-flight list. + // This could be made more efficient because only one agent will have this sequence + // in its list. + // + map<uint32_t, AgentProxyPtr> copy; + { + Mutex::ScopedLock _block(broker.lock); + copy = broker.agentList; + } + for (map<uint32_t, AgentProxyPtr>::iterator iter = copy.begin(); iter != copy.end(); iter++) + iter->second->impl->delSequence(sequence); + } + else if (opcode == Protocol::OP_OBJECT_INDICATION) { + object = broker.handleObjectIndication(buffer, sequence, true, true); + if (object.get() != 0) + queryResponse->impl->results.push_back(object); + } + else { + QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode); + completeContext = true; + } + + return completeContext; +} + +void MethodContext::release() +{ + Mutex::ScopedLock _block(broker.lock); + broker.eventQueue.push_back(broker.eventMethodResponse(userContext, methodResponse)); +} + +bool MethodContext::handleMessage(uint8_t opcode, uint32_t sequence, const string& /*routingKey*/, Buffer& buffer) +{ + if (opcode == Protocol::OP_METHOD_RESPONSE) + methodResponse = broker.handleMethodResponse(buffer, sequence, schema); + else + QPID_LOG(trace, "QueryContext::handleMessage invalid opcode: " << opcode); + + return true; +} + + +//================================================================== +// Wrappers +//================================================================== + +AgentProxy::AgentProxy(AgentProxyImpl* i) : impl(i) {} +AgentProxy::AgentProxy(const AgentProxy& from) : impl(new AgentProxyImpl(*(from.impl))) {} +AgentProxy::~AgentProxy() { delete impl; } +const char* AgentProxy::getLabel() const { return impl->getLabel().c_str(); } +uint32_t AgentProxy::getBrokerBank() const { return impl->getBrokerBank(); } +uint32_t AgentProxy::getAgentBank() const { return impl->getAgentBank(); } + +BrokerProxy::BrokerProxy(Console& console) : impl(new BrokerProxyImpl(*this, console)) {} +BrokerProxy::~BrokerProxy() { delete impl; } +void BrokerProxy::sessionOpened(SessionHandle& sh) { impl->sessionOpened(sh); } +void BrokerProxy::sessionClosed() { impl->sessionClosed(); } +void BrokerProxy::startProtocol() { impl->startProtocol(); } +void BrokerProxy::handleRcvMessage(Message& message) { impl->handleRcvMessage(message); } +bool BrokerProxy::getXmtMessage(Message& item) const { return impl->getXmtMessage(item); } +void BrokerProxy::popXmt() { impl->popXmt(); } +bool BrokerProxy::getEvent(BrokerEvent& event) const { return impl->getEvent(event); } +void BrokerProxy::popEvent() { impl->popEvent(); } +uint32_t BrokerProxy::agentCount() const { return impl->agentCount(); } +const AgentProxy* BrokerProxy::getAgent(uint32_t idx) const { return impl->getAgent(idx); } +void BrokerProxy::sendQuery(const Query& query, void* context, const AgentProxy* agent) { impl->sendQuery(query, context, agent); } + +MethodResponse::MethodResponse(const MethodResponse& from) : impl(new MethodResponseImpl(*(from.impl))) {} +MethodResponse::MethodResponse(MethodResponseImpl* i) : impl(i) {} +MethodResponse::~MethodResponse() {} +uint32_t MethodResponse::getStatus() const { return impl->getStatus(); } +const Value* MethodResponse::getException() const { return impl->getException(); } +const Value* MethodResponse::getArgs() const { return impl->getArgs(); } + +QueryResponse::QueryResponse(QueryResponseImpl* i) : impl(i) {} +QueryResponse::~QueryResponse() {} +uint32_t QueryResponse::getStatus() const { return impl->getStatus(); } +const Value* QueryResponse::getException() const { return impl->getException(); } +uint32_t QueryResponse::getObjectCount() const { return impl->getObjectCount(); } +const Object* QueryResponse::getObject(uint32_t idx) const { return impl->getObject(idx); } + diff --git a/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h new file mode 100644 index 0000000000..0542b67dbb --- /dev/null +++ b/qpid/cpp/src/qmf/engine/BrokerProxyImpl.h @@ -0,0 +1,241 @@ +#ifndef _QmfEngineBrokerProxyImpl_ +#define _QmfEngineBrokerProxyImpl_ + +/* + * 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 "qmf/engine/Console.h" +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/EventImpl.h" +#include "qmf/engine/SchemaImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/SequenceManager.h" +#include "qmf/engine/MessageImpl.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/Mutex.h" +#include "boost/shared_ptr.hpp" +#include "boost/noncopyable.hpp" +#include <memory> +#include <string> +#include <deque> +#include <map> +#include <set> +#include <vector> + +namespace qmf { +namespace engine { + + typedef boost::shared_ptr<MethodResponse> MethodResponsePtr; + struct MethodResponseImpl { + uint32_t status; + const SchemaMethod* schema; + std::auto_ptr<Value> exception; + std::auto_ptr<Value> arguments; + + MethodResponseImpl(const MethodResponseImpl& from); + MethodResponseImpl(qpid::framing::Buffer& buf, const SchemaMethod* schema); + MethodResponseImpl(uint32_t status, const std::string& text); + static MethodResponse* factory(qpid::framing::Buffer& buf, const SchemaMethod* schema); + static MethodResponse* factory(uint32_t status, const std::string& text); + ~MethodResponseImpl() {} + uint32_t getStatus() const { return status; } + const Value* getException() const { return exception.get(); } + const Value* getArgs() const { return arguments.get(); } + }; + + typedef boost::shared_ptr<QueryResponse> QueryResponsePtr; + struct QueryResponseImpl { + uint32_t status; + std::auto_ptr<Value> exception; + std::vector<ObjectPtr> results; + + QueryResponseImpl() : status(0) {} + static QueryResponse* factory() { + QueryResponseImpl* impl(new QueryResponseImpl()); + return new QueryResponse(impl); + } + ~QueryResponseImpl() {} + uint32_t getStatus() const { return status; } + const Value* getException() const { return exception.get(); } + uint32_t getObjectCount() const { return results.size(); } + const Object* getObject(uint32_t idx) const; + }; + + struct BrokerEventImpl { + typedef boost::shared_ptr<BrokerEventImpl> Ptr; + BrokerEvent::EventKind kind; + std::string name; + std::string exchange; + std::string bindingKey; + void* context; + QueryResponsePtr queryResponse; + MethodResponsePtr methodResponse; + + BrokerEventImpl(BrokerEvent::EventKind k) : kind(k), context(0) {} + ~BrokerEventImpl() {} + BrokerEvent copy(); + }; + + typedef boost::shared_ptr<AgentProxy> AgentProxyPtr; + struct AgentProxyImpl { + Console& console; + BrokerProxy& broker; + uint32_t agentBank; + std::string label; + std::set<uint32_t> inFlightSequences; + + AgentProxyImpl(Console& c, BrokerProxy& b, uint32_t ab, const std::string& l) : console(c), broker(b), agentBank(ab), label(l) {} + static AgentProxy* factory(Console& c, BrokerProxy& b, uint32_t ab, const std::string& l) { + AgentProxyImpl* impl(new AgentProxyImpl(c, b, ab, l)); + return new AgentProxy(impl); + } + ~AgentProxyImpl() {} + const std::string& getLabel() const { return label; } + uint32_t getBrokerBank() const { return 1; } + uint32_t getAgentBank() const { return agentBank; } + void addSequence(uint32_t seq) { inFlightSequences.insert(seq); } + void delSequence(uint32_t seq) { inFlightSequences.erase(seq); } + void releaseInFlight(SequenceManager& seqMgr) { + for (std::set<uint32_t>::iterator iter = inFlightSequences.begin(); iter != inFlightSequences.end(); iter++) + seqMgr.release(*iter); + inFlightSequences.clear(); + } + }; + + class BrokerProxyImpl : public boost::noncopyable { + public: + BrokerProxyImpl(BrokerProxy& pub, Console& _console); + ~BrokerProxyImpl() {} + + void sessionOpened(SessionHandle& sh); + void sessionClosed(); + void startProtocol(); + + void sendBufferLH(qpid::framing::Buffer& buf, const std::string& destination, const std::string& routingKey); + void handleRcvMessage(Message& message); + bool getXmtMessage(Message& item) const; + void popXmt(); + + bool getEvent(BrokerEvent& event) const; + void popEvent(); + + uint32_t agentCount() const; + const AgentProxy* getAgent(uint32_t idx) const; + void sendQuery(const Query& query, void* context, const AgentProxy* agent); + bool sendGetRequestLH(SequenceContext::Ptr queryContext, const Query& query, const AgentProxy* agent); + std::string encodeMethodArguments(const SchemaMethod* schema, const Value* args, qpid::framing::Buffer& buffer); + std::string encodedSizeMethodArguments(const SchemaMethod* schema, const Value* args, uint32_t& size); + void sendMethodRequest(ObjectId* oid, const SchemaObjectClass* cls, const std::string& method, const Value* args, void* context); + + void addBinding(const std::string& exchange, const std::string& key); + void staticRelease() { decOutstanding(); } + + private: + friend struct StaticContext; + friend struct QueryContext; + friend struct MethodContext; + BrokerProxy& publicObject; + mutable qpid::sys::Mutex lock; + Console& console; + std::string queueName; + qpid::framing::Uuid brokerId; + SequenceManager seqMgr; + uint32_t requestsOutstanding; + bool topicBound; + std::map<uint32_t, AgentProxyPtr> agentList; + std::deque<MessageImpl::Ptr> xmtQueue; + std::deque<BrokerEventImpl::Ptr> eventQueue; + +# define MA_BUFFER_SIZE 65536 + char outputBuffer[MA_BUFFER_SIZE]; + + BrokerEventImpl::Ptr eventDeclareQueue(const std::string& queueName); + BrokerEventImpl::Ptr eventBind(const std::string& exchange, const std::string& queue, const std::string& key); + BrokerEventImpl::Ptr eventSetupComplete(); + BrokerEventImpl::Ptr eventStable(); + BrokerEventImpl::Ptr eventQueryComplete(void* context, QueryResponsePtr response); + BrokerEventImpl::Ptr eventMethodResponse(void* context, MethodResponsePtr response); + + void handleBrokerResponse(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handlePackageIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleCommandComplete(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleClassIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + MethodResponsePtr handleMethodResponse(qpid::framing::Buffer& inBuffer, uint32_t seq, const SchemaMethod* schema); + void handleHeartbeatIndication(qpid::framing::Buffer& inBuffer, uint32_t seq, const std::string& routingKey); + void handleEventIndication(qpid::framing::Buffer& inBuffer, uint32_t seq); + void handleSchemaResponse(qpid::framing::Buffer& inBuffer, uint32_t seq); + ObjectPtr handleObjectIndication(qpid::framing::Buffer& inBuffer, uint32_t seq, bool prop, bool stat); + void updateAgentList(ObjectPtr obj); + void incOutstandingLH(); + void decOutstanding(); + }; + + // + // StaticContext is used to handle: + // + // 1) Responses to console-level requests (for schema info, etc.) + // 2) Unsolicited messages from agents (events, published updates, etc.) + // + struct StaticContext : public SequenceContext { + StaticContext(BrokerProxyImpl& b) : broker(b) {} + virtual ~StaticContext() {} + void reserve() {} + void release() { broker.staticRelease(); } + bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer); + BrokerProxyImpl& broker; + }; + + // + // QueryContext is used to track and handle responses associated with a single Get Query + // + struct QueryContext : public SequenceContext { + QueryContext(BrokerProxyImpl& b, void* u) : + broker(b), userContext(u), requestsOutstanding(0), queryResponse(QueryResponseImpl::factory()) {} + virtual ~QueryContext() {} + void reserve(); + void release(); + bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer); + + mutable qpid::sys::Mutex lock; + BrokerProxyImpl& broker; + void* userContext; + uint32_t requestsOutstanding; + QueryResponsePtr queryResponse; + }; + + struct MethodContext : public SequenceContext { + MethodContext(BrokerProxyImpl& b, void* u, const SchemaMethod* s) : broker(b), userContext(u), schema(s) {} + virtual ~MethodContext() {} + void reserve() {} + void release(); + bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer); + + BrokerProxyImpl& broker; + void* userContext; + const SchemaMethod* schema; + MethodResponsePtr methodResponse; + }; + +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp new file mode 100644 index 0000000000..22a65f28ca --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.cpp @@ -0,0 +1,278 @@ +/* + * 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 "qmf/engine/ConnectionSettingsImpl.h" +#include "qmf/engine/Typecode.h" + +using namespace std; +using namespace qmf::engine; +using namespace qpid; + +const string attrProtocol("protocol"); +const string attrHost("host"); +const string attrPort("port"); +const string attrVirtualhost("virtualhost"); +const string attrUsername("username"); +const string attrPassword("password"); +const string attrMechanism("mechanism"); +const string attrLocale("locale"); +const string attrHeartbeat("heartbeat"); +const string attrMaxChannels("maxChannels"); +const string attrMaxFrameSize("maxFrameSize"); +const string attrBounds("bounds"); +const string attrTcpNoDelay("tcpNoDelay"); +const string attrService("service"); +const string attrMinSsf("minSsf"); +const string attrMaxSsf("maxSsf"); +const string attrRetryDelayMin("retryDelayMin"); +const string attrRetryDelayMax("retryDelayMax"); +const string attrRetryDelayFactor("retryDelayFactor"); +const string attrSendUserId("sendUserId"); + +ConnectionSettingsImpl::ConnectionSettingsImpl() : + retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2), sendUserId(true) +{ +} + +ConnectionSettingsImpl::ConnectionSettingsImpl(const string& /*url*/) : + retryDelayMin(1), retryDelayMax(64), retryDelayFactor(2), sendUserId(true) +{ + // TODO: Parse the URL +} + +bool ConnectionSettingsImpl::setAttr(const string& key, const Value& value) +{ + if (key == attrProtocol) clientSettings.protocol = value.asString(); + else if (key == attrHost) clientSettings.host = value.asString(); + else if (key == attrPort) clientSettings.port = value.asUint(); + else if (key == attrVirtualhost) clientSettings.virtualhost = value.asString(); + else if (key == attrUsername) clientSettings.username = value.asString(); + else if (key == attrPassword) clientSettings.password = value.asString(); + else if (key == attrMechanism) clientSettings.mechanism = value.asString(); + else if (key == attrLocale) clientSettings.locale = value.asString(); + else if (key == attrHeartbeat) clientSettings.heartbeat = value.asUint(); + else if (key == attrMaxChannels) clientSettings.maxChannels = value.asUint(); + else if (key == attrMaxFrameSize) clientSettings.maxFrameSize = value.asUint(); + else if (key == attrBounds) clientSettings.bounds = value.asUint(); + else if (key == attrTcpNoDelay) clientSettings.tcpNoDelay = value.asBool(); + else if (key == attrService) clientSettings.service = value.asString(); + else if (key == attrMinSsf) clientSettings.minSsf = value.asUint(); + else if (key == attrMaxSsf) clientSettings.maxSsf = value.asUint(); + + else if (key == attrRetryDelayMin) retryDelayMin = value.asUint(); + else if (key == attrRetryDelayMax) retryDelayMax = value.asUint(); + else if (key == attrRetryDelayFactor) retryDelayFactor = value.asUint(); + else if (key == attrSendUserId) sendUserId = value.asBool(); + else + return false; + return true; +} + +Value ConnectionSettingsImpl::getAttr(const string& key) const +{ + Value strval(TYPE_LSTR); + Value intval(TYPE_UINT32); + Value boolval(TYPE_BOOL); + + if (key == attrProtocol) { + strval.setString(clientSettings.protocol.c_str()); + return strval; + } + + if (key == attrHost) { + strval.setString(clientSettings.host.c_str()); + return strval; + } + + if (key == attrPort) { + intval.setUint(clientSettings.port); + return intval; + } + + if (key == attrVirtualhost) { + strval.setString(clientSettings.virtualhost.c_str()); + return strval; + } + + if (key == attrUsername) { + strval.setString(clientSettings.username.c_str()); + return strval; + } + + if (key == attrPassword) { + strval.setString(clientSettings.password.c_str()); + return strval; + } + + if (key == attrMechanism) { + strval.setString(clientSettings.mechanism.c_str()); + return strval; + } + + if (key == attrLocale) { + strval.setString(clientSettings.locale.c_str()); + return strval; + } + + if (key == attrHeartbeat) { + intval.setUint(clientSettings.heartbeat); + return intval; + } + + if (key == attrMaxChannels) { + intval.setUint(clientSettings.maxChannels); + return intval; + } + + if (key == attrMaxFrameSize) { + intval.setUint(clientSettings.maxFrameSize); + return intval; + } + + if (key == attrBounds) { + intval.setUint(clientSettings.bounds); + return intval; + } + + if (key == attrTcpNoDelay) { + boolval.setBool(clientSettings.tcpNoDelay); + return boolval; + } + + if (key == attrService) { + strval.setString(clientSettings.service.c_str()); + return strval; + } + + if (key == attrMinSsf) { + intval.setUint(clientSettings.minSsf); + return intval; + } + + if (key == attrMaxSsf) { + intval.setUint(clientSettings.maxSsf); + return intval; + } + + if (key == attrRetryDelayMin) { + intval.setUint(retryDelayMin); + return intval; + } + + if (key == attrRetryDelayMax) { + intval.setUint(retryDelayMax); + return intval; + } + + if (key == attrRetryDelayFactor) { + intval.setUint(retryDelayFactor); + return intval; + } + + if (key == attrSendUserId) { + boolval.setBool(sendUserId); + return boolval; + } + + return strval; +} + +const string& ConnectionSettingsImpl::getAttrString() const +{ + // TODO: build and return attribute string + return attrString; +} + +void ConnectionSettingsImpl::transportTcp(uint16_t port) +{ + clientSettings.protocol = "tcp"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::transportSsl(uint16_t port) +{ + clientSettings.protocol = "ssl"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::transportRdma(uint16_t port) +{ + clientSettings.protocol = "rdma"; + clientSettings.port = port; +} + +void ConnectionSettingsImpl::authAnonymous(const string& username) +{ + clientSettings.mechanism = "ANONYMOUS"; + clientSettings.username = username; +} + +void ConnectionSettingsImpl::authPlain(const string& username, const string& password) +{ + clientSettings.mechanism = "PLAIN"; + clientSettings.username = username; + clientSettings.password = password; +} + +void ConnectionSettingsImpl::authGssapi(const string& serviceName, uint32_t minSsf, uint32_t maxSsf) +{ + clientSettings.mechanism = "GSSAPI"; + clientSettings.service = serviceName; + clientSettings.minSsf = minSsf; + clientSettings.maxSsf = maxSsf; +} + +void ConnectionSettingsImpl::setRetry(int delayMin, int delayMax, int delayFactor) +{ + retryDelayMin = delayMin; + retryDelayMax = delayMax; + retryDelayFactor = delayFactor; +} + +const client::ConnectionSettings& ConnectionSettingsImpl::getClientSettings() const +{ + return clientSettings; +} + +void ConnectionSettingsImpl::getRetrySettings(int* min, int* max, int* factor) const +{ + *min = retryDelayMin; + *max = retryDelayMax; + *factor = retryDelayFactor; +} + +//================================================================== +// Wrappers +//================================================================== + +ConnectionSettings::ConnectionSettings(const ConnectionSettings& from) { impl = new ConnectionSettingsImpl(*from.impl); } +ConnectionSettings::ConnectionSettings() { impl = new ConnectionSettingsImpl(); } +ConnectionSettings::ConnectionSettings(const char* url) { impl = new ConnectionSettingsImpl(url); } +ConnectionSettings::~ConnectionSettings() { delete impl; } +bool ConnectionSettings::setAttr(const char* key, const Value& value) { return impl->setAttr(key, value); } +Value ConnectionSettings::getAttr(const char* key) const { return impl->getAttr(key); } +const char* ConnectionSettings::getAttrString() const { return impl->getAttrString().c_str(); } +void ConnectionSettings::transportTcp(uint16_t port) { impl->transportTcp(port); } +void ConnectionSettings::transportSsl(uint16_t port) { impl->transportSsl(port); } +void ConnectionSettings::transportRdma(uint16_t port) { impl->transportRdma(port); } +void ConnectionSettings::authAnonymous(const char* username) { impl->authAnonymous(username); } +void ConnectionSettings::authPlain(const char* username, const char* password) { impl->authPlain(username, password); } +void ConnectionSettings::authGssapi(const char* serviceName, uint32_t minSsf, uint32_t maxSsf) { impl->authGssapi(serviceName, minSsf, maxSsf); } +void ConnectionSettings::setRetry(int delayMin, int delayMax, int delayFactor) { impl->setRetry(delayMin, delayMax, delayFactor); } + diff --git a/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h new file mode 100644 index 0000000000..98bf87868b --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ConnectionSettingsImpl.h @@ -0,0 +1,63 @@ +#ifndef _QmfEngineConnectionSettingsImpl_ +#define _QmfEngineConnectionSettingsImpl_ + +/* + * 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 "qmf/engine/ConnectionSettings.h" +#include "qmf/engine/Value.h" +#include "qpid/client/ConnectionSettings.h" +#include <string> +#include <map> + +namespace qmf { +namespace engine { + + class ConnectionSettingsImpl { + qpid::client::ConnectionSettings clientSettings; + mutable std::string attrString; + int retryDelayMin; + int retryDelayMax; + int retryDelayFactor; + bool sendUserId; + + public: + ConnectionSettingsImpl(); + ConnectionSettingsImpl(const std::string& url); + ~ConnectionSettingsImpl() {} + bool setAttr(const std::string& key, const Value& value); + Value getAttr(const std::string& key) const; + const std::string& getAttrString() const; + void transportTcp(uint16_t port); + void transportSsl(uint16_t port); + void transportRdma(uint16_t port); + void authAnonymous(const std::string& username); + void authPlain(const std::string& username, const std::string& password); + void authGssapi(const std::string& serviceName, uint32_t minSsf, uint32_t maxSsf); + void setRetry(int delayMin, int delayMax, int delayFactor); + + const qpid::client::ConnectionSettings& getClientSettings() const; + void getRetrySettings(int* delayMin, int* delayMax, int* delayFactor) const; + bool getSendUserId() const { return sendUserId; } + }; + +} +} + +#endif diff --git a/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp b/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp new file mode 100644 index 0000000000..4a5da31bdc --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ConsoleImpl.cpp @@ -0,0 +1,458 @@ +/* + * 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 "qmf/engine/ConsoleImpl.h" +#include "qmf/engine/MessageImpl.h" +#include "qmf/engine/SchemaImpl.h" +#include "qmf/engine/Typecode.h" +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/ObjectIdImpl.h" +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/Protocol.h" +#include "qmf/engine/SequenceManager.h" +#include "qmf/engine/BrokerProxyImpl.h" +#include <qpid/framing/Buffer.h> +#include <qpid/framing/Uuid.h> +#include <qpid/framing/FieldTable.h> +#include <qpid/framing/FieldValue.h> +#include <qpid/log/Statement.h> +#include <qpid/sys/Time.h> +#include <qpid/sys/SystemInfo.h> +#include <string.h> +#include <iostream> +#include <fstream> + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace { + const char* QMF_EXCHANGE = "qpid.management"; +} + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());} + +ConsoleEvent ConsoleEventImpl::copy() +{ + ConsoleEvent item; + + ::memset(&item, 0, sizeof(ConsoleEvent)); + item.kind = kind; + item.agent = agent.get(); + item.classKey = classKey; + item.object = object.get(); + item.context = context; + item.event = event.get(); + item.timestamp = timestamp; + item.hasProps = hasProps; + item.hasStats = hasStats; + + STRING_REF(name); + + return item; +} + +ConsoleImpl::ConsoleImpl(const ConsoleSettings& s) : settings(s) +{ + bindingList.push_back(pair<string, string>(string(), "schema.#")); + if (settings.rcvObjects && settings.rcvEvents && settings.rcvHeartbeats && !settings.userBindings) { + bindingList.push_back(pair<string, string>(string(), "console.#")); + } else { + if (settings.rcvObjects && !settings.userBindings) + bindingList.push_back(pair<string, string>(string(), "console.obj.#")); + else + bindingList.push_back(pair<string, string>(string(), "console.obj.*.*.org.apache.qpid.broker.agent")); + if (settings.rcvEvents) + bindingList.push_back(pair<string, string>(string(), "console.event.#")); + if (settings.rcvHeartbeats) + bindingList.push_back(pair<string, string>(string(), "console.heartbeat.#")); + } +} + +ConsoleImpl::~ConsoleImpl() +{ + // This function intentionally left blank. +} + +bool ConsoleImpl::getEvent(ConsoleEvent& event) const +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front()->copy(); + return true; +} + +void ConsoleImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +void ConsoleImpl::addConnection(BrokerProxy& broker, void* /*context*/) +{ + Mutex::ScopedLock _lock(lock); + brokerList.push_back(broker.impl); +} + +void ConsoleImpl::delConnection(BrokerProxy& broker) +{ + Mutex::ScopedLock _lock(lock); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + if (*iter == broker.impl) { + brokerList.erase(iter); + break; + } +} + +uint32_t ConsoleImpl::packageCount() const +{ + Mutex::ScopedLock _lock(lock); + return packages.size(); +} + +const string& ConsoleImpl::getPackageName(uint32_t idx) const +{ + const static string empty; + + Mutex::ScopedLock _lock(lock); + if (idx >= packages.size()) + return empty; + + PackageList::const_iterator iter = packages.begin(); + for (uint32_t i = 0; i < idx; i++) iter++; + return iter->first; +} + +uint32_t ConsoleImpl::classCount(const char* packageName) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + + return oList.size() + eList.size(); +} + +const SchemaClassKey* ConsoleImpl::getClass(const char* packageName, uint32_t idx) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(packageName); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + uint32_t count = 0; + + for (ObjectClassList::const_iterator oIter = oList.begin(); + oIter != oList.end(); oIter++) { + if (count == idx) + return oIter->second->getClassKey(); + count++; + } + + for (EventClassList::const_iterator eIter = eList.begin(); + eIter != eList.end(); eIter++) { + if (count == idx) + return eIter->second->getClassKey(); + count++; + } + + return 0; +} + +ClassKind ConsoleImpl::getClassKind(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return CLASS_OBJECT; + + const EventClassList& eList = pIter->second.second; + if (eList.find(key) != eList.end()) + return CLASS_EVENT; + return CLASS_OBJECT; +} + +const SchemaObjectClass* ConsoleImpl::getObjectClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + ObjectClassList::const_iterator iter = oList.find(key); + if (iter == oList.end()) + return 0; + return iter->second; +} + +const SchemaEventClass* ConsoleImpl::getEventClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const EventClassList& eList = pIter->second.second; + EventClassList::const_iterator iter = eList.find(key); + if (iter == eList.end()) + return 0; + return iter->second; +} + +void ConsoleImpl::bindPackage(const char* packageName) +{ + stringstream key; + key << "console.obj.*.*." << packageName << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair<string, string>(string(), key.str())); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +void ConsoleImpl::bindClass(const SchemaClassKey* classKey) +{ + stringstream key; + key << "console.obj.*.*." << classKey->getPackageName() << "." << classKey->getClassName() << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair<string, string>(string(), key.str())); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +void ConsoleImpl::bindClass(const char* packageName, const char* className) +{ + stringstream key; + key << "console.obj.*.*." << packageName << "." << className << ".#"; + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair<string, string>(string(), key.str())); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + + +void ConsoleImpl::bindEvent(const SchemaClassKey* classKey) +{ + bindEvent(classKey->getPackageName(), classKey->getClassName()); +} + +void ConsoleImpl::bindEvent(const char* packageName, const char* eventName) +{ + if (!settings.userBindings) throw qpid::Exception("Console not configured for userBindings."); + if (settings.rcvEvents) throw qpid::Exception("Console already configured to receive all events."); + + stringstream key; + key << "console.event.*.*." << packageName; + if (eventName && *eventName) { + key << "." << eventName << ".#"; + } else { + key << ".#"; + } + + Mutex::ScopedLock _lock(lock); + bindingList.push_back(pair<string, string>(string(), key.str())); + for (vector<BrokerProxyImpl*>::iterator iter = brokerList.begin(); + iter != brokerList.end(); iter++) + (*iter)->addBinding(QMF_EXCHANGE, key.str()); +} + +/* +void ConsoleImpl::startSync(const Query& query, void* context, SyncQuery& sync) +{ +} + +void ConsoleImpl::touchSync(SyncQuery& sync) +{ +} + +void ConsoleImpl::endSync(SyncQuery& sync) +{ +} +*/ + +void ConsoleImpl::learnPackage(const string& packageName) +{ + Mutex::ScopedLock _lock(lock); + if (packages.find(packageName) == packages.end()) { + packages.insert(pair<string, pair<ObjectClassList, EventClassList> > + (packageName, pair<ObjectClassList, EventClassList>(ObjectClassList(), EventClassList()))); + eventNewPackage(packageName); + } +} + +void ConsoleImpl::learnClass(SchemaObjectClass* cls) +{ + Mutex::ScopedLock _lock(lock); + const SchemaClassKey* key = cls->getClassKey(); + PackageList::iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return; + + ObjectClassList& list = pIter->second.first; + if (list.find(key) == list.end()) { + list[key] = cls; + eventNewClass(key); + } +} + +void ConsoleImpl::learnClass(SchemaEventClass* cls) +{ + Mutex::ScopedLock _lock(lock); + const SchemaClassKey* key = cls->getClassKey(); + PackageList::iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return; + + EventClassList& list = pIter->second.second; + if (list.find(key) == list.end()) { + list[key] = cls; + eventNewClass(key); + } +} + +bool ConsoleImpl::haveClass(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return false; + + const ObjectClassList& oList = pIter->second.first; + const EventClassList& eList = pIter->second.second; + + return oList.find(key) != oList.end() || eList.find(key) != eList.end(); +} + +SchemaObjectClass* ConsoleImpl::getSchema(const SchemaClassKey* key) const +{ + Mutex::ScopedLock _lock(lock); + PackageList::const_iterator pIter = packages.find(key->getPackageName()); + if (pIter == packages.end()) + return 0; + + const ObjectClassList& oList = pIter->second.first; + ObjectClassList::const_iterator iter = oList.find(key); + if (iter == oList.end()) + return 0; + + return iter->second; +} + +void ConsoleImpl::eventAgentAdded(boost::shared_ptr<AgentProxy> agent) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_ADDED)); + event->agent = agent; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventAgentDeleted(boost::shared_ptr<AgentProxy> agent) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_DELETED)); + event->agent = agent; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventNewPackage(const string& packageName) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::NEW_PACKAGE)); + event->name = packageName; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventNewClass(const SchemaClassKey* key) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::NEW_CLASS)); + event->classKey = key; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventObjectUpdate(ObjectPtr object, bool prop, bool stat) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::OBJECT_UPDATE)); + event->object = object; + event->hasProps = prop; + event->hasStats = stat; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + +void ConsoleImpl::eventAgentHeartbeat(boost::shared_ptr<AgentProxy> agent, uint64_t timestamp) +{ + ConsoleEventImpl::Ptr event(new ConsoleEventImpl(ConsoleEvent::AGENT_HEARTBEAT)); + event->agent = agent; + event->timestamp = timestamp; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(event); +} + + +void ConsoleImpl::eventEventReceived(EventPtr event) +{ + ConsoleEventImpl::Ptr console_event(new ConsoleEventImpl(ConsoleEvent::EVENT_RECEIVED)); + console_event->event = event; + Mutex::ScopedLock _lock(lock); + eventQueue.push_back(console_event); +} + +//================================================================== +// Wrappers +//================================================================== + +Console::Console(const ConsoleSettings& settings) : impl(new ConsoleImpl(settings)) {} +Console::~Console() { delete impl; } +bool Console::getEvent(ConsoleEvent& event) const { return impl->getEvent(event); } +void Console::popEvent() { impl->popEvent(); } +void Console::addConnection(BrokerProxy& broker, void* context) { impl->addConnection(broker, context); } +void Console::delConnection(BrokerProxy& broker) { impl->delConnection(broker); } +uint32_t Console::packageCount() const { return impl->packageCount(); } +const char* Console::getPackageName(uint32_t idx) const { return impl->getPackageName(idx).c_str(); } +uint32_t Console::classCount(const char* packageName) const { return impl->classCount(packageName); } +const SchemaClassKey* Console::getClass(const char* packageName, uint32_t idx) const { return impl->getClass(packageName, idx); } +ClassKind Console::getClassKind(const SchemaClassKey* key) const { return impl->getClassKind(key); } +const SchemaObjectClass* Console::getObjectClass(const SchemaClassKey* key) const { return impl->getObjectClass(key); } +const SchemaEventClass* Console::getEventClass(const SchemaClassKey* key) const { return impl->getEventClass(key); } +void Console::bindPackage(const char* packageName) { impl->bindPackage(packageName); } +void Console::bindClass(const SchemaClassKey* key) { impl->bindClass(key); } +void Console::bindClass(const char* packageName, const char* className) { impl->bindClass(packageName, className); } + +void Console::bindEvent(const SchemaClassKey *key) { impl->bindEvent(key); } +void Console::bindEvent(const char* packageName, const char* eventName) { impl->bindEvent(packageName, eventName); } + +//void Console::startSync(const Query& query, void* context, SyncQuery& sync) { impl->startSync(query, context, sync); } +//void Console::touchSync(SyncQuery& sync) { impl->touchSync(sync); } +//void Console::endSync(SyncQuery& sync) { impl->endSync(sync); } + + diff --git a/qpid/cpp/src/qmf/engine/ConsoleImpl.h b/qpid/cpp/src/qmf/engine/ConsoleImpl.h new file mode 100644 index 0000000000..0c27fdabcd --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ConsoleImpl.h @@ -0,0 +1,148 @@ +#ifndef _QmfEngineConsoleEngineImpl_ +#define _QmfEngineConsoleEngineImpl_ + +/* + * 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 "qmf/engine/Console.h" +#include "qmf/engine/MessageImpl.h" +#include "qmf/engine/SchemaImpl.h" +#include "qmf/engine/Typecode.h" +#include "qmf/engine/ObjectImpl.h" +#include "qmf/engine/ObjectIdImpl.h" +#include "qmf/engine/QueryImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/Protocol.h" +#include "qmf/engine/SequenceManager.h" +#include "qmf/engine/BrokerProxyImpl.h" +#include <qpid/framing/Buffer.h> +#include <qpid/framing/Uuid.h> +#include <qpid/framing/FieldTable.h> +#include <qpid/framing/FieldValue.h> +#include <qpid/sys/Mutex.h> +#include <qpid/sys/Time.h> +#include <qpid/sys/SystemInfo.h> +#include <string.h> +#include <string> +#include <deque> +#include <map> +#include <vector> +#include <iostream> +#include <fstream> +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> + +namespace qmf { +namespace engine { + + struct ConsoleEventImpl { + typedef boost::shared_ptr<ConsoleEventImpl> Ptr; + ConsoleEvent::EventKind kind; + boost::shared_ptr<AgentProxy> agent; + std::string name; + const SchemaClassKey* classKey; + boost::shared_ptr<Object> object; + void* context; + boost::shared_ptr<Event> event; + uint64_t timestamp; + bool hasProps; + bool hasStats; + + ConsoleEventImpl(ConsoleEvent::EventKind k) : + kind(k), classKey(0), context(0), timestamp(0) {} + ~ConsoleEventImpl() {} + ConsoleEvent copy(); + }; + + class ConsoleImpl : public boost::noncopyable { + public: + ConsoleImpl(const ConsoleSettings& settings = ConsoleSettings()); + ~ConsoleImpl(); + + bool getEvent(ConsoleEvent& event) const; + void popEvent(); + + void addConnection(BrokerProxy& broker, void* context); + void delConnection(BrokerProxy& broker); + + uint32_t packageCount() const; + const std::string& getPackageName(uint32_t idx) const; + + uint32_t classCount(const char* packageName) const; + const SchemaClassKey* getClass(const char* packageName, uint32_t idx) const; + + ClassKind getClassKind(const SchemaClassKey* key) const; + const SchemaObjectClass* getObjectClass(const SchemaClassKey* key) const; + const SchemaEventClass* getEventClass(const SchemaClassKey* key) const; + + void bindPackage(const char* packageName); + void bindClass(const SchemaClassKey* key); + void bindClass(const char* packageName, const char* className); + void bindEvent(const SchemaClassKey* key); + void bindEvent(const char* packageName, const char* eventName); + + /* + void startSync(const Query& query, void* context, SyncQuery& sync); + void touchSync(SyncQuery& sync); + void endSync(SyncQuery& sync); + */ + + private: + friend class BrokerProxyImpl; + friend struct StaticContext; + const ConsoleSettings& settings; + mutable qpid::sys::Mutex lock; + std::deque<ConsoleEventImpl::Ptr> eventQueue; + std::vector<BrokerProxyImpl*> brokerList; + std::vector<std::pair<std::string, std::string> > bindingList; // exchange/key (empty exchange => QMF_EXCHANGE) + + // Declare a compare class for the class maps that compares the dereferenced + // class key pointers. The default behavior would be to compare the pointer + // addresses themselves. + struct KeyCompare { + bool operator()(const SchemaClassKey* left, const SchemaClassKey* right) const { + return *left < *right; + } + }; + + typedef std::map<const SchemaClassKey*, SchemaObjectClass*, KeyCompare> ObjectClassList; + typedef std::map<const SchemaClassKey*, SchemaEventClass*, KeyCompare> EventClassList; + typedef std::map<std::string, std::pair<ObjectClassList, EventClassList> > PackageList; + + PackageList packages; + + void learnPackage(const std::string& packageName); + void learnClass(SchemaObjectClass* cls); + void learnClass(SchemaEventClass* cls); + bool haveClass(const SchemaClassKey* key) const; + SchemaObjectClass* getSchema(const SchemaClassKey* key) const; + + void eventAgentAdded(boost::shared_ptr<AgentProxy> agent); + void eventAgentDeleted(boost::shared_ptr<AgentProxy> agent); + void eventNewPackage(const std::string& packageName); + void eventNewClass(const SchemaClassKey* key); + void eventObjectUpdate(ObjectPtr object, bool prop, bool stat); + void eventAgentHeartbeat(boost::shared_ptr<AgentProxy> agent, uint64_t timestamp); + void eventEventReceived(boost::shared_ptr<Event> event); + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/EventImpl.cpp b/qpid/cpp/src/qmf/engine/EventImpl.cpp new file mode 100644 index 0000000000..4b034e8e83 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/EventImpl.cpp @@ -0,0 +1,120 @@ +/* + * 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 <qmf/engine/EventImpl.h> +#include <qmf/engine/ValueImpl.h> + +#include <sstream> + +using namespace std; +using namespace qmf::engine; +using qpid::framing::Buffer; + +EventImpl::EventImpl(const SchemaEventClass* type) : eventClass(type), timestamp(0), severity(0) +{ + int argCount = eventClass->getArgumentCount(); + int idx; + + for (idx = 0; idx < argCount; idx++) { + const SchemaArgument* arg = eventClass->getArgument(idx); + arguments[arg->getName()] = ValuePtr(new Value(arg->getType())); + } +} + + +EventImpl::EventImpl(const SchemaEventClass* type, Buffer& buffer) : + eventClass(type), timestamp(0), severity(0) +{ + int argCount = eventClass->getArgumentCount(); + int idx; + + timestamp = buffer.getLongLong(); + severity = buffer.getOctet(); + + for (idx = 0; idx < argCount; idx++) + { + const SchemaArgument *arg = eventClass->getArgument(idx); + Value* pval = ValueImpl::factory(arg->getType(), buffer); + arguments[arg->getName()] = ValuePtr(pval); + } +} + + +Event* EventImpl::factory(const SchemaEventClass* type, Buffer& buffer) +{ + EventImpl* impl(new EventImpl(type, buffer)); + return new Event(impl); +} + + +Value* EventImpl::getValue(const char* key) const +{ + map<string, ValuePtr>::const_iterator iter; + + iter = arguments.find(key); + if (iter != arguments.end()) + return iter->second.get(); + + return 0; +} + + +void EventImpl::encodeSchemaKey(Buffer& buffer) const +{ + buffer.putShortString(eventClass->getClassKey()->getPackageName()); + buffer.putShortString(eventClass->getClassKey()->getClassName()); + buffer.putBin128(const_cast<uint8_t*>(eventClass->getClassKey()->getHash())); +} + + +void EventImpl::encode(Buffer& buffer) const +{ + buffer.putOctet((uint8_t) eventClass->getSeverity()); + + int argCount = eventClass->getArgumentCount(); + for (int idx = 0; idx < argCount; idx++) { + const SchemaArgument* arg = eventClass->getArgument(idx); + ValuePtr value = arguments[arg->getName()]; + value->impl->encode(buffer); + } +} + + +string EventImpl::getRoutingKey(uint32_t brokerBank, uint32_t agentBank) const +{ + stringstream key; + + key << "console.event." << brokerBank << "." << agentBank << "." << + eventClass->getClassKey()->getPackageName() << "." << + eventClass->getClassKey()->getClassName(); + return key.str(); +} + + +//================================================================== +// Wrappers +//================================================================== + +Event::Event(const SchemaEventClass* type) : impl(new EventImpl(type)) {} +Event::Event(EventImpl* i) : impl(i) {} +Event::Event(const Event& from) : impl(new EventImpl(*(from.impl))) {} +Event::~Event() { delete impl; } +const SchemaEventClass* Event::getClass() const { return impl->getClass(); } +Value* Event::getValue(const char* key) const { return impl->getValue(key); } + diff --git a/qpid/cpp/src/qmf/engine/EventImpl.h b/qpid/cpp/src/qmf/engine/EventImpl.h new file mode 100644 index 0000000000..4046e71ef9 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/EventImpl.h @@ -0,0 +1,57 @@ +#ifndef _QmfEngineEventImpl_ +#define _QmfEngineEventImpl_ + +/* + * 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 <qmf/engine/Event.h> +#include <qmf/engine/Schema.h> +#include <qpid/framing/Buffer.h> +#include <boost/shared_ptr.hpp> +#include <map> + +namespace qmf { +namespace engine { + + typedef boost::shared_ptr<Event> EventPtr; + + struct EventImpl { + typedef boost::shared_ptr<Value> ValuePtr; + const SchemaEventClass* eventClass; + uint64_t timestamp; + uint8_t severity; + mutable std::map<std::string, ValuePtr> arguments; + + EventImpl(const SchemaEventClass* type); + EventImpl(const SchemaEventClass* type, qpid::framing::Buffer& buffer); + static Event* factory(const SchemaEventClass* type, qpid::framing::Buffer& buffer); + + const SchemaEventClass* getClass() const { return eventClass; } + Value* getValue(const char* key) const; + + void encodeSchemaKey(qpid::framing::Buffer& buffer) const; + void encode(qpid::framing::Buffer& buffer) const; + std::string getRoutingKey(uint32_t brokerBank, uint32_t agentBank) const; + }; + +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/MessageImpl.cpp b/qpid/cpp/src/qmf/engine/MessageImpl.cpp new file mode 100644 index 0000000000..0047d3eb9d --- /dev/null +++ b/qpid/cpp/src/qmf/engine/MessageImpl.cpp @@ -0,0 +1,43 @@ +/* + * 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 "qmf/engine/MessageImpl.h" +#include <string.h> + +using namespace std; +using namespace qmf::engine; + +#define STRING_REF(s) {if (!s.empty()) item.s = const_cast<char*>(s.c_str());} + +Message MessageImpl::copy() +{ + Message item; + + ::memset(&item, 0, sizeof(Message)); + item.body = const_cast<char*>(body.c_str()); + item.length = body.length(); + STRING_REF(destination); + STRING_REF(routingKey); + STRING_REF(replyExchange); + STRING_REF(replyKey); + STRING_REF(userId); + + return item; +} + diff --git a/qpid/cpp/src/qmf/engine/MessageImpl.h b/qpid/cpp/src/qmf/engine/MessageImpl.h new file mode 100644 index 0000000000..b91291d2e4 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/MessageImpl.h @@ -0,0 +1,44 @@ +#ifndef _QmfEngineMessageImpl_ +#define _QmfEngineMessageImpl_ + +/* + * 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 "qmf/engine/Message.h" +#include <string> +#include <boost/shared_ptr.hpp> + +namespace qmf { +namespace engine { + + struct MessageImpl { + typedef boost::shared_ptr<MessageImpl> Ptr; + std::string body; + std::string destination; + std::string routingKey; + std::string replyExchange; + std::string replyKey; + std::string userId; + + Message copy(); + }; +} +} + +#endif diff --git a/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp b/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp new file mode 100644 index 0000000000..9216f7bac0 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ObjectIdImpl.cpp @@ -0,0 +1,210 @@ +/* + * 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 "qmf/engine/ObjectIdImpl.h" +#include <stdlib.h> +#include <sstream> + +using namespace std; +using namespace qmf::engine; +using qpid::framing::Buffer; + +void AgentAttachment::setBanks(uint32_t broker, uint32_t agent) +{ + first = + ((uint64_t) (broker & 0x000fffff)) << 28 | + ((uint64_t) (agent & 0x0fffffff)); +} + +ObjectIdImpl::ObjectIdImpl(Buffer& buffer) : agent(0) +{ + decode(buffer); +} + +ObjectIdImpl::ObjectIdImpl(AgentAttachment* a, uint8_t flags, uint16_t seq, uint64_t object) : agent(a) +{ + first = + ((uint64_t) (flags & 0x0f)) << 60 | + ((uint64_t) (seq & 0x0fff)) << 48; + second = object; +} + +ObjectId* ObjectIdImpl::factory(Buffer& buffer) +{ + ObjectIdImpl* impl(new ObjectIdImpl(buffer)); + return new ObjectId(impl); +} + +ObjectId* ObjectIdImpl::factory(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object) +{ + ObjectIdImpl* impl(new ObjectIdImpl(agent, flags, seq, object)); + return new ObjectId(impl); +} + +void ObjectIdImpl::decode(Buffer& buffer) +{ + first = buffer.getLongLong(); + second = buffer.getLongLong(); +} + +void ObjectIdImpl::encode(Buffer& buffer) const +{ + if (agent == 0) + buffer.putLongLong(first); + else + buffer.putLongLong(first | agent->first); + buffer.putLongLong(second); +} + +void ObjectIdImpl::fromString(const std::string& repr) +{ +#define FIELDS 5 +#if defined (_WIN32) && !defined (atoll) +# define atoll(X) _atoi64(X) +#endif + + std::string copy(repr.c_str()); + char* cText; + char* field[FIELDS]; + bool atFieldStart = true; + int idx = 0; + + cText = const_cast<char*>(copy.c_str()); + for (char* cursor = cText; *cursor; cursor++) { + if (atFieldStart) { + if (idx >= FIELDS) + return; // TODO error + field[idx++] = cursor; + atFieldStart = false; + } else { + if (*cursor == '-') { + *cursor = '\0'; + atFieldStart = true; + } + } + } + + if (idx != FIELDS) + return; // TODO error + + first = (atoll(field[0]) << 60) + + (atoll(field[1]) << 48) + + (atoll(field[2]) << 28) + + atoll(field[3]); + second = atoll(field[4]); + agent = 0; +} + +const string& ObjectIdImpl::asString() const +{ + stringstream val; + + val << (int) getFlags() << "-" << getSequence() << "-" << getBrokerBank() << "-" << + getAgentBank() << "-" << getObjectNum(); + repr = val.str(); + return repr; +} + +#define ACTUAL_FIRST (agent == 0 ? first : first | agent->first) +#define ACTUAL_OTHER (other.agent == 0 ? other.first : other.first | other.agent->first) + +uint8_t ObjectIdImpl::getFlags() const +{ + return (ACTUAL_FIRST & 0xF000000000000000LL) >> 60; +} + +uint16_t ObjectIdImpl::getSequence() const +{ + return (ACTUAL_FIRST & 0x0FFF000000000000LL) >> 48; +} + +uint32_t ObjectIdImpl::getBrokerBank() const +{ + return (ACTUAL_FIRST & 0x0000FFFFF0000000LL) >> 28; +} + +uint32_t ObjectIdImpl::getAgentBank() const +{ + return ACTUAL_FIRST & 0x000000000FFFFFFFLL; +} + +uint64_t ObjectIdImpl::getObjectNum() const +{ + return second; +} + +uint32_t ObjectIdImpl::getObjectNumHi() const +{ + return (uint32_t) (second >> 32); +} + +uint32_t ObjectIdImpl::getObjectNumLo() const +{ + return (uint32_t) (second & 0x00000000FFFFFFFFLL); +} + +bool ObjectIdImpl::operator==(const ObjectIdImpl& other) const +{ + return ACTUAL_FIRST == ACTUAL_OTHER && second == other.second; +} + +bool ObjectIdImpl::operator<(const ObjectIdImpl& other) const +{ + return (ACTUAL_FIRST < ACTUAL_OTHER) || ((ACTUAL_FIRST == ACTUAL_OTHER) && (second < other.second)); +} + +bool ObjectIdImpl::operator>(const ObjectIdImpl& other) const +{ + return (ACTUAL_FIRST > ACTUAL_OTHER) || ((ACTUAL_FIRST == ACTUAL_OTHER) && (second > other.second)); +} + + +//================================================================== +// Wrappers +//================================================================== + +ObjectId::ObjectId() : impl(new ObjectIdImpl()) {} +ObjectId::ObjectId(const ObjectId& from) : impl(new ObjectIdImpl(*(from.impl))) {} +ObjectId::ObjectId(ObjectIdImpl* i) : impl(i) {} +ObjectId::~ObjectId() { delete impl; } +uint64_t ObjectId::getObjectNum() const { return impl->getObjectNum(); } +uint32_t ObjectId::getObjectNumHi() const { return impl->getObjectNumHi(); } +uint32_t ObjectId::getObjectNumLo() const { return impl->getObjectNumLo(); } +bool ObjectId::isDurable() const { return impl->isDurable(); } +const char* ObjectId::str() const { return impl->asString().c_str(); } +uint8_t ObjectId::getFlags() const { return impl->getFlags(); } +uint16_t ObjectId::getSequence() const { return impl->getSequence(); } +uint32_t ObjectId::getBrokerBank() const { return impl->getBrokerBank(); } +uint32_t ObjectId::getAgentBank() const { return impl->getAgentBank(); } +bool ObjectId::operator==(const ObjectId& other) const { return *impl == *other.impl; } +bool ObjectId::operator<(const ObjectId& other) const { return *impl < *other.impl; } +bool ObjectId::operator>(const ObjectId& other) const { return *impl > *other.impl; } +bool ObjectId::operator<=(const ObjectId& other) const { return !(*impl > *other.impl); } +bool ObjectId::operator>=(const ObjectId& other) const { return !(*impl < *other.impl); } +ObjectId& ObjectId::operator=(const ObjectId& other) { + ObjectIdImpl *old; + if (this != &other) { + old = impl; + impl = new ObjectIdImpl(*(other.impl)); + if (old) + delete old; + } + return *this; +} + diff --git a/qpid/cpp/src/qmf/engine/ObjectIdImpl.h b/qpid/cpp/src/qmf/engine/ObjectIdImpl.h new file mode 100644 index 0000000000..d70c8efff4 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ObjectIdImpl.h @@ -0,0 +1,72 @@ +#ifndef _QmfEngineObjectIdImpl_ +#define _QmfEngineObjectIdImpl_ + +/* + * 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 <qmf/engine/ObjectId.h> +#include <qpid/framing/Buffer.h> + +namespace qmf { +namespace engine { + + struct AgentAttachment { + uint64_t first; + + AgentAttachment() : first(0) {} + void setBanks(uint32_t broker, uint32_t bank); + uint64_t getFirst() const { return first; } + }; + + struct ObjectIdImpl { + AgentAttachment* agent; + uint64_t first; + uint64_t second; + mutable std::string repr; + + ObjectIdImpl() : agent(0), first(0), second(0) {} + ObjectIdImpl(qpid::framing::Buffer& buffer); + ObjectIdImpl(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object); + + static ObjectId* factory(qpid::framing::Buffer& buffer); + static ObjectId* factory(AgentAttachment* agent, uint8_t flags, uint16_t seq, uint64_t object); + + void decode(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void fromString(const std::string& repr); + const std::string& asString() const; + uint8_t getFlags() const; + uint16_t getSequence() const; + uint32_t getBrokerBank() const; + uint32_t getAgentBank() const; + uint64_t getObjectNum() const; + uint32_t getObjectNumHi() const; + uint32_t getObjectNumLo() const; + bool isDurable() const { return getSequence() == 0; } + void setValue(uint64_t f, uint64_t s) { first = f; second = s; agent = 0; } + + bool operator==(const ObjectIdImpl& other) const; + bool operator<(const ObjectIdImpl& other) const; + bool operator>(const ObjectIdImpl& other) const; + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/ObjectImpl.cpp b/qpid/cpp/src/qmf/engine/ObjectImpl.cpp new file mode 100644 index 0000000000..45925cb804 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ObjectImpl.cpp @@ -0,0 +1,232 @@ +/* + * 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 "qmf/engine/ObjectImpl.h" +#include "qmf/engine/ValueImpl.h" +#include "qmf/engine/BrokerProxyImpl.h" +#include <qpid/sys/Time.h> + +using namespace std; +using namespace qmf::engine; +using namespace qpid::sys; +using qpid::framing::Buffer; + +ObjectImpl::ObjectImpl(const SchemaObjectClass* type) : objectClass(type), broker(0), createTime(uint64_t(Duration(EPOCH, now()))), destroyTime(0), lastUpdatedTime(createTime) +{ + int propCount = objectClass->getPropertyCount(); + int statCount = objectClass->getStatisticCount(); + int idx; + + for (idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + properties[prop->getName()] = ValuePtr(new Value(prop->getType())); + } + + for (idx = 0; idx < statCount; idx++) { + const SchemaStatistic* stat = objectClass->getStatistic(idx); + statistics[stat->getName()] = ValuePtr(new Value(stat->getType())); + } +} + +ObjectImpl::ObjectImpl(const SchemaObjectClass* type, BrokerProxyImpl* b, Buffer& buffer, bool prop, bool stat, bool managed) : + objectClass(type), broker(b), createTime(0), destroyTime(0), lastUpdatedTime(0) +{ + int idx; + + if (managed) { + lastUpdatedTime = buffer.getLongLong(); + createTime = buffer.getLongLong(); + destroyTime = buffer.getLongLong(); + objectId.reset(ObjectIdImpl::factory(buffer)); + } + + if (prop) { + int propCount = objectClass->getPropertyCount(); + set<string> excludes; + parsePresenceMasks(buffer, excludes); + for (idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + if (excludes.count(prop->getName()) != 0) { + properties[prop->getName()] = ValuePtr(new Value(prop->getType())); + } else { + Value* pval = ValueImpl::factory(prop->getType(), buffer); + properties[prop->getName()] = ValuePtr(pval); + } + } + } + + if (stat) { + int statCount = objectClass->getStatisticCount(); + for (idx = 0; idx < statCount; idx++) { + const SchemaStatistic* stat = objectClass->getStatistic(idx); + Value* sval = ValueImpl::factory(stat->getType(), buffer); + statistics[stat->getName()] = ValuePtr(sval); + } + } +} + +Object* ObjectImpl::factory(const SchemaObjectClass* type, BrokerProxyImpl* b, Buffer& buffer, bool prop, bool stat, bool managed) +{ + ObjectImpl* impl(new ObjectImpl(type, b, buffer, prop, stat, managed)); + return new Object(impl); +} + +ObjectImpl::~ObjectImpl() +{ +} + +void ObjectImpl::destroy() +{ + destroyTime = uint64_t(Duration(EPOCH, now())); + // TODO - flag deletion +} + +Value* ObjectImpl::getValue(const string& key) const +{ + map<string, ValuePtr>::const_iterator iter; + + iter = properties.find(key); + if (iter != properties.end()) + return iter->second.get(); + + iter = statistics.find(key); + if (iter != statistics.end()) + return iter->second.get(); + + return 0; +} + +void ObjectImpl::invokeMethod(const string& methodName, const Value* inArgs, void* context) const +{ + if (broker != 0 && objectId.get() != 0) + broker->sendMethodRequest(objectId.get(), objectClass, methodName, inArgs, context); +} + +void ObjectImpl::merge(const Object& from) +{ + for (map<string, ValuePtr>::const_iterator piter = from.impl->properties.begin(); + piter != from.impl->properties.end(); piter++) + properties[piter->first] = piter->second; + for (map<string, ValuePtr>::const_iterator siter = from.impl->statistics.begin(); + siter != from.impl->statistics.end(); siter++) + statistics[siter->first] = siter->second; +} + +void ObjectImpl::parsePresenceMasks(Buffer& buffer, set<string>& excludeList) +{ + int propCount = objectClass->getPropertyCount(); + excludeList.clear(); + uint8_t bit = 0; + uint8_t mask = 0; + + for (int idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + if (prop->isOptional()) { + if (bit == 0) { + mask = buffer.getOctet(); + bit = 1; + } + if ((mask & bit) == 0) + excludeList.insert(string(prop->getName())); + if (bit == 0x80) + bit = 0; + else + bit = bit << 1; + } + } +} + +void ObjectImpl::encodeSchemaKey(qpid::framing::Buffer& buffer) const +{ + buffer.putShortString(objectClass->getClassKey()->getPackageName()); + buffer.putShortString(objectClass->getClassKey()->getClassName()); + buffer.putBin128(const_cast<uint8_t*>(objectClass->getClassKey()->getHash())); +} + +void ObjectImpl::encodeManagedObjectData(qpid::framing::Buffer& buffer) const +{ + buffer.putLongLong(lastUpdatedTime); + buffer.putLongLong(createTime); + buffer.putLongLong(destroyTime); + objectId->impl->encode(buffer); +} + +void ObjectImpl::encodeProperties(qpid::framing::Buffer& buffer) const +{ + int propCount = objectClass->getPropertyCount(); + uint8_t bit = 0; + uint8_t mask = 0; + ValuePtr value; + + for (int idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + if (prop->isOptional()) { + value = properties[prop->getName()]; + if (bit == 0) + bit = 1; + if (!value->isNull()) + mask |= bit; + if (bit == 0x80) { + buffer.putOctet(mask); + bit = 0; + mask = 0; + } else + bit = bit << 1; + } + } + if (bit != 0) { + buffer.putOctet(mask); + } + + for (int idx = 0; idx < propCount; idx++) { + const SchemaProperty* prop = objectClass->getProperty(idx); + value = properties[prop->getName()]; + if (!prop->isOptional() || !value->isNull()) { + value->impl->encode(buffer); + } + } +} + +void ObjectImpl::encodeStatistics(qpid::framing::Buffer& buffer) const +{ + int statCount = objectClass->getStatisticCount(); + for (int idx = 0; idx < statCount; idx++) { + const SchemaStatistic* stat = objectClass->getStatistic(idx); + ValuePtr value = statistics[stat->getName()]; + value->impl->encode(buffer); + } +} + +//================================================================== +// Wrappers +//================================================================== + +Object::Object(const SchemaObjectClass* type) : impl(new ObjectImpl(type)) {} +Object::Object(ObjectImpl* i) : impl(i) {} +Object::Object(const Object& from) : impl(new ObjectImpl(*(from.impl))) {} +Object::~Object() { delete impl; } +void Object::destroy() { impl->destroy(); } +const ObjectId* Object::getObjectId() const { return impl->getObjectId(); } +void Object::setObjectId(ObjectId* oid) { impl->setObjectId(oid); } +const SchemaObjectClass* Object::getClass() const { return impl->getClass(); } +Value* Object::getValue(const char* key) const { return impl->getValue(key); } +void Object::invokeMethod(const char* m, const Value* a, void* c) const { impl->invokeMethod(m, a, c); } +bool Object::isDeleted() const { return impl->isDeleted(); } +void Object::merge(const Object& from) { impl->merge(from); } + diff --git a/qpid/cpp/src/qmf/engine/ObjectImpl.h b/qpid/cpp/src/qmf/engine/ObjectImpl.h new file mode 100644 index 0000000000..6f25867004 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ObjectImpl.h @@ -0,0 +1,76 @@ +#ifndef _QmfEngineObjectImpl_ +#define _QmfEngineObjectImpl_ + +/* + * 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 <qmf/engine/Object.h> +#include <qmf/engine/ObjectIdImpl.h> +#include <map> +#include <set> +#include <string> +#include <qpid/framing/Buffer.h> +#include <boost/shared_ptr.hpp> +#include <qpid/sys/Mutex.h> + +namespace qmf { +namespace engine { + + class BrokerProxyImpl; + + typedef boost::shared_ptr<Object> ObjectPtr; + + struct ObjectImpl { + typedef boost::shared_ptr<Value> ValuePtr; + const SchemaObjectClass* objectClass; + BrokerProxyImpl* broker; + boost::shared_ptr<ObjectId> objectId; + uint64_t createTime; + uint64_t destroyTime; + uint64_t lastUpdatedTime; + mutable std::map<std::string, ValuePtr> properties; + mutable std::map<std::string, ValuePtr> statistics; + + ObjectImpl(const SchemaObjectClass* type); + ObjectImpl(const SchemaObjectClass* type, BrokerProxyImpl* b, qpid::framing::Buffer& buffer, + bool prop, bool stat, bool managed); + static Object* factory(const SchemaObjectClass* type, BrokerProxyImpl* b, qpid::framing::Buffer& buffer, + bool prop, bool stat, bool managed); + ~ObjectImpl(); + + void destroy(); + const ObjectId* getObjectId() const { return objectId.get(); } + void setObjectId(ObjectId* oid) { objectId.reset(new ObjectId(*oid)); } + const SchemaObjectClass* getClass() const { return objectClass; } + Value* getValue(const std::string& key) const; + void invokeMethod(const std::string& methodName, const Value* inArgs, void* context) const; + bool isDeleted() const { return destroyTime != 0; } + void merge(const Object& from); + + void parsePresenceMasks(qpid::framing::Buffer& buffer, std::set<std::string>& excludeList); + void encodeSchemaKey(qpid::framing::Buffer& buffer) const; + void encodeManagedObjectData(qpid::framing::Buffer& buffer) const; + void encodeProperties(qpid::framing::Buffer& buffer) const; + void encodeStatistics(qpid::framing::Buffer& buffer) const; + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/Protocol.cpp b/qpid/cpp/src/qmf/engine/Protocol.cpp new file mode 100644 index 0000000000..9e5f490604 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/Protocol.cpp @@ -0,0 +1,52 @@ +/* + * 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 "qmf/engine/Protocol.h" +#include "qpid/framing/Buffer.h" + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; + + +bool Protocol::checkHeader(Buffer& buf, uint8_t *opcode, uint32_t *seq) +{ + if (buf.available() < 8) + return false; + + uint8_t h1 = buf.getOctet(); + uint8_t h2 = buf.getOctet(); + uint8_t h3 = buf.getOctet(); + + *opcode = buf.getOctet(); + *seq = buf.getLong(); + + return h1 == 'A' && h2 == 'M' && h3 == '2'; +} + +void Protocol::encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq) +{ + buf.putOctet('A'); + buf.putOctet('M'); + buf.putOctet('2'); + buf.putOctet(opcode); + buf.putLong (seq); +} + + diff --git a/qpid/cpp/src/qmf/engine/Protocol.h b/qpid/cpp/src/qmf/engine/Protocol.h new file mode 100644 index 0000000000..1cdfa60c84 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/Protocol.h @@ -0,0 +1,69 @@ +#ifndef _QmfEngineProtocol_ +#define _QmfEngineProtocol_ + +/* + * 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 <qpid/sys/IntegerTypes.h> + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { +namespace engine { + + class Protocol { + public: + static bool checkHeader(qpid::framing::Buffer& buf, uint8_t *opcode, uint32_t *seq); + static void encodeHeader(qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0); + + const static uint8_t OP_ATTACH_REQUEST = 'A'; + const static uint8_t OP_ATTACH_RESPONSE = 'a'; + + const static uint8_t OP_BROKER_REQUEST = 'B'; + const static uint8_t OP_BROKER_RESPONSE = 'b'; + + const static uint8_t OP_CONSOLE_ADDED_INDICATION = 'x'; + const static uint8_t OP_COMMAND_COMPLETE = 'z'; + const static uint8_t OP_HEARTBEAT_INDICATION = 'h'; + + const static uint8_t OP_PACKAGE_REQUEST = 'P'; + const static uint8_t OP_PACKAGE_INDICATION = 'p'; + const static uint8_t OP_CLASS_QUERY = 'Q'; + const static uint8_t OP_CLASS_INDICATION = 'q'; + const static uint8_t OP_SCHEMA_REQUEST = 'S'; + const static uint8_t OP_SCHEMA_RESPONSE = 's'; + + const static uint8_t OP_METHOD_REQUEST = 'M'; + const static uint8_t OP_METHOD_RESPONSE = 'm'; + const static uint8_t OP_GET_QUERY = 'G'; + const static uint8_t OP_OBJECT_INDICATION = 'g'; + const static uint8_t OP_PROPERTY_INDICATION = 'c'; + const static uint8_t OP_STATISTIC_INDICATION = 'i'; + const static uint8_t OP_EVENT_INDICATION = 'e'; + }; + +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/QueryImpl.cpp b/qpid/cpp/src/qmf/engine/QueryImpl.cpp new file mode 100644 index 0000000000..6f2beeee87 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/QueryImpl.cpp @@ -0,0 +1,103 @@ +/* + * 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 "qmf/engine/QueryImpl.h" +#include "qmf/engine/ObjectIdImpl.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/FieldTable.h" + +using namespace std; +using namespace qmf::engine; +using namespace qpid::framing; + +bool QueryElementImpl::evaluate(const Object* /*object*/) const +{ + // TODO: Implement this + return false; +} + +bool QueryExpressionImpl::evaluate(const Object* /*object*/) const +{ + // TODO: Implement this + return false; +} + +QueryImpl::QueryImpl(Buffer& buffer) +{ + FieldTable ft; + ft.decode(buffer); + // TODO +} + +Query* QueryImpl::factory(Buffer& buffer) +{ + QueryImpl* impl(new QueryImpl(buffer)); + return new Query(impl); +} + +void QueryImpl::encode(Buffer& buffer) const +{ + FieldTable ft; + + if (oid.get() != 0) { + ft.setString("_objectid", oid->impl->asString()); + } else { + if (!packageName.empty()) + ft.setString("_package", packageName); + ft.setString("_class", className); + } + + ft.encode(buffer); +} + + +//================================================================== +// Wrappers +//================================================================== + +QueryElement::QueryElement(const char* attrName, const Value* value, ValueOper oper) : impl(new QueryElementImpl(attrName, value, oper)) {} +QueryElement::QueryElement(QueryElementImpl* i) : impl(i) {} +QueryElement::~QueryElement() { delete impl; } +bool QueryElement::evaluate(const Object* object) const { return impl->evaluate(object); } + +QueryExpression::QueryExpression(ExprOper oper, const QueryOperand* operand1, const QueryOperand* operand2) : impl(new QueryExpressionImpl(oper, operand1, operand2)) {} +QueryExpression::QueryExpression(QueryExpressionImpl* i) : impl(i) {} +QueryExpression::~QueryExpression() { delete impl; } +bool QueryExpression::evaluate(const Object* object) const { return impl->evaluate(object); } + +Query::Query(const char* className, const char* packageName) : impl(new QueryImpl(className, packageName)) {} +Query::Query(const SchemaClassKey* key) : impl(new QueryImpl(key)) {} +Query::Query(const ObjectId* oid) : impl(new QueryImpl(oid)) {} +Query::Query(QueryImpl* i) : impl(i) {} +Query::Query(const Query& from) : impl(new QueryImpl(*(from.impl))) {} +Query::~Query() { delete impl; } +void Query::setSelect(const QueryOperand* criterion) { impl->setSelect(criterion); } +void Query::setLimit(uint32_t maxResults) { impl->setLimit(maxResults); } +void Query::setOrderBy(const char* attrName, bool decreasing) { impl->setOrderBy(attrName, decreasing); } +const char* Query::getPackage() const { return impl->getPackage().c_str(); } +const char* Query::getClass() const { return impl->getClass().c_str(); } +const ObjectId* Query::getObjectId() const { return impl->getObjectId(); } +bool Query::haveSelect() const { return impl->haveSelect(); } +bool Query::haveLimit() const { return impl->haveLimit(); } +bool Query::haveOrderBy() const { return impl->haveOrderBy(); } +const QueryOperand* Query::getSelect() const { return impl->getSelect(); } +uint32_t Query::getLimit() const { return impl->getLimit(); } +const char* Query::getOrderBy() const { return impl->getOrderBy().c_str(); } +bool Query::getDecreasing() const { return impl->getDecreasing(); } + diff --git a/qpid/cpp/src/qmf/engine/QueryImpl.h b/qpid/cpp/src/qmf/engine/QueryImpl.h new file mode 100644 index 0000000000..8ebe0d932f --- /dev/null +++ b/qpid/cpp/src/qmf/engine/QueryImpl.h @@ -0,0 +1,102 @@ +#ifndef _QmfEngineQueryImpl_ +#define _QmfEngineQueryImpl_ + +/* + * 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 "qmf/engine/Query.h" +#include "qmf/engine/Schema.h" +#include <string> +#include <boost/shared_ptr.hpp> + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { +namespace engine { + + struct QueryElementImpl { + QueryElementImpl(const std::string& a, const Value* v, ValueOper o) : attrName(a), value(v), oper(o) {} + ~QueryElementImpl() {} + bool evaluate(const Object* object) const; + + std::string attrName; + const Value* value; + ValueOper oper; + }; + + struct QueryExpressionImpl { + QueryExpressionImpl(ExprOper o, const QueryOperand* operand1, const QueryOperand* operand2) : oper(o), left(operand1), right(operand2) {} + ~QueryExpressionImpl() {} + bool evaluate(const Object* object) const; + + ExprOper oper; + const QueryOperand* left; + const QueryOperand* right; + }; + + struct QueryImpl { + // Constructors mapped to public + QueryImpl(const std::string& c, const std::string& p) : packageName(p), className(c), select(0), resultLimit(0) {} + QueryImpl(const SchemaClassKey* key) : packageName(key->getPackageName()), className(key->getClassName()), select(0), resultLimit(0) {} + QueryImpl(const ObjectId* oid) : oid(new ObjectId(*oid)), select(0), resultLimit(0) {} + + // Factory constructors + QueryImpl(qpid::framing::Buffer& buffer); + + ~QueryImpl() {}; + static Query* factory(qpid::framing::Buffer& buffer); + + void setSelect(const QueryOperand* criterion) { select = criterion; } + void setLimit(uint32_t maxResults) { resultLimit = maxResults; } + void setOrderBy(const std::string& attrName, bool decreasing) { + orderBy = attrName; orderDecreasing = decreasing; + } + + const std::string& getPackage() const { return packageName; } + const std::string& getClass() const { return className; } + const ObjectId* getObjectId() const { return oid.get(); } + + bool haveSelect() const { return select != 0; } + bool haveLimit() const { return resultLimit > 0; } + bool haveOrderBy() const { return !orderBy.empty(); } + const QueryOperand* getSelect() const { return select; } + uint32_t getLimit() const { return resultLimit; } + const std::string& getOrderBy() const { return orderBy; } + bool getDecreasing() const { return orderDecreasing; } + + void encode(qpid::framing::Buffer& buffer) const; + bool singleAgent() const { return oid.get() != 0; } + uint32_t agentBank() const { return singleAgent() ? oid->getAgentBank() : 0; } + + std::string packageName; + std::string className; + boost::shared_ptr<ObjectId> oid; + const QueryOperand* select; + uint32_t resultLimit; + std::string orderBy; + bool orderDecreasing; + }; +} +} + +#endif diff --git a/qpid/cpp/src/qmf/engine/ResilientConnection.cpp b/qpid/cpp/src/qmf/engine/ResilientConnection.cpp new file mode 100644 index 0000000000..ab65b8d768 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ResilientConnection.cpp @@ -0,0 +1,514 @@ +/* + * 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 "qmf/engine/ResilientConnection.h" +#include "qmf/engine/MessageImpl.h" +#include "qmf/engine/ConnectionSettingsImpl.h" +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/MessageListener.h> +#include <qpid/client/SubscriptionManager.h> +#include <qpid/client/Message.h> +#include <qpid/sys/Thread.h> +#include <qpid/sys/Runnable.h> +#include <qpid/sys/Mutex.h> +#include <qpid/sys/Condition.h> +#include <qpid/sys/Time.h> +#include <qpid/log/Statement.h> +#include <qpid/RefCounted.h> +#include <boost/bind.hpp> +#include <string> +#include <deque> +#include <vector> +#include <set> +#include <boost/intrusive_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <unistd.h> +#include <fcntl.h> + +using namespace std; +using namespace qmf::engine; +using namespace qpid; +using qpid::sys::Mutex; + +namespace qmf { +namespace engine { + struct ResilientConnectionEventImpl { + ResilientConnectionEvent::EventKind kind; + void* sessionContext; + string errorText; + MessageImpl message; + + ResilientConnectionEventImpl(ResilientConnectionEvent::EventKind k, + const MessageImpl& m = MessageImpl()) : + kind(k), sessionContext(0), message(m) {} + ResilientConnectionEvent copy(); + }; + + struct RCSession : public client::MessageListener, public qpid::sys::Runnable, public qpid::RefCounted { + typedef boost::intrusive_ptr<RCSession> Ptr; + ResilientConnectionImpl& connImpl; + string name; + client::Connection& connection; + client::Session session; + client::SubscriptionManager* subscriptions; + string userId; + void* userContext; + vector<string> dests; + qpid::sys::Thread thread; + + RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc); + ~RCSession(); + void received(client::Message& msg); + void run(); + void stop(); + }; + + class ResilientConnectionImpl : public qpid::sys::Runnable, public boost::noncopyable { + public: + ResilientConnectionImpl(const ConnectionSettings& settings); + ~ResilientConnectionImpl(); + + bool isConnected() const; + bool getEvent(ResilientConnectionEvent& event); + void popEvent(); + bool createSession(const char* name, void* sessionContext, SessionHandle& handle); + void destroySession(SessionHandle handle); + void sendMessage(SessionHandle handle, qmf::engine::Message& message); + void declareQueue(SessionHandle handle, char* queue); + void deleteQueue(SessionHandle handle, char* queue); + void bind(SessionHandle handle, char* exchange, char* queue, char* key); + void unbind(SessionHandle handle, char* exchange, char* queue, char* key); + void setNotifyFd(int fd); + void notify(); + + void run(); + void failure(); + void sessionClosed(RCSession* sess); + + void EnqueueEvent(ResilientConnectionEvent::EventKind kind, + void* sessionContext = 0, + const MessageImpl& message = MessageImpl(), + const string& errorText = ""); + + private: + int notifyFd; + bool connected; + bool shutdown; + string lastError; + const ConnectionSettings settings; + client::Connection connection; + mutable qpid::sys::Mutex lock; + int delayMin; + int delayMax; + int delayFactor; + qpid::sys::Condition cond; + deque<ResilientConnectionEventImpl> eventQueue; + set<RCSession::Ptr> sessions; + qpid::sys::Thread connThread; + }; +} +} + +ResilientConnectionEvent ResilientConnectionEventImpl::copy() +{ + ResilientConnectionEvent item; + + ::memset(&item, 0, sizeof(ResilientConnectionEvent)); + item.kind = kind; + item.sessionContext = sessionContext; + item.message = message.copy(); + item.errorText = const_cast<char*>(errorText.c_str()); + + return item; +} + +RCSession::RCSession(ResilientConnectionImpl& ci, const string& n, client::Connection& c, void* uc) : + connImpl(ci), name(n), connection(c), session(connection.newSession(name)), + subscriptions(new client::SubscriptionManager(session)), userContext(uc), thread(*this) +{ + const qpid::client::ConnectionSettings& operSettings = connection.getNegotiatedSettings(); + userId = operSettings.username; +} + +RCSession::~RCSession() +{ + subscriptions->stop(); + thread.join(); + session.close(); + delete subscriptions; +} + +void RCSession::run() +{ + try { + subscriptions->run(); + } catch (exception& /*e*/) { + connImpl.sessionClosed(this); + } +} + +void RCSession::stop() +{ + subscriptions->stop(); +} + +void RCSession::received(client::Message& msg) +{ + MessageImpl qmsg; + qmsg.body = msg.getData(); + + qpid::framing::DeliveryProperties dp = msg.getDeliveryProperties(); + if (dp.hasRoutingKey()) { + qmsg.routingKey = dp.getRoutingKey(); + } + + qpid::framing::MessageProperties mp = msg.getMessageProperties(); + if (mp.hasReplyTo()) { + const qpid::framing::ReplyTo& rt = mp.getReplyTo(); + qmsg.replyExchange = rt.getExchange(); + qmsg.replyKey = rt.getRoutingKey(); + } + + if (mp.hasUserId()) { + qmsg.userId = mp.getUserId(); + } + + connImpl.EnqueueEvent(ResilientConnectionEvent::RECV, userContext, qmsg); +} + +ResilientConnectionImpl::ResilientConnectionImpl(const ConnectionSettings& _settings) : + notifyFd(-1), connected(false), shutdown(false), settings(_settings), delayMin(1), connThread(*this) +{ + connection.registerFailureCallback(boost::bind(&ResilientConnectionImpl::failure, this)); + settings.impl->getRetrySettings(&delayMin, &delayMax, &delayFactor); +} + +ResilientConnectionImpl::~ResilientConnectionImpl() +{ + shutdown = true; + connected = false; + cond.notify(); + connThread.join(); + connection.close(); +} + +bool ResilientConnectionImpl::isConnected() const +{ + Mutex::ScopedLock _lock(lock); + return connected; +} + +bool ResilientConnectionImpl::getEvent(ResilientConnectionEvent& event) +{ + Mutex::ScopedLock _lock(lock); + if (eventQueue.empty()) + return false; + event = eventQueue.front().copy(); + return true; +} + +void ResilientConnectionImpl::popEvent() +{ + Mutex::ScopedLock _lock(lock); + if (!eventQueue.empty()) + eventQueue.pop_front(); +} + +bool ResilientConnectionImpl::createSession(const char* name, void* sessionContext, + SessionHandle& handle) +{ + Mutex::ScopedLock _lock(lock); + if (!connected) + return false; + + RCSession::Ptr sess = RCSession::Ptr(new RCSession(*this, name, connection, sessionContext)); + + handle.impl = (void*) sess.get(); + sessions.insert(sess); + + return true; +} + +void ResilientConnectionImpl::destroySession(SessionHandle handle) +{ + Mutex::ScopedLock _lock(lock); + RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); + set<RCSession::Ptr>::iterator iter = sessions.find(sess); + if (iter != sessions.end()) { + for (vector<string>::iterator dIter = sess->dests.begin(); dIter != sess->dests.end(); dIter++) + sess->subscriptions->cancel(dIter->c_str()); + sess->subscriptions->stop(); + sess->subscriptions->wait(); + + sessions.erase(iter); + return; + } +} + +void ResilientConnectionImpl::sendMessage(SessionHandle handle, qmf::engine::Message& message) +{ + Mutex::ScopedLock _lock(lock); + RCSession::Ptr sess = RCSession::Ptr((RCSession*) handle.impl); + set<RCSession::Ptr>::iterator iter = sessions.find(sess); + qpid::client::Message msg; + string data(message.body, message.length); + msg.getDeliveryProperties().setRoutingKey(message.routingKey); + msg.getMessageProperties().setReplyTo(qpid::framing::ReplyTo(message.replyExchange, message.replyKey)); + if (settings.impl->getSendUserId()) + msg.getMessageProperties().setUserId(sess->userId); + msg.setData(data); + + try { + sess->session.messageTransfer(client::arg::content=msg, client::arg::destination=message.destination); + } catch(exception& e) { + QPID_LOG(error, "Session Exception during message-transfer: " << e.what()); + sessions.erase(iter); + EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, (*iter)->userContext); + } +} + +void ResilientConnectionImpl::declareQueue(SessionHandle handle, char* queue) +{ + Mutex::ScopedLock _lock(lock); + RCSession* sess = (RCSession*) handle.impl; + + sess->session.queueDeclare(client::arg::queue=queue, client::arg::autoDelete=true, client::arg::exclusive=true); + sess->subscriptions->setAcceptMode(client::ACCEPT_MODE_NONE); + sess->subscriptions->setAcquireMode(client::ACQUIRE_MODE_PRE_ACQUIRED); + sess->subscriptions->subscribe(*sess, queue, queue); + sess->subscriptions->setFlowControl(queue, client::FlowControl::unlimited()); + sess->dests.push_back(string(queue)); +} + +void ResilientConnectionImpl::deleteQueue(SessionHandle handle, char* queue) +{ + Mutex::ScopedLock _lock(lock); + RCSession* sess = (RCSession*) handle.impl; + + sess->session.queueDelete(client::arg::queue=queue); + for (vector<string>::iterator iter = sess->dests.begin(); + iter != sess->dests.end(); iter++) + if (*iter == queue) { + sess->subscriptions->cancel(queue); + sess->dests.erase(iter); + break; + } +} + +void ResilientConnectionImpl::bind(SessionHandle handle, + char* exchange, char* queue, char* key) +{ + Mutex::ScopedLock _lock(lock); + RCSession* sess = (RCSession*) handle.impl; + + sess->session.exchangeBind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); +} + +void ResilientConnectionImpl::unbind(SessionHandle handle, + char* exchange, char* queue, char* key) +{ + Mutex::ScopedLock _lock(lock); + RCSession* sess = (RCSession*) handle.impl; + + sess->session.exchangeUnbind(client::arg::exchange=exchange, client::arg::queue=queue, client::arg::bindingKey=key); +} + +void ResilientConnectionImpl::notify() +{ + if (notifyFd != -1) + { + int unused_ret; //Suppress warnings about ignoring return value. + unused_ret = ::write(notifyFd, ".", 1); + } +} + + +void ResilientConnectionImpl::setNotifyFd(int fd) +{ + notifyFd = fd; + if (notifyFd > 0) { + int original = fcntl(notifyFd, F_GETFL); + fcntl(notifyFd, F_SETFL, O_NONBLOCK | original); + } +} + +void ResilientConnectionImpl::run() +{ + int delay(delayMin); + + while (true) { + try { + QPID_LOG(trace, "Trying to open connection..."); + connection.open(settings.impl->getClientSettings()); + { + Mutex::ScopedLock _lock(lock); + connected = true; + EnqueueEvent(ResilientConnectionEvent::CONNECTED); + + while (connected) + cond.wait(lock); + delay = delayMin; + + while (!sessions.empty()) { + set<RCSession::Ptr>::iterator iter = sessions.begin(); + RCSession::Ptr sess = *iter; + sessions.erase(iter); + EnqueueEvent(ResilientConnectionEvent::SESSION_CLOSED, sess->userContext); + Mutex::ScopedUnlock _u(lock); + sess->stop(); + + // Nullify the intrusive pointer within the scoped unlock, otherwise, + // the reference is held until overwritted above (under lock) which causes + // the session destructor to be called with the lock held. + sess = 0; + } + + EnqueueEvent(ResilientConnectionEvent::DISCONNECTED); + + if (shutdown) + return; + } + connection.close(); + } catch (exception &e) { + QPID_LOG(debug, "connection.open exception: " << e.what()); + Mutex::ScopedLock _lock(lock); + lastError = e.what(); + if (delay < delayMax) + delay *= delayFactor; + } + + ::qpid::sys::sleep(delay); + } +} + +void ResilientConnectionImpl::failure() +{ + Mutex::ScopedLock _lock(lock); + + connected = false; + lastError = "Closed by Peer"; + cond.notify(); +} + +void ResilientConnectionImpl::sessionClosed(RCSession*) +{ + Mutex::ScopedLock _lock(lock); + connected = false; + lastError = "Closed due to Session failure"; + cond.notify(); +} + +void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind kind, + void* sessionContext, + const MessageImpl& message, + const string& errorText) +{ + { + Mutex::ScopedLock _lock(lock); + ResilientConnectionEventImpl event(kind, message); + + event.sessionContext = sessionContext; + event.errorText = errorText; + + eventQueue.push_back(event); + } + + if (notifyFd != -1) + { + int unused_ret; //Suppress warnings about ignoring return value. + unused_ret = ::write(notifyFd, ".", 1); + } +} + + +//================================================================== +// Wrappers +//================================================================== + +ResilientConnection::ResilientConnection(const ConnectionSettings& settings) +{ + impl = new ResilientConnectionImpl(settings); +} + +ResilientConnection::~ResilientConnection() +{ + delete impl; +} + +bool ResilientConnection::isConnected() const +{ + return impl->isConnected(); +} + +bool ResilientConnection::getEvent(ResilientConnectionEvent& event) +{ + return impl->getEvent(event); +} + +void ResilientConnection::popEvent() +{ + impl->popEvent(); +} + +bool ResilientConnection::createSession(const char* name, void* sessionContext, SessionHandle& handle) +{ + return impl->createSession(name, sessionContext, handle); +} + +void ResilientConnection::destroySession(SessionHandle handle) +{ + impl->destroySession(handle); +} + +void ResilientConnection::sendMessage(SessionHandle handle, qmf::engine::Message& message) +{ + impl->sendMessage(handle, message); +} + +void ResilientConnection::declareQueue(SessionHandle handle, char* queue) +{ + impl->declareQueue(handle, queue); +} + +void ResilientConnection::deleteQueue(SessionHandle handle, char* queue) +{ + impl->deleteQueue(handle, queue); +} + +void ResilientConnection::bind(SessionHandle handle, char* exchange, char* queue, char* key) +{ + impl->bind(handle, exchange, queue, key); +} + +void ResilientConnection::unbind(SessionHandle handle, char* exchange, char* queue, char* key) +{ + impl->unbind(handle, exchange, queue, key); +} + +void ResilientConnection::setNotifyFd(int fd) +{ + impl->setNotifyFd(fd); +} + +void ResilientConnection::notify() +{ + impl->notify(); +} + diff --git a/qpid/cpp/src/qmf/engine/SchemaImpl.cpp b/qpid/cpp/src/qmf/engine/SchemaImpl.cpp new file mode 100644 index 0000000000..f75663e131 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/SchemaImpl.cpp @@ -0,0 +1,614 @@ +/* + * 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 "qmf/engine/SchemaImpl.h" +#include <qpid/framing/Buffer.h> +#include <qpid/framing/FieldTable.h> +#include <qpid/framing/Uuid.h> +#include <string.h> +#include <string> +#include <vector> +#include <sstream> + +using namespace std; +using namespace qmf::engine; +using qpid::framing::Buffer; +using qpid::framing::FieldTable; +using qpid::framing::Uuid; + +SchemaHash::SchemaHash() +{ + for (int idx = 0; idx < 16; idx++) + hash[idx] = 0x5A; +} + +void SchemaHash::encode(Buffer& buffer) const +{ + buffer.putBin128(hash); +} + +void SchemaHash::decode(Buffer& buffer) +{ + buffer.getBin128(hash); +} + +void SchemaHash::update(uint8_t data) +{ + update((char*) &data, 1); +} + +void SchemaHash::update(const char* data, uint32_t len) +{ + union h { + uint8_t b[16]; + uint64_t q[2]; + }* h = reinterpret_cast<union h*>(&hash[0]); + uint64_t* first = &h->q[0]; + uint64_t* second = &h->q[1]; + for (uint32_t idx = 0; idx < len; idx++) { + *first = *first ^ (uint64_t) data[idx]; + *second = *second << 1; + *second |= ((*first & 0x8000000000000000LL) >> 63); + *first = *first << 1; + *first = *first ^ *second; + } +} + +bool SchemaHash::operator==(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) == 0; +} + +bool SchemaHash::operator<(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) < 0; +} + +bool SchemaHash::operator>(const SchemaHash& other) const +{ + return ::memcmp(&hash, &other.hash, 16) > 0; +} + +SchemaArgumentImpl::SchemaArgumentImpl(Buffer& buffer) +{ + FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typecode = (Typecode) map.getAsInt("type"); + unit = map.getAsString("unit"); + description = map.getAsString("desc"); + + dir = DIR_IN; + string dstr(map.getAsString("dir")); + if (dstr == "O") + dir = DIR_OUT; + else if (dstr == "IO") + dir = DIR_IN_OUT; +} + +SchemaArgument* SchemaArgumentImpl::factory(Buffer& buffer) +{ + SchemaArgumentImpl* impl(new SchemaArgumentImpl(buffer)); + return new SchemaArgument(impl); +} + +void SchemaArgumentImpl::encode(Buffer& buffer) const +{ + FieldTable map; + + map.setString("name", name); + map.setInt("type", (int) typecode); + if (dir == DIR_IN) + map.setString("dir", "I"); + else if (dir == DIR_OUT) + map.setString("dir", "O"); + else + map.setString("dir", "IO"); + if (!unit.empty()) + map.setString("unit", unit); + if (!description.empty()) + map.setString("desc", description); + + map.encode(buffer); +} + +void SchemaArgumentImpl::updateHash(SchemaHash& hash) const +{ + hash.update(name); + hash.update(typecode); + hash.update(dir); + hash.update(unit); + hash.update(description); +} + +SchemaMethodImpl::SchemaMethodImpl(Buffer& buffer) +{ + FieldTable map; + int argCount; + + map.decode(buffer); + name = map.getAsString("name"); + argCount = map.getAsInt("argCount"); + description = map.getAsString("desc"); + + for (int idx = 0; idx < argCount; idx++) { + SchemaArgument* arg = SchemaArgumentImpl::factory(buffer); + addArgument(arg); + } +} + +SchemaMethod* SchemaMethodImpl::factory(Buffer& buffer) +{ + SchemaMethodImpl* impl(new SchemaMethodImpl(buffer)); + return new SchemaMethod(impl); +} + +void SchemaMethodImpl::encode(Buffer& buffer) const +{ + FieldTable map; + + map.setString("name", name); + map.setInt("argCount", arguments.size()); + if (!description.empty()) + map.setString("desc", description); + map.encode(buffer); + + for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + (*iter)->impl->encode(buffer); +} + +void SchemaMethodImpl::addArgument(const SchemaArgument* argument) +{ + arguments.push_back(argument); +} + +const SchemaArgument* SchemaMethodImpl::getArgument(int idx) const +{ + int count = 0; + for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++, count++) + if (idx == count) + return (*iter); + return 0; +} + +void SchemaMethodImpl::updateHash(SchemaHash& hash) const +{ + hash.update(name); + hash.update(description); + for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + (*iter)->impl->updateHash(hash); +} + +SchemaPropertyImpl::SchemaPropertyImpl(Buffer& buffer) +{ + FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typecode = (Typecode) map.getAsInt("type"); + access = (Access) map.getAsInt("access"); + index = map.getAsInt("index") != 0; + optional = map.getAsInt("optional") != 0; + unit = map.getAsString("unit"); + description = map.getAsString("desc"); +} + +SchemaProperty* SchemaPropertyImpl::factory(Buffer& buffer) +{ + SchemaPropertyImpl* impl(new SchemaPropertyImpl(buffer)); + return new SchemaProperty(impl); +} + +void SchemaPropertyImpl::encode(Buffer& buffer) const +{ + FieldTable map; + + map.setString("name", name); + map.setInt("type", (int) typecode); + map.setInt("access", (int) access); + map.setInt("index", index ? 1 : 0); + map.setInt("optional", optional ? 1 : 0); + if (!unit.empty()) + map.setString("unit", unit); + if (!description.empty()) + map.setString("desc", description); + + map.encode(buffer); +} + +void SchemaPropertyImpl::updateHash(SchemaHash& hash) const +{ + hash.update(name); + hash.update(typecode); + hash.update(access); + hash.update(index); + hash.update(optional); + hash.update(unit); + hash.update(description); +} + +SchemaStatisticImpl::SchemaStatisticImpl(Buffer& buffer) +{ + FieldTable map; + map.decode(buffer); + + name = map.getAsString("name"); + typecode = (Typecode) map.getAsInt("type"); + unit = map.getAsString("unit"); + description = map.getAsString("desc"); +} + +SchemaStatistic* SchemaStatisticImpl::factory(Buffer& buffer) +{ + SchemaStatisticImpl* impl(new SchemaStatisticImpl(buffer)); + return new SchemaStatistic(impl); +} + +void SchemaStatisticImpl::encode(Buffer& buffer) const +{ + FieldTable map; + + map.setString("name", name); + map.setInt("type", (int) typecode); + if (!unit.empty()) + map.setString("unit", unit); + if (!description.empty()) + map.setString("desc", description); + + map.encode(buffer); +} + +void SchemaStatisticImpl::updateHash(SchemaHash& hash) const +{ + hash.update(name); + hash.update(typecode); + hash.update(unit); + hash.update(description); +} + +SchemaClassKeyImpl::SchemaClassKeyImpl(const string& p, const string& n, const SchemaHash& h) : package(p), name(n), hash(h) {} + +SchemaClassKeyImpl::SchemaClassKeyImpl(Buffer& buffer) : package(packageContainer), name(nameContainer), hash(hashContainer) +{ + buffer.getShortString(packageContainer); + buffer.getShortString(nameContainer); + hashContainer.decode(buffer); +} + +SchemaClassKey* SchemaClassKeyImpl::factory(const string& package, const string& name, const SchemaHash& hash) +{ + SchemaClassKeyImpl* impl(new SchemaClassKeyImpl(package, name, hash)); + return new SchemaClassKey(impl); +} + +SchemaClassKey* SchemaClassKeyImpl::factory(Buffer& buffer) +{ + SchemaClassKeyImpl* impl(new SchemaClassKeyImpl(buffer)); + return new SchemaClassKey(impl); +} + +void SchemaClassKeyImpl::encode(Buffer& buffer) const +{ + buffer.putShortString(package); + buffer.putShortString(name); + hash.encode(buffer); +} + +bool SchemaClassKeyImpl::operator==(const SchemaClassKeyImpl& other) const +{ + return package == other.package && + name == other.name && + hash == other.hash; +} + +bool SchemaClassKeyImpl::operator<(const SchemaClassKeyImpl& other) const +{ + if (package < other.package) return true; + if (package > other.package) return false; + if (name < other.name) return true; + if (name > other.name) return false; + return hash < other.hash; +} + +const string& SchemaClassKeyImpl::str() const +{ + Uuid printableHash(hash.get()); + stringstream str; + str << package << ":" << name << "(" << printableHash << ")"; + repr = str.str(); + return repr; +} + +SchemaObjectClassImpl::SchemaObjectClassImpl(Buffer& buffer) : hasHash(true), classKey(SchemaClassKeyImpl::factory(package, name, hash)) +{ + buffer.getShortString(package); + buffer.getShortString(name); + hash.decode(buffer); + + uint16_t propCount = buffer.getShort(); + uint16_t statCount = buffer.getShort(); + uint16_t methodCount = buffer.getShort(); + + for (uint16_t idx = 0; idx < propCount; idx++) { + const SchemaProperty* property = SchemaPropertyImpl::factory(buffer); + addProperty(property); + } + + for (uint16_t idx = 0; idx < statCount; idx++) { + const SchemaStatistic* statistic = SchemaStatisticImpl::factory(buffer); + addStatistic(statistic); + } + + for (uint16_t idx = 0; idx < methodCount; idx++) { + SchemaMethod* method = SchemaMethodImpl::factory(buffer); + addMethod(method); + } +} + +SchemaObjectClass* SchemaObjectClassImpl::factory(Buffer& buffer) +{ + SchemaObjectClassImpl* impl(new SchemaObjectClassImpl(buffer)); + return new SchemaObjectClass(impl); +} + +void SchemaObjectClassImpl::encode(Buffer& buffer) const +{ + buffer.putOctet((uint8_t) CLASS_OBJECT); + buffer.putShortString(package); + buffer.putShortString(name); + hash.encode(buffer); + //buffer.putOctet(0); // No parent class + buffer.putShort((uint16_t) properties.size()); + buffer.putShort((uint16_t) statistics.size()); + buffer.putShort((uint16_t) methods.size()); + + for (vector<const SchemaProperty*>::const_iterator iter = properties.begin(); + iter != properties.end(); iter++) + (*iter)->impl->encode(buffer); + for (vector<const SchemaStatistic*>::const_iterator iter = statistics.begin(); + iter != statistics.end(); iter++) + (*iter)->impl->encode(buffer); + for (vector<const SchemaMethod*>::const_iterator iter = methods.begin(); + iter != methods.end(); iter++) + (*iter)->impl->encode(buffer); +} + +const SchemaClassKey* SchemaObjectClassImpl::getClassKey() const +{ + if (!hasHash) { + hasHash = true; + hash.update(package); + hash.update(name); + for (vector<const SchemaProperty*>::const_iterator iter = properties.begin(); + iter != properties.end(); iter++) + (*iter)->impl->updateHash(hash); + for (vector<const SchemaStatistic*>::const_iterator iter = statistics.begin(); + iter != statistics.end(); iter++) + (*iter)->impl->updateHash(hash); + for (vector<const SchemaMethod*>::const_iterator iter = methods.begin(); + iter != methods.end(); iter++) + (*iter)->impl->updateHash(hash); + } + + return classKey.get(); +} + +void SchemaObjectClassImpl::addProperty(const SchemaProperty* property) +{ + properties.push_back(property); +} + +void SchemaObjectClassImpl::addStatistic(const SchemaStatistic* statistic) +{ + statistics.push_back(statistic); +} + +void SchemaObjectClassImpl::addMethod(const SchemaMethod* method) +{ + methods.push_back(method); +} + +const SchemaProperty* SchemaObjectClassImpl::getProperty(int idx) const +{ + int count = 0; + for (vector<const SchemaProperty*>::const_iterator iter = properties.begin(); + iter != properties.end(); iter++, count++) + if (idx == count) + return *iter; + return 0; +} + +const SchemaStatistic* SchemaObjectClassImpl::getStatistic(int idx) const +{ + int count = 0; + for (vector<const SchemaStatistic*>::const_iterator iter = statistics.begin(); + iter != statistics.end(); iter++, count++) + if (idx == count) + return *iter; + return 0; +} + +const SchemaMethod* SchemaObjectClassImpl::getMethod(int idx) const +{ + int count = 0; + for (vector<const SchemaMethod*>::const_iterator iter = methods.begin(); + iter != methods.end(); iter++, count++) + if (idx == count) + return *iter; + return 0; +} + +SchemaEventClassImpl::SchemaEventClassImpl(Buffer& buffer) : hasHash(true), classKey(SchemaClassKeyImpl::factory(package, name, hash)) +{ + buffer.getShortString(package); + buffer.getShortString(name); + hash.decode(buffer); + + uint16_t argCount = buffer.getShort(); + + for (uint16_t idx = 0; idx < argCount; idx++) { + SchemaArgument* argument = SchemaArgumentImpl::factory(buffer); + addArgument(argument); + } +} + +SchemaEventClass* SchemaEventClassImpl::factory(Buffer& buffer) +{ + SchemaEventClassImpl* impl(new SchemaEventClassImpl(buffer)); + return new SchemaEventClass(impl); +} + +void SchemaEventClassImpl::encode(Buffer& buffer) const +{ + buffer.putOctet((uint8_t) CLASS_EVENT); + buffer.putShortString(package); + buffer.putShortString(name); + hash.encode(buffer); + buffer.putShort((uint16_t) arguments.size()); + + for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + (*iter)->impl->encode(buffer); +} + +const SchemaClassKey* SchemaEventClassImpl::getClassKey() const +{ + if (!hasHash) { + hasHash = true; + hash.update(package); + hash.update(name); + for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++) + (*iter)->impl->updateHash(hash); + } + return classKey.get(); +} + +void SchemaEventClassImpl::addArgument(const SchemaArgument* argument) +{ + arguments.push_back(argument); +} + +const SchemaArgument* SchemaEventClassImpl::getArgument(int idx) const +{ + int count = 0; + for (vector<const SchemaArgument*>::const_iterator iter = arguments.begin(); + iter != arguments.end(); iter++, count++) + if (idx == count) + return (*iter); + return 0; +} + + +//================================================================== +// Wrappers +//================================================================== + +SchemaArgument::SchemaArgument(const char* name, Typecode typecode) { impl = new SchemaArgumentImpl(name, typecode); } +SchemaArgument::SchemaArgument(SchemaArgumentImpl* i) : impl(i) {} +SchemaArgument::SchemaArgument(const SchemaArgument& from) : impl(new SchemaArgumentImpl(*(from.impl))) {} +SchemaArgument::~SchemaArgument() { delete impl; } +void SchemaArgument::setDirection(Direction dir) { impl->setDirection(dir); } +void SchemaArgument::setUnit(const char* val) { impl->setUnit(val); } +void SchemaArgument::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaArgument::getName() const { return impl->getName().c_str(); } +Typecode SchemaArgument::getType() const { return impl->getType(); } +Direction SchemaArgument::getDirection() const { return impl->getDirection(); } +const char* SchemaArgument::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaArgument::getDesc() const { return impl->getDesc().c_str(); } + +SchemaMethod::SchemaMethod(const char* name) : impl(new SchemaMethodImpl(name)) {} +SchemaMethod::SchemaMethod(SchemaMethodImpl* i) : impl(i) {} +SchemaMethod::SchemaMethod(const SchemaMethod& from) : impl(new SchemaMethodImpl(*(from.impl))) {} +SchemaMethod::~SchemaMethod() { delete impl; } +void SchemaMethod::addArgument(const SchemaArgument* argument) { impl->addArgument(argument); } +void SchemaMethod::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaMethod::getName() const { return impl->getName().c_str(); } +const char* SchemaMethod::getDesc() const { return impl->getDesc().c_str(); } +int SchemaMethod::getArgumentCount() const { return impl->getArgumentCount(); } +const SchemaArgument* SchemaMethod::getArgument(int idx) const { return impl->getArgument(idx); } + +SchemaProperty::SchemaProperty(const char* name, Typecode typecode) : impl(new SchemaPropertyImpl(name, typecode)) {} +SchemaProperty::SchemaProperty(SchemaPropertyImpl* i) : impl(i) {} +SchemaProperty::SchemaProperty(const SchemaProperty& from) : impl(new SchemaPropertyImpl(*(from.impl))) {} +SchemaProperty::~SchemaProperty() { delete impl; } +void SchemaProperty::setAccess(Access access) { impl->setAccess(access); } +void SchemaProperty::setIndex(bool val) { impl->setIndex(val); } +void SchemaProperty::setOptional(bool val) { impl->setOptional(val); } +void SchemaProperty::setUnit(const char* val) { impl->setUnit(val); } +void SchemaProperty::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaProperty::getName() const { return impl->getName().c_str(); } +Typecode SchemaProperty::getType() const { return impl->getType(); } +Access SchemaProperty::getAccess() const { return impl->getAccess(); } +bool SchemaProperty::isIndex() const { return impl->isIndex(); } +bool SchemaProperty::isOptional() const { return impl->isOptional(); } +const char* SchemaProperty::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaProperty::getDesc() const { return impl->getDesc().c_str(); } + +SchemaStatistic::SchemaStatistic(const char* name, Typecode typecode) : impl(new SchemaStatisticImpl(name, typecode)) {} +SchemaStatistic::SchemaStatistic(SchemaStatisticImpl* i) : impl(i) {} +SchemaStatistic::SchemaStatistic(const SchemaStatistic& from) : impl(new SchemaStatisticImpl(*(from.impl))) {} +SchemaStatistic::~SchemaStatistic() { delete impl; } +void SchemaStatistic::setUnit(const char* val) { impl->setUnit(val); } +void SchemaStatistic::setDesc(const char* desc) { impl->setDesc(desc); } +const char* SchemaStatistic::getName() const { return impl->getName().c_str(); } +Typecode SchemaStatistic::getType() const { return impl->getType(); } +const char* SchemaStatistic::getUnit() const { return impl->getUnit().c_str(); } +const char* SchemaStatistic::getDesc() const { return impl->getDesc().c_str(); } + +SchemaClassKey::SchemaClassKey(SchemaClassKeyImpl* i) : impl(i) {} +SchemaClassKey::SchemaClassKey(const SchemaClassKey& from) : impl(new SchemaClassKeyImpl(*(from.impl))) {} +SchemaClassKey::~SchemaClassKey() { delete impl; } +const char* SchemaClassKey::getPackageName() const { return impl->getPackageName().c_str(); } +const char* SchemaClassKey::getClassName() const { return impl->getClassName().c_str(); } +const uint8_t* SchemaClassKey::getHash() const { return impl->getHash(); } +const char* SchemaClassKey::asString() const { return impl->str().c_str(); } +bool SchemaClassKey::operator==(const SchemaClassKey& other) const { return *impl == *(other.impl); } +bool SchemaClassKey::operator<(const SchemaClassKey& other) const { return *impl < *(other.impl); } + +SchemaObjectClass::SchemaObjectClass(const char* package, const char* name) : impl(new SchemaObjectClassImpl(package, name)) {} +SchemaObjectClass::SchemaObjectClass(SchemaObjectClassImpl* i) : impl(i) {} +SchemaObjectClass::SchemaObjectClass(const SchemaObjectClass& from) : impl(new SchemaObjectClassImpl(*(from.impl))) {} +SchemaObjectClass::~SchemaObjectClass() { delete impl; } +void SchemaObjectClass::addProperty(const SchemaProperty* property) { impl->addProperty(property); } +void SchemaObjectClass::addStatistic(const SchemaStatistic* statistic) { impl->addStatistic(statistic); } +void SchemaObjectClass::addMethod(const SchemaMethod* method) { impl->addMethod(method); } +const SchemaClassKey* SchemaObjectClass::getClassKey() const { return impl->getClassKey(); } +int SchemaObjectClass::getPropertyCount() const { return impl->getPropertyCount(); } +int SchemaObjectClass::getStatisticCount() const { return impl->getStatisticCount(); } +int SchemaObjectClass::getMethodCount() const { return impl->getMethodCount(); } +const SchemaProperty* SchemaObjectClass::getProperty(int idx) const { return impl->getProperty(idx); } +const SchemaStatistic* SchemaObjectClass::getStatistic(int idx) const { return impl->getStatistic(idx); } +const SchemaMethod* SchemaObjectClass::getMethod(int idx) const { return impl->getMethod(idx); } + +SchemaEventClass::SchemaEventClass(const char* package, const char* name, Severity s) : impl(new SchemaEventClassImpl(package, name, s)) {} +SchemaEventClass::SchemaEventClass(SchemaEventClassImpl* i) : impl(i) {} +SchemaEventClass::SchemaEventClass(const SchemaEventClass& from) : impl(new SchemaEventClassImpl(*(from.impl))) {} +SchemaEventClass::~SchemaEventClass() { delete impl; } +void SchemaEventClass::addArgument(const SchemaArgument* argument) { impl->addArgument(argument); } +void SchemaEventClass::setDesc(const char* desc) { impl->setDesc(desc); } +const SchemaClassKey* SchemaEventClass::getClassKey() const { return impl->getClassKey(); } +Severity SchemaEventClass::getSeverity() const { return impl->getSeverity(); } +int SchemaEventClass::getArgumentCount() const { return impl->getArgumentCount(); } +const SchemaArgument* SchemaEventClass::getArgument(int idx) const { return impl->getArgument(idx); } + diff --git a/qpid/cpp/src/qmf/engine/SchemaImpl.h b/qpid/cpp/src/qmf/engine/SchemaImpl.h new file mode 100644 index 0000000000..8b079a5ec6 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/SchemaImpl.h @@ -0,0 +1,227 @@ +#ifndef _QmfEngineSchemaImpl_ +#define _QmfEngineSchemaImpl_ + +/* + * 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 "qmf/engine/Schema.h" +#include "qpid/framing/Buffer.h" + +#include <string> +#include <vector> +#include <memory> + +namespace qmf { +namespace engine { + + // TODO: Destructors for schema classes + // TODO: Add "frozen" attribute for schema classes so they can't be modified after + // they've been registered. + + class SchemaHash { + uint8_t hash[16]; + public: + SchemaHash(); + void encode(qpid::framing::Buffer& buffer) const; + void decode(qpid::framing::Buffer& buffer); + void update(const char* data, uint32_t len); + void update(uint8_t data); + void update(const std::string& data) { update(data.c_str(), data.size()); } + void update(Typecode t) { update((uint8_t) t); } + void update(Direction d) { update((uint8_t) d); } + void update(Access a) { update((uint8_t) a); } + void update(bool b) { update((uint8_t) (b ? 1 : 0)); } + const uint8_t* get() const { return hash; } + bool operator==(const SchemaHash& other) const; + bool operator<(const SchemaHash& other) const; + bool operator>(const SchemaHash& other) const; + }; + + struct SchemaArgumentImpl { + std::string name; + Typecode typecode; + Direction dir; + std::string unit; + std::string description; + + SchemaArgumentImpl(const char* n, Typecode t) : name(n), typecode(t), dir(DIR_IN) {} + SchemaArgumentImpl(qpid::framing::Buffer& buffer); + static SchemaArgument* factory(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void setDirection(Direction d) { dir = d; } + void setUnit(const char* val) { unit = val; } + void setDesc(const char* desc) { description = desc; } + const std::string& getName() const { return name; } + Typecode getType() const { return typecode; } + Direction getDirection() const { return dir; } + const std::string& getUnit() const { return unit; } + const std::string& getDesc() const { return description; } + void updateHash(SchemaHash& hash) const; + }; + + struct SchemaMethodImpl { + std::string name; + std::string description; + std::vector<const SchemaArgument*> arguments; + + SchemaMethodImpl(const char* n) : name(n) {} + SchemaMethodImpl(qpid::framing::Buffer& buffer); + static SchemaMethod* factory(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void addArgument(const SchemaArgument* argument); + void setDesc(const char* desc) { description = desc; } + const std::string& getName() const { return name; } + const std::string& getDesc() const { return description; } + int getArgumentCount() const { return arguments.size(); } + const SchemaArgument* getArgument(int idx) const; + void updateHash(SchemaHash& hash) const; + }; + + struct SchemaPropertyImpl { + std::string name; + Typecode typecode; + Access access; + bool index; + bool optional; + std::string unit; + std::string description; + + SchemaPropertyImpl(const char* n, Typecode t) : name(n), typecode(t), access(ACCESS_READ_ONLY), index(false), optional(false) {} + SchemaPropertyImpl(qpid::framing::Buffer& buffer); + static SchemaProperty* factory(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void setAccess(Access a) { access = a; } + void setIndex(bool val) { index = val; } + void setOptional(bool val) { optional = val; } + void setUnit(const char* val) { unit = val; } + void setDesc(const char* desc) { description = desc; } + const std::string& getName() const { return name; } + Typecode getType() const { return typecode; } + Access getAccess() const { return access; } + bool isIndex() const { return index; } + bool isOptional() const { return optional; } + const std::string& getUnit() const { return unit; } + const std::string& getDesc() const { return description; } + void updateHash(SchemaHash& hash) const; + }; + + struct SchemaStatisticImpl { + std::string name; + Typecode typecode; + std::string unit; + std::string description; + + SchemaStatisticImpl(const char* n, Typecode t) : name(n), typecode(t) {} + SchemaStatisticImpl(qpid::framing::Buffer& buffer); + static SchemaStatistic* factory(qpid::framing::Buffer& buffer); + void encode(qpid::framing::Buffer& buffer) const; + void setUnit(const char* val) { unit = val; } + void setDesc(const char* desc) { description = desc; } + const std::string& getName() const { return name; } + Typecode getType() const { return typecode; } + const std::string& getUnit() const { return unit; } + const std::string& getDesc() const { return description; } + void updateHash(SchemaHash& hash) const; + }; + + struct SchemaClassKeyImpl { + const std::string& package; + const std::string& name; + const SchemaHash& hash; + mutable std::string repr; + + // The *Container elements are only used if there isn't an external place to + // store these values. + std::string packageContainer; + std::string nameContainer; + SchemaHash hashContainer; + + SchemaClassKeyImpl(const std::string& package, const std::string& name, const SchemaHash& hash); + SchemaClassKeyImpl(qpid::framing::Buffer& buffer); + static SchemaClassKey* factory(const std::string& package, const std::string& name, const SchemaHash& hash); + static SchemaClassKey* factory(qpid::framing::Buffer& buffer); + + const std::string& getPackageName() const { return package; } + const std::string& getClassName() const { return name; } + const uint8_t* getHash() const { return hash.get(); } + + void encode(qpid::framing::Buffer& buffer) const; + bool operator==(const SchemaClassKeyImpl& other) const; + bool operator<(const SchemaClassKeyImpl& other) const; + const std::string& str() const; + }; + + struct SchemaObjectClassImpl { + std::string package; + std::string name; + mutable SchemaHash hash; + mutable bool hasHash; + std::auto_ptr<SchemaClassKey> classKey; + std::vector<const SchemaProperty*> properties; + std::vector<const SchemaStatistic*> statistics; + std::vector<const SchemaMethod*> methods; + + SchemaObjectClassImpl(const char* p, const char* n) : + package(p), name(n), hasHash(false), classKey(SchemaClassKeyImpl::factory(package, name, hash)) {} + SchemaObjectClassImpl(qpid::framing::Buffer& buffer); + static SchemaObjectClass* factory(qpid::framing::Buffer& buffer); + + void encode(qpid::framing::Buffer& buffer) const; + void addProperty(const SchemaProperty* property); + void addStatistic(const SchemaStatistic* statistic); + void addMethod(const SchemaMethod* method); + + const SchemaClassKey* getClassKey() const; + int getPropertyCount() const { return properties.size(); } + int getStatisticCount() const { return statistics.size(); } + int getMethodCount() const { return methods.size(); } + const SchemaProperty* getProperty(int idx) const; + const SchemaStatistic* getStatistic(int idx) const; + const SchemaMethod* getMethod(int idx) const; + }; + + struct SchemaEventClassImpl { + std::string package; + std::string name; + mutable SchemaHash hash; + mutable bool hasHash; + std::auto_ptr<SchemaClassKey> classKey; + std::string description; + Severity severity; + std::vector<const SchemaArgument*> arguments; + + SchemaEventClassImpl(const char* p, const char* n, Severity sev) : + package(p), name(n), hasHash(false), classKey(SchemaClassKeyImpl::factory(package, name, hash)), severity(sev) {} + SchemaEventClassImpl(qpid::framing::Buffer& buffer); + static SchemaEventClass* factory(qpid::framing::Buffer& buffer); + + void encode(qpid::framing::Buffer& buffer) const; + void addArgument(const SchemaArgument* argument); + void setDesc(const char* desc) { description = desc; } + + const SchemaClassKey* getClassKey() const; + Severity getSeverity() const { return severity; } + int getArgumentCount() const { return arguments.size(); } + const SchemaArgument* getArgument(int idx) const; + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/SequenceManager.cpp b/qpid/cpp/src/qmf/engine/SequenceManager.cpp new file mode 100644 index 0000000000..4a4644a8b9 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/SequenceManager.cpp @@ -0,0 +1,96 @@ +/* + * 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 "qmf/engine/SequenceManager.h" + +using namespace std; +using namespace qmf::engine; +using namespace qpid::sys; + +SequenceManager::SequenceManager() : nextSequence(1) {} + +void SequenceManager::setUnsolicitedContext(SequenceContext::Ptr ctx) +{ + unsolicitedContext = ctx; +} + +uint32_t SequenceManager::reserve(SequenceContext::Ptr ctx) +{ + Mutex::ScopedLock _lock(lock); + if (ctx.get() == 0) + ctx = unsolicitedContext; + uint32_t seq = nextSequence; + while (contextMap.find(seq) != contextMap.end()) + seq = seq < 0xFFFFFFFF ? seq + 1 : 1; + nextSequence = seq < 0xFFFFFFFF ? seq + 1 : 1; + contextMap[seq] = ctx; + ctx->reserve(); + return seq; +} + +void SequenceManager::release(uint32_t sequence) +{ + Mutex::ScopedLock _lock(lock); + + if (sequence == 0) { + if (unsolicitedContext.get() != 0) + unsolicitedContext->release(); + return; + } + + map<uint32_t, SequenceContext::Ptr>::iterator iter = contextMap.find(sequence); + if (iter != contextMap.end()) { + if (iter->second != 0) + iter->second->release(); + contextMap.erase(iter); + } +} + +void SequenceManager::releaseAll() +{ + Mutex::ScopedLock _lock(lock); + contextMap.clear(); +} + +void SequenceManager::dispatch(uint8_t opcode, uint32_t sequence, const string& routingKey, qpid::framing::Buffer& buffer) +{ + Mutex::ScopedLock _lock(lock); + bool done; + + if (sequence == 0) { + if (unsolicitedContext.get() != 0) { + done = unsolicitedContext->handleMessage(opcode, sequence, routingKey, buffer); + if (done) + unsolicitedContext->release(); + } + return; + } + + map<uint32_t, SequenceContext::Ptr>::iterator iter = contextMap.find(sequence); + if (iter != contextMap.end()) { + if (iter->second != 0) { + done = iter->second->handleMessage(opcode, sequence, routingKey, buffer); + if (done) { + iter->second->release(); + contextMap.erase(iter); + } + } + } +} + diff --git a/qpid/cpp/src/qmf/engine/SequenceManager.h b/qpid/cpp/src/qmf/engine/SequenceManager.h new file mode 100644 index 0000000000..9e47e38610 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/SequenceManager.h @@ -0,0 +1,68 @@ +#ifndef _QmfEngineSequenceManager_ +#define _QmfEngineSequenceManager_ + +/* + * 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 "qpid/sys/Mutex.h" +#include <boost/shared_ptr.hpp> +#include <map> + +namespace qpid { + namespace framing { + class Buffer; + } +} + +namespace qmf { +namespace engine { + + class SequenceContext { + public: + typedef boost::shared_ptr<SequenceContext> Ptr; + SequenceContext() {} + virtual ~SequenceContext() {} + + virtual void reserve() = 0; + virtual bool handleMessage(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer) = 0; + virtual void release() = 0; + }; + + class SequenceManager { + public: + SequenceManager(); + + void setUnsolicitedContext(SequenceContext::Ptr ctx); + uint32_t reserve(SequenceContext::Ptr ctx = SequenceContext::Ptr()); + void release(uint32_t sequence); + void releaseAll(); + void dispatch(uint8_t opcode, uint32_t sequence, const std::string& routingKey, qpid::framing::Buffer& buffer); + + private: + mutable qpid::sys::Mutex lock; + uint32_t nextSequence; + SequenceContext::Ptr unsolicitedContext; + std::map<uint32_t, SequenceContext::Ptr> contextMap; + }; + +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/engine/ValueImpl.cpp b/qpid/cpp/src/qmf/engine/ValueImpl.cpp new file mode 100644 index 0000000000..f9ebbf5028 --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ValueImpl.cpp @@ -0,0 +1,571 @@ +/* + * 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 "qmf/engine/ValueImpl.h" +#include <qpid/framing/FieldValue.h> +#include <qpid/framing/FieldTable.h> +#include <qpid/framing/List.h> +#include <qpid/log/Statement.h> + +using namespace std; +using namespace qmf::engine; +//using qpid::framing::Buffer; +//using qpid::framing::FieldTable; +//using qpid::framing::FieldValue; +using namespace qpid::framing; + +ValueImpl::ValueImpl(Typecode t, Buffer& buf) : typecode(t) +{ + uint64_t first; + uint64_t second; + FieldTable ft; + List fl; + + switch (typecode) { + case TYPE_UINT8 : value.u32 = (uint32_t) buf.getOctet(); break; + case TYPE_UINT16 : value.u32 = (uint32_t) buf.getShort(); break; + case TYPE_UINT32 : value.u32 = (uint32_t) buf.getLong(); break; + case TYPE_UINT64 : value.u64 = buf.getLongLong(); break; + case TYPE_SSTR : buf.getShortString(stringVal); break; + case TYPE_LSTR : buf.getMediumString(stringVal); break; + case TYPE_ABSTIME : value.s64 = buf.getLongLong(); break; + case TYPE_DELTATIME : value.u64 = buf.getLongLong(); break; + case TYPE_BOOL : value.boolVal = (buf.getOctet() != 0); break; + case TYPE_FLOAT : value.floatVal = buf.getFloat(); break; + case TYPE_DOUBLE : value.doubleVal = buf.getDouble(); break; + case TYPE_INT8 : value.s32 = (int32_t) ((int8_t) buf.getOctet()); break; + case TYPE_INT16 : value.s32 = (int32_t) ((int16_t) buf.getShort()); break; + case TYPE_INT32 : value.s32 = (int32_t) buf.getLong(); break; + case TYPE_INT64 : value.s64 = buf.getLongLong(); break; + case TYPE_UUID : buf.getBin128(value.uuidVal); break; + case TYPE_REF: + first = buf.getLongLong(); + second = buf.getLongLong(); + refVal.impl->setValue(first, second); + break; + + case TYPE_MAP: + ft.decode(buf); + initMap(ft); + break; + + case TYPE_LIST: + fl.decode(buf); + initList(fl); + break; + + case TYPE_ARRAY: + case TYPE_OBJECT: + default: + break; + } +} + +ValueImpl::ValueImpl(Typecode t, Typecode at) : typecode(t), valid(false), arrayTypecode(at) +{ +} + +ValueImpl::ValueImpl(Typecode t) : typecode(t) +{ + ::memset(&value, 0, sizeof(value)); +} + +Value* ValueImpl::factory(Typecode t, Buffer& b) +{ + ValueImpl* impl(new ValueImpl(t, b)); + return new Value(impl); +} + +Value* ValueImpl::factory(Typecode t) +{ + ValueImpl* impl(new ValueImpl(t)); + return new Value(impl); +} + +ValueImpl::~ValueImpl() +{ +} + +void ValueImpl::initMap(const FieldTable& ft) +{ + for (FieldTable::ValueMap::const_iterator iter = ft.begin(); + iter != ft.end(); iter++) { + const string& name(iter->first); + const FieldValue& fvalue(*iter->second); + uint8_t amqType = fvalue.getType(); + + if (amqType == 0x32) { + Value* subval(new Value(TYPE_UINT64)); + subval->setUint64(fvalue.get<int64_t>()); + insert(name.c_str(), subval); + } else if ((amqType & 0xCF) == 0x02) { + Value* subval(new Value(TYPE_UINT32)); + switch (amqType) { + case 0x02 : subval->setUint(fvalue.get<int>()); break; + case 0x12 : subval->setUint(fvalue.get<int>()); break; + case 0x22 : subval->setUint(fvalue.get<int>()); break; + } + insert(name.c_str(), subval); + } else if (amqType == 0x31) { // int64 + Value* subval(new Value(TYPE_INT64)); + subval->setInt64(fvalue.get<int64_t>()); + insert(name.c_str(), subval); + } else if ((amqType & 0xCF) == 0x01) { // 0x01:int8, 0x11:int16, 0x21:int21 + Value* subval(new Value(TYPE_INT32)); + subval->setInt((int32_t)fvalue.get<int>()); + insert(name.c_str(), subval); + } else if (amqType == 0x85 || amqType == 0x95) { + Value* subval(new Value(TYPE_LSTR)); + subval->setString(fvalue.get<string>().c_str()); + insert(name.c_str(), subval); + } else if (amqType == 0x23 || amqType == 0x33) { + Value* subval(new Value(TYPE_DOUBLE)); + subval->setDouble(fvalue.get<double>()); + insert(name.c_str(), subval); + } else if (amqType == 0xa8) { + FieldTable subFt; + bool valid = qpid::framing::getEncodedValue<FieldTable>(iter->second, subFt); + if (valid) { + Value* subval(new Value(TYPE_MAP)); + subval->impl->initMap(subFt); + insert(name.c_str(), subval); + } + } else if (amqType == 0xa9) { + List subList; + bool valid = qpid::framing::getEncodedValue<List>(iter->second, subList); + if (valid) { + Value* subval(new Value(TYPE_LIST)); + subval->impl->initList(subList); + insert(name.c_str(), subval); + } + } else if (amqType == 0x08) { + Value* subval(new Value(TYPE_BOOL)); + subval->setBool(fvalue.get<int>() ? true : false); + insert(name.c_str(), subval); + } else { + QPID_LOG(error, "Unable to decode unsupported AMQP typecode=" << amqType << " map index=" << name); + } + } +} + +void ValueImpl::mapToFieldTable(FieldTable& ft) const +{ + FieldTable subFt; + + for (map<string, Value>::const_iterator iter = mapVal.begin(); + iter != mapVal.end(); iter++) { + const string& name(iter->first); + const Value& subval(iter->second); + + switch (subval.getType()) { + case TYPE_UINT8: + case TYPE_UINT16: + case TYPE_UINT32: + ft.setUInt64(name, (uint64_t) subval.asUint()); + break; + case TYPE_UINT64: + case TYPE_DELTATIME: + ft.setUInt64(name, subval.asUint64()); + break; + case TYPE_SSTR: + case TYPE_LSTR: + ft.setString(name, subval.asString()); + break; + case TYPE_INT64: + case TYPE_ABSTIME: + ft.setInt64(name, subval.asInt64()); + break; + case TYPE_BOOL: + ft.set(name, FieldTable::ValuePtr(new BoolValue(subval.asBool()))); + break; + case TYPE_FLOAT: + ft.setFloat(name, subval.asFloat()); + break; + case TYPE_DOUBLE: + ft.setDouble(name, subval.asDouble()); + break; + case TYPE_INT8: + case TYPE_INT16: + case TYPE_INT32: + ft.setInt(name, subval.asInt()); + break; + case TYPE_MAP: + subFt.clear(); + subval.impl->mapToFieldTable(subFt); + ft.setTable(name, subFt); + break; + case TYPE_LIST: + { + List subList; + subval.impl->listToFramingList(subList); + ft.set(name, + ::qpid::framing::FieldTable::ValuePtr( + new ListValue( + subList))); + } break; + case TYPE_ARRAY: + case TYPE_OBJECT: + case TYPE_UUID: + case TYPE_REF: + default: + break; + } + } + } + + +void ValueImpl::initList(const List& fl) +{ + for (List::const_iterator iter = fl.begin(); + iter != fl.end(); iter++) { + const FieldValue& fvalue(*iter->get()); + uint8_t amqType = fvalue.getType(); + + if (amqType == 0x32) { + Value* subval(new Value(TYPE_UINT64)); + subval->setUint64(fvalue.get<int64_t>()); + appendToList(subval); + } else if ((amqType & 0xCF) == 0x02) { + Value* subval(new Value(TYPE_UINT32)); + switch (amqType) { + case 0x02 : subval->setUint(fvalue.get<int>()); break; // uint8 + case 0x12 : subval->setUint(fvalue.get<int>()); break; // uint16 + case 0x22 : subval->setUint(fvalue.get<int>()); break; // uint32 + } + appendToList(subval); + } else if (amqType == 0x31) { // int64 + Value* subval(new Value(TYPE_INT64)); + subval->setInt64(fvalue.get<int64_t>()); + appendToList(subval); + } else if ((amqType & 0xCF) == 0x01) { // 0x01:int8, 0x11:int16, 0x21:int32 + Value* subval(new Value(TYPE_INT32)); + subval->setInt((int32_t)fvalue.get<int>()); + appendToList(subval); + } else if (amqType == 0x85 || amqType == 0x95) { + Value* subval(new Value(TYPE_LSTR)); + subval->setString(fvalue.get<string>().c_str()); + appendToList(subval); + } else if (amqType == 0x23 || amqType == 0x33) { + Value* subval(new Value(TYPE_DOUBLE)); + subval->setDouble(fvalue.get<double>()); + appendToList(subval); + } else if (amqType == 0xa8) { + FieldTable subFt; + bool valid = qpid::framing::getEncodedValue<FieldTable>(*iter, subFt); + if (valid) { + Value* subval(new Value(TYPE_MAP)); + subval->impl->initMap(subFt); + appendToList(subval); + } + } else if (amqType == 0xa9) { + List subList; + bool valid = qpid::framing::getEncodedValue<List>(*iter, subList); + if (valid) { + Value *subVal(new Value(TYPE_LIST)); + subVal->impl->initList(subList); + appendToList(subVal); + } + } else if (amqType == 0x08) { + Value* subval(new Value(TYPE_BOOL)); + subval->setBool(fvalue.get<int>() ? true : false); + appendToList(subval); + } else { + QPID_LOG(error, "Unable to decode unsupported AMQP typecode =" << amqType); + } + } +} + +void ValueImpl::listToFramingList(List& fl) const +{ + for (vector<Value>::const_iterator iter = vectorVal.begin(); + iter != vectorVal.end(); iter++) { + const Value& subval(*iter); + + switch (subval.getType()) { + case TYPE_UINT8: + case TYPE_UINT16: + case TYPE_UINT32: + fl.push_back(List::ValuePtr(new Unsigned64Value((uint64_t) subval.asUint()))); + break; + case TYPE_UINT64: + case TYPE_DELTATIME: + fl.push_back(List::ValuePtr(new Unsigned64Value(subval.asUint64()))); + break; + case TYPE_SSTR: + case TYPE_LSTR: + fl.push_back(List::ValuePtr(new Str16Value(subval.asString()))); + break; + case TYPE_INT64: + case TYPE_ABSTIME: + fl.push_back(List::ValuePtr(new Integer64Value(subval.asInt64()))); + break; + case TYPE_BOOL: + fl.push_back(List::ValuePtr(new BoolValue(subval.asBool() ? 1 : 0))); + break; + case TYPE_FLOAT: + fl.push_back(List::ValuePtr(new FloatValue(subval.asFloat()))); + break; + case TYPE_DOUBLE: + fl.push_back(List::ValuePtr(new DoubleValue(subval.asDouble()))); + break; + case TYPE_INT8: + case TYPE_INT16: + case TYPE_INT32: + fl.push_back(List::ValuePtr(new IntegerValue(subval.asInt()))); + break; + case TYPE_MAP: + { + FieldTable subFt; + subval.impl->mapToFieldTable(subFt); + fl.push_back(List::ValuePtr(new FieldTableValue(subFt))); + + } break; + case TYPE_LIST: + { + List subList; + subval.impl->listToFramingList(subList); + fl.push_back(List::ValuePtr(new ListValue(subList))); + } break; + + case TYPE_ARRAY: + case TYPE_OBJECT: + case TYPE_UUID: + case TYPE_REF: + default: + break; + } + } + } + + + +void ValueImpl::encode(Buffer& buf) const +{ + FieldTable ft; + List fl; + + switch (typecode) { + case TYPE_UINT8 : buf.putOctet((uint8_t) value.u32); break; + case TYPE_UINT16 : buf.putShort((uint16_t) value.u32); break; + case TYPE_UINT32 : buf.putLong(value.u32); break; + case TYPE_UINT64 : buf.putLongLong(value.u64); break; + case TYPE_SSTR : buf.putShortString(stringVal); break; + case TYPE_LSTR : buf.putMediumString(stringVal); break; + case TYPE_ABSTIME : buf.putLongLong(value.s64); break; + case TYPE_DELTATIME : buf.putLongLong(value.u64); break; + case TYPE_BOOL : buf.putOctet(value.boolVal ? 1 : 0); break; + case TYPE_FLOAT : buf.putFloat(value.floatVal); break; + case TYPE_DOUBLE : buf.putDouble(value.doubleVal); break; + case TYPE_INT8 : buf.putOctet((uint8_t) value.s32); break; + case TYPE_INT16 : buf.putShort((uint16_t) value.s32); break; + case TYPE_INT32 : buf.putLong(value.s32); break; + case TYPE_INT64 : buf.putLongLong(value.s64); break; + case TYPE_UUID : buf.putBin128(value.uuidVal); break; + case TYPE_REF : refVal.impl->encode(buf); break; + case TYPE_MAP: + mapToFieldTable(ft); + ft.encode(buf); + break; + case TYPE_LIST: + listToFramingList(fl); + fl.encode(buf); + break; + + case TYPE_ARRAY: + case TYPE_OBJECT: + default: + break; + } +} + +uint32_t ValueImpl::encodedSize() const +{ + FieldTable ft; + List fl; + + switch (typecode) { + case TYPE_UINT8 : + case TYPE_BOOL : + case TYPE_INT8 : return 1; + + case TYPE_UINT16 : + case TYPE_INT16 : return 2; + + case TYPE_UINT32 : + case TYPE_INT32 : + case TYPE_FLOAT : return 4; + + case TYPE_UINT64 : + case TYPE_INT64 : + case TYPE_DOUBLE : + case TYPE_ABSTIME : + case TYPE_DELTATIME : return 8; + + case TYPE_UUID : + case TYPE_REF : return 16; + + case TYPE_SSTR : return 1 + stringVal.size(); + case TYPE_LSTR : return 2 + stringVal.size(); + case TYPE_MAP: + mapToFieldTable(ft); + return ft.encodedSize(); + + case TYPE_LIST: + listToFramingList(fl); + return fl.encodedSize(); + + case TYPE_ARRAY: + case TYPE_OBJECT: + default: + break; + } + + return 0; +} + +bool ValueImpl::keyInMap(const char* key) const +{ + return typecode == TYPE_MAP && mapVal.count(key) > 0; +} + +Value* ValueImpl::byKey(const char* key) +{ + if (keyInMap(key)) { + map<string, Value>::iterator iter = mapVal.find(key); + if (iter != mapVal.end()) + return &iter->second; + } + return 0; +} + +const Value* ValueImpl::byKey(const char* key) const +{ + if (keyInMap(key)) { + map<string, Value>::const_iterator iter = mapVal.find(key); + if (iter != mapVal.end()) + return &iter->second; + } + return 0; +} + +void ValueImpl::deleteKey(const char* key) +{ + mapVal.erase(key); +} + +void ValueImpl::insert(const char* key, Value* val) +{ + pair<string, Value> entry(key, *val); + mapVal.insert(entry); +} + +const char* ValueImpl::key(uint32_t idx) const +{ + map<string, Value>::const_iterator iter = mapVal.begin(); + for (uint32_t i = 0; i < idx; i++) { + if (iter == mapVal.end()) + break; + iter++; + } + + if (iter == mapVal.end()) + return 0; + else + return iter->first.c_str(); +} + +Value* ValueImpl::arrayItem(uint32_t) +{ + return 0; +} + +void ValueImpl::appendToArray(Value*) +{ +} + +void ValueImpl::deleteArrayItem(uint32_t) +{ +} + + +//================================================================== +// Wrappers +//================================================================== + +Value::Value(const Value& from) : impl(new ValueImpl(*(from.impl))) {} +Value::Value(Typecode t, Typecode at) : impl(new ValueImpl(t, at)) {} +Value::Value(ValueImpl* i) : impl(i) {} +Value::~Value() { delete impl;} + +Typecode Value::getType() const { return impl->getType(); } +bool Value::isNull() const { return impl->isNull(); } +void Value::setNull() { impl->setNull(); } +bool Value::isObjectId() const { return impl->isObjectId(); } +const ObjectId& Value::asObjectId() const { return impl->asObjectId(); } +void Value::setObjectId(const ObjectId& oid) { impl->setObjectId(oid); } +bool Value::isUint() const { return impl->isUint(); } +uint32_t Value::asUint() const { return impl->asUint(); } +void Value::setUint(uint32_t val) { impl->setUint(val); } +bool Value::isInt() const { return impl->isInt(); } +int32_t Value::asInt() const { return impl->asInt(); } +void Value::setInt(int32_t val) { impl->setInt(val); } +bool Value::isUint64() const { return impl->isUint64(); } +uint64_t Value::asUint64() const { return impl->asUint64(); } +void Value::setUint64(uint64_t val) { impl->setUint64(val); } +bool Value::isInt64() const { return impl->isInt64(); } +int64_t Value::asInt64() const { return impl->asInt64(); } +void Value::setInt64(int64_t val) { impl->setInt64(val); } +bool Value::isString() const { return impl->isString(); } +const char* Value::asString() const { return impl->asString(); } +void Value::setString(const char* val) { impl->setString(val); } +bool Value::isBool() const { return impl->isBool(); } +bool Value::asBool() const { return impl->asBool(); } +void Value::setBool(bool val) { impl->setBool(val); } +bool Value::isFloat() const { return impl->isFloat(); } +float Value::asFloat() const { return impl->asFloat(); } +void Value::setFloat(float val) { impl->setFloat(val); } +bool Value::isDouble() const { return impl->isDouble(); } +double Value::asDouble() const { return impl->asDouble(); } +void Value::setDouble(double val) { impl->setDouble(val); } +bool Value::isUuid() const { return impl->isUuid(); } +const uint8_t* Value::asUuid() const { return impl->asUuid(); } +void Value::setUuid(const uint8_t* val) { impl->setUuid(val); } +bool Value::isObject() const { return impl->isObject(); } +const Object* Value::asObject() const { return impl->asObject(); } +void Value::setObject(Object* val) { impl->setObject(val); } +bool Value::isMap() const { return impl->isMap(); } +bool Value::keyInMap(const char* key) const { return impl->keyInMap(key); } +Value* Value::byKey(const char* key) { return impl->byKey(key); } +const Value* Value::byKey(const char* key) const { return impl->byKey(key); } +void Value::deleteKey(const char* key) { impl->deleteKey(key); } +void Value::insert(const char* key, Value* val) { impl->insert(key, val); } +uint32_t Value::keyCount() const { return impl->keyCount(); } +const char* Value::key(uint32_t idx) const { return impl->key(idx); } +bool Value::isList() const { return impl->isList(); } +uint32_t Value::listItemCount() const { return impl->listItemCount(); } +Value* Value::listItem(uint32_t idx) { return impl->listItem(idx); } +void Value::appendToList(Value* val) { impl->appendToList(val); } +void Value::deleteListItem(uint32_t idx) { impl->deleteListItem(idx); } +bool Value::isArray() const { return impl->isArray(); } +Typecode Value::arrayType() const { return impl->arrayType(); } +uint32_t Value::arrayItemCount() const { return impl->arrayItemCount(); } +Value* Value::arrayItem(uint32_t idx) { return impl->arrayItem(idx); } +void Value::appendToArray(Value* val) { impl->appendToArray(val); } +void Value::deleteArrayItem(uint32_t idx) { impl->deleteArrayItem(idx); } + diff --git a/qpid/cpp/src/qmf/engine/ValueImpl.h b/qpid/cpp/src/qmf/engine/ValueImpl.h new file mode 100644 index 0000000000..8de8c5329f --- /dev/null +++ b/qpid/cpp/src/qmf/engine/ValueImpl.h @@ -0,0 +1,166 @@ +#ifndef _QmfEngineValueImpl_ +#define _QmfEngineValueImpl_ + +/* + * 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 <qmf/engine/Value.h> +#include <qmf/engine/ObjectIdImpl.h> +#include <qmf/engine/Object.h> +#include <qpid/framing/Buffer.h> +#include <string> +#include <string.h> +#include <map> +#include <vector> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace framing { + class FieldTable; + class List; +} +} + +namespace qmf { +namespace engine { + + // TODO: set valid flag on all value settors + // TODO: add a modified flag and accessors + + struct ValueImpl { + const Typecode typecode; + bool valid; + + ObjectId refVal; + std::string stringVal; + std::auto_ptr<Object> objectVal; + std::map<std::string, Value> mapVal; + std::vector<Value> vectorVal; + Typecode arrayTypecode; + + union { + uint32_t u32; + uint64_t u64; + int32_t s32; + int64_t s64; + bool boolVal; + float floatVal; + double doubleVal; + uint8_t uuidVal[16]; + } value; + + ValueImpl(const ValueImpl& from) : + typecode(from.typecode), valid(from.valid), refVal(from.refVal), stringVal(from.stringVal), + objectVal(from.objectVal.get() ? new Object(*(from.objectVal)) : 0), + mapVal(from.mapVal), vectorVal(from.vectorVal), arrayTypecode(from.arrayTypecode), + value(from.value) {} + + ValueImpl(Typecode t, Typecode at); + ValueImpl(Typecode t, qpid::framing::Buffer& b); + ValueImpl(Typecode t); + static Value* factory(Typecode t, qpid::framing::Buffer& b); + static Value* factory(Typecode t); + ~ValueImpl(); + + void encode(qpid::framing::Buffer& b) const; + uint32_t encodedSize() const; + + Typecode getType() const { return typecode; } + bool isNull() const { return !valid; } + void setNull() { valid = false; } + + bool isObjectId() const { return typecode == TYPE_REF; } + const ObjectId& asObjectId() const { return refVal; } + void setObjectId(const ObjectId& o) { refVal = o; } // TODO + + bool isUint() const { return typecode >= TYPE_UINT8 && typecode <= TYPE_UINT32; } + uint32_t asUint() const { return value.u32; } + void setUint(uint32_t val) { value.u32 = val; } + + bool isInt() const { return typecode >= TYPE_INT8 && typecode <= TYPE_INT32; } + int32_t asInt() const { return value.s32; } + void setInt(int32_t val) { value.s32 = val; } + + bool isUint64() const { return typecode == TYPE_UINT64 || typecode == TYPE_DELTATIME; } + uint64_t asUint64() const { return value.u64; } + void setUint64(uint64_t val) { value.u64 = val; } + + bool isInt64() const { return typecode == TYPE_INT64 || typecode == TYPE_ABSTIME; } + int64_t asInt64() const { return value.s64; } + void setInt64(int64_t val) { value.s64 = val; } + + bool isString() const { return typecode == TYPE_SSTR || typecode == TYPE_LSTR; } + const char* asString() const { return stringVal.c_str(); } + void setString(const char* val) { stringVal = val; } + + bool isBool() const { return typecode == TYPE_BOOL; } + bool asBool() const { return value.boolVal; } + void setBool(bool val) { value.boolVal = val; } + + bool isFloat() const { return typecode == TYPE_FLOAT; } + float asFloat() const { return value.floatVal; } + void setFloat(float val) { value.floatVal = val; } + + bool isDouble() const { return typecode == TYPE_DOUBLE; } + double asDouble() const { return value.doubleVal; } + void setDouble(double val) { value.doubleVal = val; } + + bool isUuid() const { return typecode == TYPE_UUID; } + const uint8_t* asUuid() const { return value.uuidVal; } + void setUuid(const uint8_t* val) { ::memcpy(value.uuidVal, val, 16); } + + bool isObject() const { return typecode == TYPE_OBJECT; } + Object* asObject() const { return objectVal.get(); } + void setObject(Object* val) { objectVal.reset(val); } + + bool isMap() const { return typecode == TYPE_MAP; } + bool keyInMap(const char* key) const; + Value* byKey(const char* key); + const Value* byKey(const char* key) const; + void deleteKey(const char* key); + void insert(const char* key, Value* val); + uint32_t keyCount() const { return mapVal.size(); } + const char* key(uint32_t idx) const; + + bool isList() const { return typecode == TYPE_LIST; } + uint32_t listItemCount() const { return vectorVal.size(); } + Value* listItem(uint32_t idx) { return idx < listItemCount() ? &vectorVal[idx] : 0; } + const Value* listItem(uint32_t idx) const { return idx < listItemCount() ? &vectorVal[idx] : 0; } + void appendToList(Value* val) { vectorVal.push_back(*val); } + void deleteListItem(uint32_t idx) { if (idx < listItemCount()) vectorVal.erase(vectorVal.begin()+idx); } + + bool isArray() const { return typecode == TYPE_ARRAY; } + Typecode arrayType() const { return arrayTypecode; } + uint32_t arrayItemCount() const { return 0; } + Value* arrayItem(uint32_t idx); + void appendToArray(Value* val); + void deleteArrayItem(uint32_t idx); + + private: + void mapToFieldTable(qpid::framing::FieldTable& ft) const; + void initMap(const qpid::framing::FieldTable& ft); + + void listToFramingList(qpid::framing::List& fl) const; + void initList(const qpid::framing::List& fl); + }; +} +} + +#endif + diff --git a/qpid/cpp/src/qmf/exceptions.cpp b/qpid/cpp/src/qmf/exceptions.cpp new file mode 100644 index 0000000000..be212f62f7 --- /dev/null +++ b/qpid/cpp/src/qmf/exceptions.cpp @@ -0,0 +1,37 @@ +/* + * + * 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 "qmf/exceptions.h" + +namespace qmf { + + QmfException::QmfException(const std::string& msg) : qpid::types::Exception(msg) {} + QmfException::~QmfException() throw() {} + + KeyNotFound::KeyNotFound(const std::string& msg) : QmfException("Key Not Found: " + msg) {} + KeyNotFound::~KeyNotFound() throw() {} + + IndexOutOfRange::IndexOutOfRange() : QmfException("Index out-of-range") {} + IndexOutOfRange::~IndexOutOfRange() throw() {} + + OperationTimedOut::OperationTimedOut() : QmfException("Timeout Expired") {} + OperationTimedOut::~OperationTimedOut() throw() {} +} + |