Home | History | Annotate | Download | only in cloud_print
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/service/cloud_print/printer_job_queue_handler.h"
      6 
      7 #include <math.h>
      8 
      9 #include <algorithm>
     10 
     11 #include "base/values.h"
     12 
     13 namespace cloud_print {
     14 
     15 class TimeProviderImpl : public PrinterJobQueueHandler::TimeProvider {
     16  public:
     17     virtual base::Time GetNow() OVERRIDE;
     18 };
     19 
     20 base::Time TimeProviderImpl::GetNow() {
     21   return base::Time::Now();
     22 }
     23 
     24 JobDetails::JobDetails() {}
     25 
     26 JobDetails::~JobDetails() {}
     27 
     28 void JobDetails::Clear() {
     29   job_id_.clear();
     30   job_title_.clear();
     31   print_ticket_.clear();
     32   print_ticket_mime_type_.clear();
     33   print_data_mime_type_.clear();
     34   print_data_file_path_ = base::FilePath();
     35   print_data_url_.clear();
     36   print_ticket_url_.clear();
     37   tags_.clear();
     38   time_remaining_ = base::TimeDelta();
     39 }
     40 
     41 // static
     42 bool JobDetails::ordering(const JobDetails& first, const JobDetails& second) {
     43   return first.time_remaining_ < second.time_remaining_;
     44 }
     45 
     46 PrinterJobQueueHandler::PrinterJobQueueHandler(TimeProvider* time_provider)
     47     : time_provider_(time_provider) {}
     48 
     49 PrinterJobQueueHandler::PrinterJobQueueHandler()
     50     : time_provider_(new TimeProviderImpl()) {}
     51 
     52 PrinterJobQueueHandler::~PrinterJobQueueHandler() {}
     53 
     54 void PrinterJobQueueHandler::ConstructJobDetailsFromJson(
     55     const base::DictionaryValue* job_data,
     56     JobDetails* job_details) {
     57   job_details->Clear();
     58 
     59   job_data->GetString(kIdValue, &job_details->job_id_);
     60   job_data->GetString(kTitleValue, &job_details->job_title_);
     61 
     62   job_data->GetString(kTicketUrlValue, &job_details->print_ticket_url_);
     63   job_data->GetString(kFileUrlValue, &job_details->print_data_url_);
     64 
     65   // Get tags for print job.
     66   const base::ListValue* tags = NULL;
     67   if (job_data->GetList(kTagsValue, &tags)) {
     68     for (size_t i = 0; i < tags->GetSize(); i++) {
     69       std::string value;
     70       if (tags->GetString(i, &value))
     71         job_details->tags_.push_back(value);
     72     }
     73   }
     74 }
     75 
     76 base::TimeDelta PrinterJobQueueHandler::ComputeBackoffTime(
     77     const std::string& job_id) {
     78   FailedJobMap::const_iterator job_location = failed_job_map_.find(job_id);
     79   if (job_location == failed_job_map_.end()) {
     80     return base::TimeDelta();
     81   }
     82 
     83   base::TimeDelta backoff_time =
     84       base::TimeDelta::FromSeconds(kJobFirstWaitTimeSecs);
     85   backoff_time *=
     86       // casting argument to double and result to uint64 to avoid compilation
     87       // issues
     88       static_cast<int64>(pow(
     89           static_cast<long double>(kJobWaitTimeExponentialMultiplier),
     90           job_location->second.retries_) + 0.5);
     91   base::Time scheduled_retry =
     92       job_location->second.last_retry_ + backoff_time;
     93   base::Time now = time_provider_->GetNow();
     94   base::TimeDelta time_remaining;
     95 
     96   if (scheduled_retry < now) {
     97     return base::TimeDelta();
     98   }
     99   return scheduled_retry - now;
    100 }
    101 
    102 void PrinterJobQueueHandler::GetJobsFromQueue(
    103     const base::DictionaryValue* json_data,
    104     std::vector<JobDetails>* jobs) {
    105   std::vector<JobDetails> jobs_with_timeouts;
    106 
    107   jobs->clear();
    108 
    109   const base::ListValue* job_list = NULL;
    110   if (!json_data->GetList(kJobListValue, &job_list)) {
    111     return;
    112   }
    113 
    114   size_t list_size = job_list->GetSize();
    115   for (size_t cur_job = 0; cur_job < list_size; cur_job++) {
    116     const base::DictionaryValue* job_data = NULL;
    117     if (job_list->GetDictionary(cur_job, &job_data)) {
    118       JobDetails job_details_current;
    119       ConstructJobDetailsFromJson(job_data, &job_details_current);
    120 
    121       job_details_current.time_remaining_ =
    122           ComputeBackoffTime(job_details_current.job_id_);
    123 
    124       if (job_details_current.time_remaining_ == base::TimeDelta()) {
    125         jobs->push_back(job_details_current);
    126       } else {
    127         jobs_with_timeouts.push_back(job_details_current);
    128       }
    129     }
    130   }
    131 
    132   sort(jobs_with_timeouts.begin(), jobs_with_timeouts.end(),
    133        &JobDetails::ordering);
    134   jobs->insert(jobs->end(), jobs_with_timeouts.begin(),
    135                jobs_with_timeouts.end());
    136 }
    137 
    138 void PrinterJobQueueHandler::JobDone(const std::string& job_id) {
    139   failed_job_map_.erase(job_id);
    140 }
    141 
    142 bool PrinterJobQueueHandler::JobFetchFailed(const std::string& job_id) {
    143   FailedJobMetadata metadata;
    144   metadata.retries_ = 0;
    145   metadata.last_retry_ = time_provider_->GetNow();
    146 
    147   std::pair<FailedJobMap::iterator, bool> job_found =
    148       failed_job_map_.insert(FailedJobPair(job_id, metadata));
    149 
    150   // If the job has already failed once, increment the number of retries.
    151   // If it has failed too many times, remove it from the map and tell the caller
    152   // to report a failure.
    153   if (!job_found.second) {
    154     if (job_found.first->second.retries_ >= kNumRetriesBeforeAbandonJob) {
    155       failed_job_map_.erase(job_found.first);
    156       return false;
    157     }
    158 
    159     job_found.first->second.retries_ += 1;
    160     job_found.first->second.last_retry_ = time_provider_->GetNow();
    161   }
    162 
    163   return true;
    164 }
    165 
    166 }  // namespace cloud_print
    167 
    168