MojErr MojService::dispatchReply(MojServiceMessage* msg) { MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); // parse payload MojObjectBuilder builder; MojObject payload; MojErr err = MojErrNone; MojInt64 errCode = MojErrNone; errCode = err = msg->payload(builder); MojErrCatchAll(err); if (errCode == MojErrNone) payload = builder.object(); // get errCode bool retVal = false; if (payload.get(MojServiceMessage::ReturnValueKey, retVal) && !retVal) { if (!payload.get(MojServiceMessage::ErrorCodeKey, errCode)) errCode = MojErrUnknown; } // find request MojRefCountedPtr<MojServiceRequest> req; err = getRequest(msg, req); MojErrCheck(err); // do the dispatch err = dispatchReplyImpl(req.get(), msg, payload, (MojErr) errCode); MojErrCatchAll(err); return MojErrNone; }
MojErr MojLunaService::dispatchReplyImpl(MojServiceRequest* req, MojServiceMessage *msg, MojObject& payload, MojErr errCode) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(req); MojAssertMutexUnlocked(m_mutex); // don't automatically cancel a subscription if the payload has "subscribed":true bool subscribed = false; payload.get(_T("subscribed"), subscribed); // remove if there are more responses coming. due to the vagaries of the luna-service protocol, // we never really know for sure, but this is a good guess. bool remove = (req->numReplies() + 1 >= req->numRepliesExpected()) || (errCode != MojErrNone && !subscribed); MojErr err = req->dispatchReply(msg, payload, errCode); MojErrCatchAll(err) { remove = true; } if (remove) { if (req->numRepliesExpected() == 1) { bool found = false; err = removeRequest(req, found); MojErrCheck(err); } else { err = cancel(req); MojErrCheck(err); } } return MojErrNone; }
// Note: this is a blocking interface exclusively for unit tests. // It will not deliver cancel callbacks if clients drop off the bus. MojErr MojLunaService::dispatch() { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssertMutexUnlocked(m_mutex); MojAssert(m_loop); GMainContext* context = g_main_loop_get_context(m_loop); g_main_context_iteration(context, true); return MojErrNone; }
MojErr MojService::removeRequest(MojServiceRequest* req, bool& foundOut) { MojAssert(req); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); MojThreadGuard guard(m_mutex); MojErr err = m_requests.del(req->token(), foundOut); MojErrCheck(err); return MojErrNone; }
MojErr MojService::getRequest(MojServiceMessage* msg, MojRefCountedPtr<MojServiceRequest>& reqOut) { MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); MojThreadGuard guard(m_mutex); if (!m_requests.get(msg->token(), reqOut)) { MojErrThrow(MojErrResponseHandlerNotFound); } return MojErrNone; }
MojErr MojLunaService::addCategory(const MojChar* name, CategoryHandler* handler) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(name && handler); MojAssertMutexUnlocked(m_mutex); MojThreadGuard guard(m_mutex); // create array of LSMethods MethodVec pubMethods; MethodVec privMethods; const CategoryHandler::CallbackMap& callbacks = handler->callbacks(); for (CategoryHandler::CallbackMap::ConstIterator i = callbacks.begin(); i != callbacks.end(); i++) { LSMethod m = {i.key(), &handleRequest}; MethodVec& methods = i->m_pub ? pubMethods : privMethods; MojErr err = methods.push(m); MojErrCheck(err); } LSMethod nullMethod = {NULL, NULL}; MojErr err = pubMethods.push(nullMethod); MojErrCheck(err); err = privMethods.push(nullMethod); MojErrCheck(err); // create category object to hang on to method array MojRefCountedPtr<LunaCategory> cat(new LunaCategory(this, handler, pubMethods, privMethods)); MojAllocCheck(cat.get()); LSMethod* pubLsMethods = const_cast<LSMethod*>(cat->m_pubMethods.begin()); LSMethod* privLsMethods = const_cast<LSMethod*>(cat->m_privMethods.begin()); MojLunaErr lserr; bool retVal; if (m_service) { retVal = LSPalmServiceRegisterCategory(m_service, name, pubLsMethods, privLsMethods, NULL, cat.get(), lserr); MojLsErrCheck(retVal, lserr); } else { MojAssert(m_handle); retVal = LSRegisterCategory(m_handle, name, privLsMethods, NULL, NULL, lserr); MojLsErrCheck(retVal, lserr); retVal = LSCategorySetData(m_handle, name, cat.get(), lserr); MojLsErrCheck(retVal, lserr); } MojString categoryStr; err = categoryStr.assign(name); MojErrCheck(err); err = m_categories.put(categoryStr, cat); MojErrCheck(err); return MojErrNone; }
MojErr MojService::getCategory(const MojChar* name, MojRefCountedPtr<Category>& catOut) { MojAssert(name); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); MojThreadGuard guard(m_mutex); if (!m_categories.get(name, catOut)) { MojErrThrow(MojErrCategoryNotFound); } return MojErrNone; }
MojErr MojService::enableSubscription(MojServiceMessage* msg) { MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); MojErr err = addSubscription(msg); MojErrCheck(err); err = enableSubscriptionImpl(msg); MojErrCheck(err); return MojErrNone; }
MojErr MojService::dispatchCancel(MojServiceMessage* msg) { MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); SubscriptionKey key; MojErr err = key.init(msg); MojErrCheck(err); err = dispatchCancel(key); MojErrCheck(err); return MojErrNone; }
MojErr MojService::cancel(MojServiceRequest* req) { MojAssert(req); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); bool found = false; MojErr err = removeRequest(req, found); MojErrCheck(err); if (found) { err = cancelImpl(req); MojErrCheck(err); } return MojErrNone; }
MojErr MojService::dispatchCancel(const SubscriptionKey& key) { MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); MojThreadGuard guard(m_mutex); SubscriptionMap::ConstIterator i = m_subscriptions.find(key); if (i != m_subscriptions.end()) { MojRefCountedPtr<MojServiceMessage> msg = *i; guard.unlock(); MojErr err = msg->dispatchCancel(); MojErrCatchAll(err); } return MojErrNone; }
MojErr MojService::send(MojServiceRequest* req, const MojChar* service, const MojChar* method, Token& tokenOut) { MojAssert(req && service && method); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); // NOV-130089: sendImpl and addReqest must be atomic MojThreadGuard guard(m_mutex); MojErr err = sendImpl(req, service, method, tokenOut); MojErrCheck(err); err = addRequest(req); MojErrCheck(err); return MojErrNone; }
MojErr MojService::addSubscription(MojServiceMessage* msg) { MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); SubscriptionKey key; MojErr err = key.init(msg); MojErrCheck(err); MojThreadGuard guard(m_mutex); MojAssert(!m_subscriptions.contains(key)); err = m_subscriptions.put(key, msg); MojErrCheck(err); return MojErrNone; }
MojErr MojLunaService::enableSubscriptionImpl(MojServiceMessage* msg) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojString token; MojErr err = token.format(_T("%d"), msg->token()); MojErrCheck(err); MojLunaErr lserr; MojLunaMessage* lsMessage = static_cast<MojLunaMessage*>(msg); LSHandle* handle = LSMessageGetConnection(lsMessage->impl()); bool retVal = LSSubscriptionAdd(handle, token, lsMessage->impl(), lserr); MojLsErrCheck(retVal, lserr); return MojErrNone; }
MojErr MojLunaService::cancelImpl(MojServiceRequest* req) { LOG_TRACE("Entering function %s", __FUNCTION__); MojAssert(req); MojAssert(m_service || m_handle); MojAssertMutexUnlocked(m_mutex); MojLunaRequest* lunaReq = static_cast<MojLunaRequest*>(req); if (req->numRepliesExpected() > 1 && !lunaReq->cancelled()) { LSMessageToken lsToken = (LSMessageToken) req->token(); MojAssert(lsToken != LSMESSAGE_TOKEN_INVALID); MojLunaErr lserr; LSHandle* handle = getHandle(lunaReq->onPublic()); bool cancelled = LSCallCancel(handle, lsToken, lserr); MojLsErrCheck(cancelled, lserr); lunaReq->cancelled(true); } return MojErrNone; }
MojErr MojService::removeSubscription(MojServiceMessage* msg) { MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); SubscriptionKey key; MojErr err = key.init(msg); MojErrCheck(err); MojThreadGuard guard(m_mutex); bool found = false; err = m_subscriptions.del(key, found); MojErrCheck(err); MojAssert(found); err = removeSubscriptionImpl(msg); MojErrCheck(err); return MojErrNone; }
MojErr MojService::dispatchRequest(MojServiceMessage* msg) { MojAssert(msg); MojAssertMutexUnlocked(m_mutex); MojLogTrace(s_log); // get payload MojObject payload; MojErr reqErr; MojErr err = reqErr = msg->payload(payload); MojErrCatchAll(err) { return msg->replyError(reqErr); } // get callback Category* category = msg->serviceCategory(); MojAssert(category); MojAssert(category && category->m_service == this); // invoke err = reqErr = category->m_handler->invoke(msg->method(), msg, payload); MojErrCatchAll(err) { MojString payloadStr; (void) payload.toJson(payloadStr); MojString errStr; (void) MojErrToString(reqErr, errStr); MojLogError(s_log, _T("%s (%d) - sender='%s' method='%s' payload='%s'"), errStr.data(), (int) reqErr, msg->senderName(), msg->method(), payloadStr.data()); if (msg->numReplies() == 0) { return msg->replyError(reqErr); } } if (msg->numReplies() == 0 && msg->hasData()) { return msg->reply(); } return MojErrNone; }