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_data_mime_type_.clear(); 33 print_data_file_path_ = base::FilePath(); 34 print_data_url_.clear(); 35 print_ticket_url_.clear(); 36 tags_.clear(); 37 time_remaining_ = base::TimeDelta(); 38 } 39 40 // static 41 bool JobDetails::ordering(const JobDetails& first, const JobDetails& second) { 42 return first.time_remaining_ < second.time_remaining_; 43 } 44 45 PrinterJobQueueHandler::PrinterJobQueueHandler(TimeProvider* time_provider) 46 : time_provider_(time_provider) {} 47 48 PrinterJobQueueHandler::PrinterJobQueueHandler() 49 : time_provider_(new TimeProviderImpl()) {} 50 51 PrinterJobQueueHandler::~PrinterJobQueueHandler() {} 52 53 void PrinterJobQueueHandler::ConstructJobDetailsFromJson( 54 const DictionaryValue* job_data, 55 JobDetails* job_details) { 56 job_details->Clear(); 57 58 job_data->GetString(kIdValue, &job_details->job_id_); 59 job_data->GetString(kTitleValue, &job_details->job_title_); 60 61 job_data->GetString(kTicketUrlValue, &job_details->print_ticket_url_); 62 job_data->GetString(kFileUrlValue, &job_details->print_data_url_); 63 64 // Get tags for print job. 65 const ListValue* tags = NULL; 66 if (job_data->GetList(kTagsValue, &tags)) { 67 for (size_t i = 0; i < tags->GetSize(); i++) { 68 std::string value; 69 if (tags->GetString(i, &value)) 70 job_details->tags_.push_back(value); 71 } 72 } 73 } 74 75 base::TimeDelta PrinterJobQueueHandler::ComputeBackoffTime( 76 const std::string& job_id) { 77 FailedJobMap::const_iterator job_location = failed_job_map_.find(job_id); 78 if (job_location == failed_job_map_.end()) { 79 return base::TimeDelta(); 80 } 81 82 base::TimeDelta backoff_time = 83 base::TimeDelta::FromSeconds(kJobFirstWaitTimeSecs); 84 backoff_time *= 85 // casting argument to double and result to uint64 to avoid compilation 86 // issues 87 static_cast<int64>(pow( 88 static_cast<long double>(kJobWaitTimeExponentialMultiplier), 89 job_location->second.retries_) + 0.5); 90 base::Time scheduled_retry = 91 job_location->second.last_retry_ + backoff_time; 92 base::Time now = time_provider_->GetNow(); 93 base::TimeDelta time_remaining; 94 95 if (scheduled_retry < now) { 96 return base::TimeDelta(); 97 } 98 return scheduled_retry - now; 99 } 100 101 void PrinterJobQueueHandler::GetJobsFromQueue(const DictionaryValue* json_data, 102 std::vector<JobDetails>* jobs) { 103 std::vector<JobDetails> jobs_with_timeouts; 104 105 jobs->clear(); 106 107 const ListValue* job_list = NULL; 108 if (!json_data->GetList(kJobListValue, &job_list)) { 109 return; 110 } 111 112 size_t list_size = job_list->GetSize(); 113 for (size_t cur_job = 0; cur_job < list_size; cur_job++) { 114 const DictionaryValue* job_data = NULL; 115 if (job_list->GetDictionary(cur_job, &job_data)) { 116 JobDetails job_details_current; 117 ConstructJobDetailsFromJson(job_data, &job_details_current); 118 119 job_details_current.time_remaining_ = 120 ComputeBackoffTime(job_details_current.job_id_); 121 122 if (job_details_current.time_remaining_ == base::TimeDelta()) { 123 jobs->push_back(job_details_current); 124 } else { 125 jobs_with_timeouts.push_back(job_details_current); 126 } 127 } 128 } 129 130 sort(jobs_with_timeouts.begin(), jobs_with_timeouts.end(), 131 &JobDetails::ordering); 132 jobs->insert(jobs->end(), jobs_with_timeouts.begin(), 133 jobs_with_timeouts.end()); 134 } 135 136 void PrinterJobQueueHandler::JobDone(const std::string& job_id) { 137 failed_job_map_.erase(job_id); 138 } 139 140 bool PrinterJobQueueHandler::JobFetchFailed(const std::string& job_id) { 141 FailedJobMetadata metadata; 142 metadata.retries_ = 0; 143 metadata.last_retry_ = time_provider_->GetNow(); 144 145 std::pair<FailedJobMap::iterator, bool> job_found = 146 failed_job_map_.insert(FailedJobPair(job_id, metadata)); 147 148 // If the job has already failed once, increment the number of retries. 149 // If it has failed too many times, remove it from the map and tell the caller 150 // to report a failure. 151 if (!job_found.second) { 152 if (job_found.first->second.retries_ >= kNumRetriesBeforeAbandonJob) { 153 failed_job_map_.erase(job_found.first); 154 return false; 155 } 156 157 job_found.first->second.retries_ += 1; 158 job_found.first->second.last_retry_ = time_provider_->GetNow(); 159 } 160 161 return true; 162 } 163 164 } // namespace cloud_print 165 166