/// <summary> /// Initiates a user-controlled upload loop whereby failed operations from a non-transactional /// package upload can be retried as many times until either all failed operations have been /// accounted for, or the user has decided to stop /// </summary> /// <param name="owner"></param> /// <param name="conn"></param> /// <param name="fileName"></param> /// <returns></returns> public static DialogResult StartNonTransactionalUploadLoop(Form owner, IServerConnection conn, string fileName) { var result = new UploadPackageResult(); var res = UploadPackageNonTransactional(owner, conn, fileName, result); //We do this until either there are no failures or the user has given up retrying while (result.Failed.Count > 0) { using (var resultDiag = new PackageUploadResultDialog(result)) { if (resultDiag.ShowDialog() == DialogResult.Retry) { var success = result.Successful; var skipped = result.SkipOperations; //Create a new result object and skip any previous ones that were //either successful or skipped themselves result = new UploadPackageResult(success); foreach (var skip in skipped) result.SkipOperations.Add(skip); res = UploadPackageNonTransactional(owner, conn, fileName, result); } else //Not retrying { break; } } } return res; }
/// <summary> /// Initializes a new instance of the <see cref="PackageUploadResultDialog"/> class. /// </summary> /// <param name="result">The result.</param> public PackageUploadResultDialog(UploadPackageResult result) : this() { lblFailed.Text = string.Format(Strings.PackageOperationsFailed, result.Failed.Count); lblSkipped.Text = string.Format(Strings.PackageOperationsSkipped, result.SkipOperations.Count); lblSucceeded.Text = string.Format(Strings.PackageOperationsSucceeded, result.Successful.Count); //grdFailed.DataSource = result.Failed; grdFailed.Columns.Add("Resource ID", Strings.HeaderResourceId); //NOXLATE grdFailed.Columns.Add("Operation", Strings.HeaderOperation); //NOXLATE grdFailed.Columns.Add("Error", Strings.HeaderError); //NOXLATE foreach (var op in result.Failed.Keys) { grdFailed.Rows.Add(op.ResourceId, op.OperationName, result.Failed[op].ToString()); } }
/// <summary> /// Uploads a package to the server in a non-transactional fashion. Resources which fail to load are added to the specified list of /// failed resources. The upload is non-transactional in the sense that it can partially fail. Failed operations are logged. /// </summary> /// <param name="sourceFile">The source package file</param> /// <param name="result">An <see cref="T:Maestro.Packaging.UploadPackageResult"/> object containing an optional list of operations to skip. It will be populated with the list of operations that passed and failed as the process executes</param> public void UploadPackageNonTransactional(string sourceFile, UploadPackageResult result) { Dictionary<PackageOperation, PackageOperation> skipOps = new Dictionary<PackageOperation, PackageOperation>(); if (result.SkipOperations.Count > 0) { foreach (var op in result.SkipOperations) { skipOps[op] = op; } } ProgressDelegate progress = this.Progress; if (progress == null) progress = (type, file, a, b) => { }; double step = 0.0; progress(ProgressType.ListingFiles, sourceFile, 100, step); //Process overview: // // 1. Extract the package to a temp directory // 2. Read the package manifest // 3. For each resource id in the manifest, if it is in the list of resource ids to skip // then skip it. Otherwise process the directive that uses this id. ZipFile package = new ZipFile(sourceFile); ZipEntry manifestEntry = package.GetEntry("MgResourcePackageManifest.xml"); //NOXLATE XmlDocument doc = new XmlDocument(); using (var s = package.GetInputStream(manifestEntry)) { doc.Load(s); } XmlNodeList opNodes = doc.GetElementsByTagName("Operation"); //NOXLATE double unit = (100.0 / (double)opNodes.Count); foreach (XmlNode opNode in opNodes) { step += unit; string name = opNode["Name"].InnerText.ToUpper(); //NOXLATE PackageOperation op = ParseOperation(opNode); //TODO: A DELETERESOURCE would cause a null operation. Should we bother to support it? if (op == null) continue; //Is a skipped operation? if (skipOps.ContainsKey(op)) { System.Diagnostics.Trace.TraceInformation("Skipping " + op.OperationName + " on " + op.ResourceId); //NOXLATE continue; } switch (name) { case "SETRESOURCE": //NOXLATE { SetResourcePackageOperation sop = (SetResourcePackageOperation)op; if (sop.Content == null) { skipOps[sop] = sop; } else { ZipEntry contentEntry = package.GetEntry(sop.Content); ZipEntry headerEntry = null; if (!string.IsNullOrEmpty(sop.Header)) headerEntry = package.GetEntry(sop.Header); try { using (var s = package.GetInputStream(contentEntry)) { m_connection.ResourceService.SetResourceXmlData(op.ResourceId, s); progress(ProgressType.SetResource, op.ResourceId, 100, step); } if (headerEntry != null) { using (var s = package.GetInputStream(headerEntry)) { using (var sr = new StreamReader(s)) { ResourceDocumentHeaderType header = ResourceDocumentHeaderType.Deserialize(sr.ReadToEnd()); m_connection.ResourceService.SetResourceHeader(op.ResourceId, header); progress(ProgressType.SetResource, op.ResourceId, 100, step); } } } result.Successful.Add(op); } catch (Exception ex) { //We don't really care about the header. We consider failure if the //content upload did not succeed if (!m_connection.ResourceService.ResourceExists(op.ResourceId)) result.Failed.Add(op, ex); } } } break; case "SETRESOURCEDATA": //NOXLATE { SetResourceDataPackageOperation sop = (SetResourceDataPackageOperation)op; ZipEntry dataEntry = package.GetEntry(sop.Data); try { using (var s = package.GetInputStream(dataEntry)) { m_connection.ResourceService.SetResourceData(sop.ResourceId, sop.DataName, sop.DataType, s); progress(ProgressType.SetResourceData, sop.ResourceId, 100, step); } result.Successful.Add(op); } catch (Exception ex) { var resData = m_connection.ResourceService.EnumerateResourceData(sop.ResourceId); bool found = false; foreach (var data in resData.ResourceData) { if (data.Name == sop.DataName) { found = true; break; } } if (!found) result.Failed.Add(sop, ex); } } break; } } }
/// <summary> /// Uploads a package file to the server in a non-transactional fashion /// </summary> /// <param name="owner">The owner form</param> /// <param name="connection">The connection used to upload the package</param> /// <param name="packageFile">The package file to upload</param> /// <param name="result">An <see cref="T:Maestro.Packaging.UploadPackageResult"/> object containing an optional list of operations to skip. It will be populated with the list of operations that passed and failed as the process executes</param> /// <returns>A DialogResult object that indicates the result of the operation</returns> public static DialogResult UploadPackageNonTransactional(Form owner, IServerConnection connection, string packageFile, UploadPackageResult result) { PackageProgress pkgp = new PackageProgress(); pkgp.Text = Strings.TitleUploading; var builder = new PackageBuilder(connection); pkgp.m_invokeObj = builder; pkgp.m_method = () => { builder.UploadPackageNonTransactional(packageFile, result); return true; }; return pkgp.ShowDialog(owner); }