- Fix error handling in syncing when using pumpGenerator()
- Take an optional error handler as the third parameter to pumpGenerator()
This commit is contained in:
parent
ec6d38df68
commit
01ea59491a
|
@ -1460,209 +1460,7 @@ Zotero.Sync.Server = new function () {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
var errorHandler = function (e) {
|
||||||
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) {
|
|
||||||
Zotero.DB.rollbackTransaction();
|
Zotero.DB.rollbackTransaction();
|
||||||
|
|
||||||
Zotero.UnresponsiveScriptIndicator.enable();
|
Zotero.UnresponsiveScriptIndicator.enable();
|
||||||
|
@ -1673,7 +1471,212 @@ Zotero.Sync.Server = new function () {
|
||||||
Zotero.suppressUIUpdates = false;
|
Zotero.suppressUIUpdates = false;
|
||||||
_updatesInProgress = 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) {
|
catch (e) {
|
||||||
|
|
|
@ -1498,8 +1498,11 @@ const ZOTERO_CONFIG = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pumps a generator until it yields false. See itemTreeView.js for an example.
|
* 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++;
|
_waiting++;
|
||||||
|
|
||||||
var timer = Components.classes["@mozilla.org/timer;1"].
|
var timer = Components.classes["@mozilla.org/timer;1"].
|
||||||
|
@ -1528,7 +1531,13 @@ const ZOTERO_CONFIG = {
|
||||||
_waitTimers = [];
|
_waitTimers = [];
|
||||||
_waitTimerCallbacks = [];
|
_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);
|
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
|
// add timer to global scope so that it doesn't get garbage collected before it completes
|
||||||
|
|
Loading…
Reference in New Issue
Block a user