• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetNow()20 base::Time TimeProviderImpl::GetNow() {
21   return base::Time::Now();
22 }
23 
JobDetails()24 JobDetails::JobDetails() {}
25 
~JobDetails()26 JobDetails::~JobDetails() {}
27 
Clear()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
ordering(const JobDetails & first,const JobDetails & second)42 bool JobDetails::ordering(const JobDetails& first, const JobDetails& second) {
43   return first.time_remaining_ < second.time_remaining_;
44 }
45 
PrinterJobQueueHandler(TimeProvider * time_provider)46 PrinterJobQueueHandler::PrinterJobQueueHandler(TimeProvider* time_provider)
47     : time_provider_(time_provider) {}
48 
PrinterJobQueueHandler()49 PrinterJobQueueHandler::PrinterJobQueueHandler()
50     : time_provider_(new TimeProviderImpl()) {}
51 
~PrinterJobQueueHandler()52 PrinterJobQueueHandler::~PrinterJobQueueHandler() {}
53 
ConstructJobDetailsFromJson(const base::DictionaryValue * job_data,JobDetails * job_details)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 
ComputeBackoffTime(const std::string & job_id)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 
GetJobsFromQueue(const base::DictionaryValue * json_data,std::vector<JobDetails> * jobs)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 
JobDone(const std::string & job_id)138 void PrinterJobQueueHandler::JobDone(const std::string& job_id) {
139   failed_job_map_.erase(job_id);
140 }
141 
JobFetchFailed(const std::string & job_id)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