1 /**
  2  * @fileOverview A collection of utilities for scheduling tasks in the
  3  * future.
  4  */
  5 
  6 if (!appjet) {
  7   throw new Error('appjet library is required for util library.');
  8 }
  9 
 10 function _paramObjectToParamArray(params, enc) {
 11   var pa = [];
 12   eachProperty(params, function(k, v) {
 13     pa.push(enc ? encodeURIComponent(k.toString()) : k.toString());
 14     pa.push(enc ? encodeURIComponent(v.toString()) : v.toString());
 15   });
 16   return pa;
 17 }
 18 
 19 _radixChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
 20 _radix = _radixChars.length
 21 
 22 function _longToString(n) {
 23   if (n < _radix) return _radixChars[n];
 24   else return _longToString(Math.floor(n/_radix)) + _radixChars[n%_radix];
 25 }
 26 
 27 function _cronId(date, path, period, params, published) {
 28   return "cron-"+ _longToString(appjet._native.storage_time()) + _longToString(Math.floor(Math.random()*_radix*_radix));
 29 }
 30 
 31 /**
 32  * <p>Schedules a CRON request to execute once at an exact date in
 33  * the future.</p>
 34  *
 35  * <p>The generated request appears as any other request, but uses HTTP
 36  * method <code>CRON</code> to distinguish itself from external HTTP
 37  * requests. It is not possible to generate a CRON request in any way
 38  * other than by scheduling one.</p>
 39  *
 40  * <p>To prevent abuse, only about 2000 CRON requests can be executed
 41  * by an app in any given minute. (This limit is lower for requests
 42  * that are very expensive, such as those that call
 43  * <code>wget()</code> and perform other high-cost operations.)
 44  * Requsts beyond this limit are rescheduled for the next minute but
 45  * ultimately canceled if the reschedule queue grows too large.</p>
 46  *
 47  * @example
 48 result = schedule(new Date("Jan 12, 2008"), "/dosomething");
 49  *
 50  * @example
 51 function cron_dosomething() {
 52   sendEmail("you@example.com", "A scheduled message.", "Greetings!");
 53 }
 54  *
 55  * @param {Date} date The date the request should trigger.
 56  * @param {string} path The path to be specified by the HTTP CRON request. Maximum length of 100 characters.
 57  * @param {object} [params] Optional parameters to be given with the HTTP CRON request. Maximum of 256 bytes of data; use storage if you need more.
 58  * @param {boolean} [published] Whether the request should be sent to
 59  * the published version of the app, or the most recent preview
 60  * version. By default only requests scheduled in published mode go to
 61  * the published version of the app.
 62  * @param {boolean} [mailOnError] Whether to email this app's owner if the cron request fails or is canceled; defaults to false.
 63  * @return {string} A unique identifier for this cron task.
 64  */
 65 function schedule(date, path, params, published, mailOnError) {
 66   if (date === undefined) {
 67     throw new Error("Must specify a date for schedule");
 68   }    
 69   var ps = _paramObjectToParamArray(params, true);
 70   var id = _cronId(date, path, -1, params, published);
 71   if (published === undefined) {
 72     published = !(appjet._native.isPreview() || appjet._native.isShell());
 73   }
 74   if (path === undefined) {
 75     path = "/";
 76   }
 77   if (path[0] != "/") throw new Error("Paths must be absolute.");
 78   if (mailOnError === undefined) {
 79     mailOnError = false;
 80   }
 81   var ret = appjet._native.cron_scheduleSingleEvent(date.getTime(), path, ps, published, mailOnError, id);
 82   if (ret != "")
 83     throw new Error("Scheduling error: "+ret);
 84   return id;
 85 }
 86 
 87 /**
 88  * <p>Schedules a CRON request to execute every <code>period</code> minutes,
 89  * starting at <code>date</code>.
 90  *
 91  * <p>The generated request appears as any other request, but uses
 92  * HTTP method <code>CRON</code> to distinguish itself from other external HTTP
 93  * requests. It is not possible to generate a CRON request in any way
 94  * other than by scheduling one.</p>
 95  *
 96  * <p>A CRON request cannot schedule a repeating CRON request.</p>
 97  *
 98  * @example
 99 result = schedule(new Date("Jan 12, 2008"), "/dosomething");
100  *
101  * @param {Date} date The exact date the cron requests should start.
102  * @param {number} period The delay between subsequent requests in minutes. Minimum of 1 minute.
103  * @param {string} path The path specified by the HTTP CRON requests. Maximum length of 100 characters.
104  * @param {object} [params] Optional parameters to be given with the HTTP CRON requests. Maximum of 256 bytes of data.
105  * @param {boolean} [published] Whether the CRON requests should be sent to
106  * the published version of the app, or the most recent preview
107  * version. By default only requests scheduled in published mode go to
108  * the published version of the app.
109  * @param {boolean} [mailOnError] Whether to email this app's owner if the cron request fails or is canceled; defaults to false.
110  * @return {string} A unique identifier for this cron task.
111  */
112 function scheduleRepeating(date, period, path, params, published, mailOnError) {
113   if (date === undefined || period === undefined) {
114     throw new Error("Must specify a date and a period for scheduleRepeating");
115   }
116   var ps = _paramObjectToParamArray(params, true);
117   var id = _cronId(date, path, period, params, published)
118   if (published === undefined) {
119     published = !(appjet._native.isPreview() || appjet._native.isShell());
120   }
121   if (path === undefined) {
122     path = "/";
123   }
124   if (path[0] != "/") throw new Error("Paths must be absolute.");
125   if (mailOnError === undefined) {
126     mailOnError = false;
127   }
128   var ret = appjet._native.cron_scheduleRepeatingEvent(date.getTime(), period*60000, path, ps, published, mailOnError, id);
129   if (ret != "")
130     throw new Error("Scheduling error: "+ret);
131   return id;
132 }
133 
134 /**
135  * <p>Returns an array of objects describing all scheduled CRON tasks.
136  * Modifications to these objects are not reflected in the scheduled
137  * tasks.</p>
138  *
139  * <p>The returned objects have property names that are the same as
140  * the arguments to <code>schedule()</code> and
141  * <code>scheduleRepeating()</code>, namely:</o>
142  * <ul>
143  * <li><strong>name:</strong> the task's identifier</li>
144  * <li><strong>date:</strong> date the task is (or was) first scheduled to run</li>
145  * <li><strong>period:</strong> delay between repeated requests, in minutes</li>
146  * <li><strong>path:</strong> path to request</li>
147  * <li><strong>params:</strong> query string containing the parameters on the request</li>
148  * <li><strong>published:</strong> whether to send the request to the published or preview version of the code</li>
149  * <li><strong>mailOnError:</strong> whether to send mail if there's an error
150  * </ul>
151  *
152  * @return {Array} An array of objects describing all scheduled CRON tasks.
153  */
154 function listAll() {
155   var ret = appjet._native.cron_allEvents();
156   ret.forEach(function (e) {
157     e.date = new Date(e.date);
158     if (e.period)
159       e.period = e.period / 60000;
160     eachProperty(e, function(name, value) {
161       e.__defineGetter__(name, function() { return value; });
162       e.__defineSetter__(name, function() { 
163 	throw new Error("Scheduled requests are not mutable; you must cancel this one and "+
164 			"schedule a new one if you'd like to change any properties.");
165       });
166     });
167   });
168   return ret;
169 }
170 
171 /**
172  * Unschedules the CRON task identified by <code>name</code>. 
173  *
174  * <p>If the CRON task is non-repeating and has already been executed,
175  * this function has no effect.</p>
176  * 
177  * @example
178 result = schedule(new Date("Jan 12, 2008"), "/dosomething");
179 unschedule(result);
180  *
181  * @param {string} name The identifier of the CRON task to cancel.
182  */
183 function unschedule(name) {
184   appjet._native.cron_unschedule(name);
185 }
186 
187 /**
188  * Unschedules all CRON tasks.
189  */
190 function unscheduleAll() {
191   appjet._native.cron_unscheduleAll();
192 }
193