- Fix error handling in syncing when using pumpGenerator()

- Take an optional error handler as the third parameter to pumpGenerator()
This commit is contained in:
Dan Stillman 2011-10-11 16:18:17 +00:00
parent ec6d38df68
commit 01ea59491a
2 changed files with 218 additions and 206 deletions

View file

@ -1460,209 +1460,7 @@ Zotero.Sync.Server = new function () {
);
}
try {
var gen = Zotero.Sync.Server.Data.processUpdatedXML(
xml.updated, lastLocalSyncDate, syncSession, libraryID, function (xmlstr) {
try {
Zotero.UnresponsiveScriptIndicator.enable();
if (progressMeter) {
Zotero.hideZoteroPaneOverlay();
}
Zotero.suppressUIUpdates = false;
_updatesInProgress = false;
//Zotero.debug(xmlstr);
//throw('break');
if (xmlstr === false) {
Zotero.debug("Sync cancelled");
Zotero.DB.rollbackTransaction();
Zotero.reloadDataObjects();
Zotero.Sync.EventListener.resetIgnored();
_syncInProgress = false;
_callbacks.onStop();
return;
}
if (xmlstr) {
Zotero.debug(xmlstr);
}
if (!xmlstr) {
Zotero.debug("Nothing to upload to server");
Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
Zotero.Sync.Server.nextLocalSyncDate = false;
Zotero.DB.commitTransaction();
_syncInProgress = false;
_callbacks.onSuccess();
return;
}
Zotero.DB.commitTransaction();
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadingData'));
var url = _serverURL + 'upload';
var body = _apiVersionComponent
+ '&' + Zotero.Sync.Server.sessionIDComponent
+ '&updateKey=' + updateKey
+ '&data=' + encodeURIComponent(xmlstr);
//var file = Zotero.getZoteroDirectory();
//file.append('lastupload.txt');
//Zotero.File.putContents(file, body);
var uploadCallback = function (xmlhttp) {
if (xmlhttp.status == 409) {
Zotero.debug("Upload key is no longer valid -- restarting sync");
setTimeout(function () {
Zotero.Sync.Server.sync(_callbacks, true, true);
}, 1);
return;
}
_checkResponse(xmlhttp);
Zotero.debug(xmlhttp.responseText);
var response = xmlhttp.responseXML.childNodes[0];
if (_checkServerLock(response, function (mode) {
switch (mode) {
// If the upload was queued, keep checking back
case 'queued':
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadAccepted'));
var url = _serverURL + 'uploadstatus';
var body = _apiVersionComponent
+ '&' + Zotero.Sync.Server.sessionIDComponent;
Zotero.HTTP.doPost(url, body, function (xmlhttp) {
uploadCallback(xmlhttp);
});
break;
// If affected libraries were locked, restart sync,
// since the upload key would be out of date anyway
case 'locked':
setTimeout(function () {
Zotero.Sync.Server.sync(_callbacks, true, true);
}, 1);
break;
default:
throw ("Unexpected server lock mode '" + mode + "' in Zotero.Sync.Server.upload()");
}
})) { return; }
if (response.firstChild.tagName == 'error') {
// handle error
_error(response.firstChild.firstChild.nodeValue);
}
if (response.firstChild.localName != 'uploaded') {
_error("Unexpected upload response '" + response.firstChild.localName
+ "' in Zotero.Sync.Server.sync()");
}
Zotero.DB.beginTransaction();
Zotero.Sync.purgeDeletedObjects(nextLocalSyncTime);
Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
Zotero.Sync.Server.nextLocalSyncDate = false;
Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
//throw('break2');
Zotero.DB.commitTransaction();
// Check if any items were modified during /upload,
// and restart the sync if so
if (Zotero.Items.getNewer(nextLocalSyncDate, true)) {
Zotero.debug("Items were modified during upload -- restarting sync");
Zotero.Sync.Server.sync(_callbacks, true, true);
return;
}
_syncInProgress = false;
_callbacks.onSuccess();
}
var compress = Zotero.Prefs.get('sync.server.compressData');
// Compress upload data
if (compress) {
// Callback when compressed data is available
var bufferUploader = function (data) {
var gzurl = url + '?gzip=1';
var oldLen = body.length;
var newLen = data.length;
var savings = Math.round(((oldLen - newLen) / oldLen) * 100)
Zotero.debug("HTTP POST " + newLen + " bytes to " + gzurl
+ " (gzipped from " + oldLen + " bytes; "
+ savings + "% savings)");
if (Zotero.HTTP.browserIsOffline()) {
Zotero.debug('Browser is offline');
return false;
}
var req =
Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance();
req.open('POST', gzurl, true);
req.setRequestHeader('Content-Type', "application/octet-stream");
req.setRequestHeader('Content-Encoding', 'gzip');
req.onreadystatechange = function () {
if (req.readyState == 4) {
uploadCallback(req);
}
};
try {
req.sendAsBinary(data);
}
catch (e) {
_error(e);
}
}
// Get input stream from POST data
var unicodeConverter =
Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
unicodeConverter.charset = "UTF-8";
var bodyStream = unicodeConverter.convertToInputStream(body);
// Get listener for when compression is done
var listener = new Zotero.BufferedInputListener(bufferUploader);
// Initialize stream converter
var converter =
Components.classes["@mozilla.org/streamconv;1?from=uncompressed&to=gzip"]
.createInstance(Components.interfaces.nsIStreamConverter);
converter.asyncConvertData("uncompressed", "gzip", listener, null);
// Send input stream to stream converter
var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
createInstance(Components.interfaces.nsIInputStreamPump);
pump.init(bodyStream, -1, -1, 0, 0, true);
pump.asyncRead(converter, null);
}
// Don't compress upload data
else {
Zotero.HTTP.doPost(url, body, uploadCallback);
}
}
catch (e) {
_error(e);
}
}
);
Zotero.pumpGenerator(gen);
}
catch (e) {
var errorHandler = function (e) {
Zotero.DB.rollbackTransaction();
Zotero.UnresponsiveScriptIndicator.enable();
@ -1673,7 +1471,212 @@ Zotero.Sync.Server = new function () {
Zotero.suppressUIUpdates = false;
_updatesInProgress = false;
throw (e);
_error(e);
}
try {
var gen = Zotero.Sync.Server.Data.processUpdatedXML(
xml.updated,
lastLocalSyncDate,
syncSession,
libraryID,
function (xmlstr) {
Zotero.UnresponsiveScriptIndicator.enable();
if (progressMeter) {
Zotero.hideZoteroPaneOverlay();
}
Zotero.suppressUIUpdates = false;
_updatesInProgress = false;
//Zotero.debug(xmlstr);
//throw('break');
if (xmlstr === false) {
Zotero.debug("Sync cancelled");
Zotero.DB.rollbackTransaction();
Zotero.reloadDataObjects();
Zotero.Sync.EventListener.resetIgnored();
_syncInProgress = false;
_callbacks.onStop();
return;
}
if (xmlstr) {
Zotero.debug(xmlstr);
}
if (!xmlstr) {
Zotero.debug("Nothing to upload to server");
Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
Zotero.Sync.Server.nextLocalSyncDate = false;
Zotero.DB.commitTransaction();
_syncInProgress = false;
_callbacks.onSuccess();
return;
}
Zotero.DB.commitTransaction();
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadingData'));
var url = _serverURL + 'upload';
var body = _apiVersionComponent
+ '&' + Zotero.Sync.Server.sessionIDComponent
+ '&updateKey=' + updateKey
+ '&data=' + encodeURIComponent(xmlstr);
//var file = Zotero.getZoteroDirectory();
//file.append('lastupload.txt');
//Zotero.File.putContents(file, body);
var uploadCallback = function (xmlhttp) {
if (xmlhttp.status == 409) {
Zotero.debug("Upload key is no longer valid -- restarting sync");
setTimeout(function () {
Zotero.Sync.Server.sync(_callbacks, true, true);
}, 1);
return;
}
_checkResponse(xmlhttp);
Zotero.debug(xmlhttp.responseText);
var response = xmlhttp.responseXML.childNodes[0];
if (_checkServerLock(response, function (mode) {
switch (mode) {
// If the upload was queued, keep checking back
case 'queued':
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.uploadAccepted'));
var url = _serverURL + 'uploadstatus';
var body = _apiVersionComponent
+ '&' + Zotero.Sync.Server.sessionIDComponent;
Zotero.HTTP.doPost(url, body, function (xmlhttp) {
uploadCallback(xmlhttp);
});
break;
// If affected libraries were locked, restart sync,
// since the upload key would be out of date anyway
case 'locked':
setTimeout(function () {
Zotero.Sync.Server.sync(_callbacks, true, true);
}, 1);
break;
default:
throw ("Unexpected server lock mode '" + mode + "' in Zotero.Sync.Server.upload()");
}
})) { return; }
if (response.firstChild.tagName == 'error') {
// handle error
_error(response.firstChild.firstChild.nodeValue);
}
if (response.firstChild.localName != 'uploaded') {
_error("Unexpected upload response '" + response.firstChild.localName
+ "' in Zotero.Sync.Server.sync()");
}
Zotero.DB.beginTransaction();
Zotero.Sync.purgeDeletedObjects(nextLocalSyncTime);
Zotero.Sync.Server.lastLocalSyncTime = nextLocalSyncTime;
Zotero.Sync.Server.nextLocalSyncDate = false;
Zotero.Sync.Server.lastRemoteSyncTime = response.getAttribute('timestamp');
//throw('break2');
Zotero.DB.commitTransaction();
// Check if any items were modified during /upload,
// and restart the sync if so
if (Zotero.Items.getNewer(nextLocalSyncDate, true)) {
Zotero.debug("Items were modified during upload -- restarting sync");
Zotero.Sync.Server.sync(_callbacks, true, true);
return;
}
_syncInProgress = false;
_callbacks.onSuccess();
}
var compress = Zotero.Prefs.get('sync.server.compressData');
// Compress upload data
if (compress) {
// Callback when compressed data is available
var bufferUploader = function (data) {
var gzurl = url + '?gzip=1';
var oldLen = body.length;
var newLen = data.length;
var savings = Math.round(((oldLen - newLen) / oldLen) * 100)
Zotero.debug("HTTP POST " + newLen + " bytes to " + gzurl
+ " (gzipped from " + oldLen + " bytes; "
+ savings + "% savings)");
if (Zotero.HTTP.browserIsOffline()) {
Zotero.debug('Browser is offline');
return false;
}
var req =
Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance();
req.open('POST', gzurl, true);
req.setRequestHeader('Content-Type', "application/octet-stream");
req.setRequestHeader('Content-Encoding', 'gzip');
req.onreadystatechange = function () {
if (req.readyState == 4) {
uploadCallback(req);
}
};
try {
req.sendAsBinary(data);
}
catch (e) {
_error(e);
}
}
// Get input stream from POST data
var unicodeConverter =
Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
unicodeConverter.charset = "UTF-8";
var bodyStream = unicodeConverter.convertToInputStream(body);
// Get listener for when compression is done
var listener = new Zotero.BufferedInputListener(bufferUploader);
// Initialize stream converter
var converter =
Components.classes["@mozilla.org/streamconv;1?from=uncompressed&to=gzip"]
.createInstance(Components.interfaces.nsIStreamConverter);
converter.asyncConvertData("uncompressed", "gzip", listener, null);
// Send input stream to stream converter
var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"].
createInstance(Components.interfaces.nsIInputStreamPump);
pump.init(bodyStream, -1, -1, 0, 0, true);
pump.asyncRead(converter, null);
}
// Don't compress upload data
else {
Zotero.HTTP.doPost(url, body, uploadCallback);
}
}
);
Zotero.pumpGenerator(gen, false, errorHandler);
}
catch (e) {
errorHandler(e);
}
}
catch (e) {

View file

@ -1498,8 +1498,11 @@ const ZOTERO_CONFIG = {
/**
* Pumps a generator until it yields false. See itemTreeView.js for an example.
*
* If errorHandler is specified, exceptions in the generator will be caught
* and passed to the callback
*/
this.pumpGenerator = function(generator, ms) {
this.pumpGenerator = function(generator, ms, errorHandler) {
_waiting++;
var timer = Components.classes["@mozilla.org/timer;1"].
@ -1528,7 +1531,13 @@ const ZOTERO_CONFIG = {
_waitTimers = [];
_waitTimerCallbacks = [];
if(err) throw err;
if(err) {
if(errorHandler) {
errorHandler(err);
} else {
throw err;
}
}
}}
timer.initWithCallback(timerCallback, ms ? ms : 0, Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
// add timer to global scope so that it doesn't get garbage collected before it completes