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