blob: 3e4574ef15aa4a3a53e3c1f19d8330b2892b9de5 [file] [log] [blame]
// Copyright 2012 Google Inc.
//
// Licensed 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.
// Object to track desired client registrations. This class belongs to caller
// (e.g., InvalidationClientImpl) and is not thread-safe - the caller has to use
// this class in a thread-safe manner.
#include "google/cacheinvalidation/impl/registration-manager.h"
#include "google/cacheinvalidation/impl/client-protocol-namespace-fix.h"
#include "google/cacheinvalidation/impl/log-macro.h"
#include "google/cacheinvalidation/impl/proto-helpers.h"
#include "google/cacheinvalidation/impl/simple-registration-store.h"
namespace invalidation {
RegistrationManager::RegistrationManager(
Logger* logger, Statistics* statistics, DigestFunction* digest_function)
: desired_registrations_(new SimpleRegistrationStore(digest_function)),
statistics_(statistics),
logger_(logger) {
// Initialize the server summary with a 0 size and the digest corresponding to
// it. Using defaultInstance would wrong since the server digest will not
// match unnecessarily and result in an info message being sent.
GetClientSummary(&last_known_server_summary_);
}
void RegistrationManager::PerformOperations(
const vector<ObjectIdP>& object_ids, RegistrationP::OpType reg_op_type,
vector<ObjectIdP>* oids_to_send) {
// Record that we have pending operations on the objects.
vector<ObjectIdP>::const_iterator iter = object_ids.begin();
for (; iter != object_ids.end(); iter++) {
pending_operations_[*iter] = reg_op_type;
}
// Update the digest appropriately.
if (reg_op_type == RegistrationP_OpType_REGISTER) {
desired_registrations_->Add(object_ids, oids_to_send);
} else {
desired_registrations_->Remove(object_ids, oids_to_send);
}
}
void RegistrationManager::GetRegistrations(
const string& digest_prefix, int prefix_len, RegistrationSubtree* builder) {
vector<ObjectIdP> oids;
desired_registrations_->GetElements(digest_prefix, prefix_len, &oids);
for (size_t i = 0; i < oids.size(); ++i) {
builder->add_registered_object()->CopyFrom(oids[i]);
}
}
void RegistrationManager::HandleRegistrationStatus(
const RepeatedPtrField<RegistrationStatus>& registration_statuses,
vector<bool>* success_status) {
// Local-processing result code for each element of
// registrationStatuses. Indicates whether the registration status was
// compatible with the client's desired state (e.g., a successful unregister
// from the server when we desire a registration is incompatible).
for (int i = 0; i < registration_statuses.size(); ++i) {
const RegistrationStatus& registration_status =
registration_statuses.Get(i);
const ObjectIdP& object_id_proto =
registration_status.registration().object_id();
// The object is no longer pending, since we have received a server status
// for it, so remove it from the pendingOperations map. (It may or may not
// have existed in the map, since we can receive spontaneous status messages
// from the server.)
pending_operations_.erase(object_id_proto);
// We start off with the local-processing set as success, then potentially
// fail.
bool is_success = true;
// if the server operation succeeded, then local processing fails on
// "incompatibility" as defined above.
if (registration_status.status().code() == StatusP_Code_SUCCESS) {
bool app_wants_registration =
desired_registrations_->Contains(object_id_proto);
bool is_op_registration =
(registration_status.registration().op_type() ==
RegistrationP_OpType_REGISTER);
bool discrepancy_exists = is_op_registration ^ app_wants_registration;
if (discrepancy_exists) {
// Remove the registration and set isSuccess to false, which will cause
// the caller to issue registration-failure to the application.
desired_registrations_->Remove(object_id_proto);
statistics_->RecordError(
Statistics::ClientErrorType_REGISTRATION_DISCREPANCY);
TLOG(logger_, INFO,
"Ticl discrepancy detected: registered = %d, requested = %d. "
"Removing %s from requested",
is_op_registration, app_wants_registration,
ProtoHelpers::ToString(object_id_proto).c_str());
is_success = false;
}
} else {
// If the server operation failed, then local processing also fails.
desired_registrations_->Remove(object_id_proto);
TLOG(logger_, FINE, "Removing %s from committed",
ProtoHelpers::ToString(object_id_proto).c_str());
is_success = false;
}
success_status->push_back(is_success);
}
}
void RegistrationManager::GetClientSummary(RegistrationSummary* summary) {
summary->set_num_registrations(desired_registrations_->size());
summary->set_registration_digest(desired_registrations_->GetDigest());
}
string RegistrationManager::ToString() {
return StringPrintf(
"Last known digest: %s, Requested regs: %s",
ProtoHelpers::ToString(last_known_server_summary_).c_str(),
desired_registrations_->ToString().c_str());
}
const char* RegistrationManager::kEmptyPrefix = "";
} // namespace invalidation